Еще в старые времена дискет и набора номера, мы должны были быть очень сознательны в использовании оперативной памяти. В наши дни облачных и терабайтных хранилищ это уже не так актуально. Тем не менее, вы можете убрать лишнее из ваших приложений и обеспечить максимальную эффективность программ.
В начале 1970-х годов 1 МБ памяти стоил 1 миллион долларов. Теперь 1 МБ стоит в миллионы раз меньше. Это одна из причин, почему инженеры и предприятия больше не беспокоятся о памяти. Кстати, 1 миллион долларов в 1970-х годах — это эквивалент нескольких миллионов долларов сегодня. Вот почему память была так драгоценна в те дни.
Автобиография Пола Аллена подробно рассматривает эту проблему. Пол Аллен рассказывает о трудностях, с которыми он и Билл Гейтс столкнулись при написании базовых программ менее чем в 4 КБ.
- ЦП
- Память
- Сеть
- Место хранения
Для большинства приложений это память. Процессор всегда находится на уровне 30-60%. Всегда есть обильное хранилище. Трудно перезагрузить сеть (если только ваше приложение не работает с потоковым видео). Таким образом, для большинства приложений это память, которая переполняется в первую очередь.
Сколько ватт потребляет компьютер
Несмотря на то, что процессор, диск и сеть недостаточно используются, вы в конечном итоге инициализируете все больше и больше экземпляров сервера приложений только потому, что память становится насыщенной. Это может увеличить вычислительные затраты для организаций до миллионов и миллиардов долларов.
- Повторяющиеся строки
- Неэффективные коллекции
- Дублирование объектов
- Дублирование массивов
- Неэффективные массивы
- Объекты, ожидающие завершения
- Цифры в упаковке
- Накладные расходы, вызванные заголовками объектов
- Неверные настройки размера памяти
Снижение вычислительных затрат: поскольку память является первым ресурсом, который насыщается, если вы можете уменьшить потребление памяти, вы сможете запускать свое приложение на меньшем количестве экземпляров сервера. Вы могли бы быть в состоянии уменьшить 30-40% серверов. Таким образом, ваше руководство может сократить на 30-40% расходы на обслуживание и поддержку своего центра обработки данных (или облачного хостинг – провайдера). Только это может привести к экономии средств на несколько миллионов или миллиардов долларов.
Лучший опыт работы с клиентами: если вы уменьшаете количество объектов, которые вы создаете для обслуживания входящего запроса, ваше время отклика будет намного лучше. Поскольку создается меньше объектов, меньше циклов процессора тратится на создание и сборку мусора. Сокращение времени отклика улучшит опыт клиента.
Сколько памяти тратит мое приложение?
Итак, давайте вернемся к исходному вопросу этой статьи «сколько памяти расходует мое приложение?». Это печально, но верно: в отрасли не так много инструментов, которые могут дать вам этот ответ. Мы поможем вам ответить на этот вопрос в два простых шага.
КАК УСКОРИТЬ И ОСВОБОДИТЬ ОЗУ Windows 10 Pro? 100% лайфхак для оперативной памяти
Шаг 1: Захват дампов кучи
Для того, чтобы проанализировать, как память тратится впустую, вам сначала нужно захватить дамп кучи. Дамп кучи — это в основном снимок памяти вашего приложения. Он содержит информацию обо всех объектах, присутствующих в памяти, кто ссылается на него, и многое другое.
Существует несколько вариантов захвата дампов кучи из вашего приложения Java. Существует также несколько вариантов захвата кучи дампов из Android-приложений.
Вы должны использовать вариант, который лучше всего подходит для вас и ваших приложений.
Рекомендуется захватывать дамп кучи, когда приложение принимает трафик. Когда приложение простаивает, новые объекты не будут созданы, таким образом, вы не сможете увидеть, сколько памяти на самом деле тратится впустую.
Шаг 2: Анализ с помощью инструмента HeapHero
После того, как вы захватили дампы кучи, вы можете загрузить захваченные дампы кучи в бесплатный онлайн инструмент анализа дампов кучи HeapHero.
В зависимости от вашего приложения, дампы кучи могут содержать данные PII и другие конфиденциальные данные. В этом случае вы можете зарегистрироваться, чтобы загрузить и установить инструмент HeapHero локально на вашем компьютере.
Этот инструмент даст высокоуровневую сводку общего объема памяти, которая тратится впустую, фактические данные, которые вызывают потерю памяти, и строки кода, которые вызывают эту потерю памяти. Он даже рекомендует решения для устранения проблемы.
HeapHero создает круговую диаграмму, которая суммирует, сколько памяти тратится впустую из-за каждой неэффективной практики программирования. По-видимому, это приложение тратит 35% своей памяти. Две главные причины этой потери:
- Неэффективные коллекции (т. е. структуры данных) вызывают 11,5% потерь памяти.
- Дублирование строк вызывает 10,4% потерь памяти.
В этом приложении имеется 343 661 экземпляр строковых объектов. Из них только 144 520 являются уникальными. По-видимому, есть 6,147 случаев “webservices.sabre.com-строковые объекты. Почему так много дубликатов? Почему это не было очищено, чтобы освободить больше памяти?
В этом приложении 34% HashMap не содержит элементов. Это вызывает вопрос о том, почему так много хэш-карт были созданы без каких-либо элементов. При создании хэш-карты по умолчанию создается 16 элементов. Пространство, выделенное для этих 16 элементов, теряется, когда вы не вставляете в него никаких элементов. Интересно, что 97% Hashtable также не содержит никаких элементов.
В заключение
Этот инструмент от HeapHero не только показывает объекты, которые теряют память, но также показывает путь кода, который вызывает потерю памяти. Эти знания облегчат программистам нахождение того, что надо исправить в приложении.
Автор этого материала — я — Пахолков Юрий. Я оказываю услуги по написанию программ на языках Java, C++, C# (а также консультирую по ним) и созданию сайтов. Работаю с сайтами на CMS OpenCart, WordPress, ModX и самописными. Кроме этого, работаю напрямую с JavaScript, PHP, CSS, HTML — то есть могу доработать ваш сайт или помочь с веб-программированием. Пишите сюда.
заметки, java, оптимизация
Бесплатный https и
домен RU в подарок
Источник: upread.ru
Как узнать используемую память
Как можно узнать сколько памяти занимает выполнение программы или класса в Intellij Idea?
Отслеживать
задан 22 июн 2015 в 4:49
2,048 5 5 золотых знаков 33 33 серебряных знака 69 69 бронзовых знаков
3 ответа 3
Сортировка: Сброс на вариант по умолчанию
Есть такой инструмент как jmap, выводящий гистограмму с показателями сколько памяти занимают объекты и т.п.
Также, утилиту jstat можно использовать для сбора самых разнообразных статистических данных. Статистика jstat сортируется в «свойства», указанные первым параметром командной строки. Чтобы увидеть список свойств, нужно запустить jstat с параметром -options.
В документации утилиты в JDK говорится, что именно показывает каждое из свойств, но большинство из них используется для сбора информации о работе сборщика мусора или его компонентов. Свойство -class показывает загруженные и выгруженные классы (что делает утилиту полезной для обнаружения утечек ClassLoader в пределах сервера приложений или вашего кода), а -compiler и -printcompilation предоставляют сведения о JIT-компиляторе Hotspot. По умолчанию jstat отображает информацию по состоянию на момент обращения. Если нужны регулярные снимки текущего состояния, укажите после -свойств интервалы в миллисекундах. jstat будет постоянно отображать снимки текущего состояния контролируемого процесса. Если нужно, чтобы jstat сделал лишь определенное количество снимков, укажите его за значением интервала/периода.
Источник: ru.stackoverflow.com
Сколько памяти нужно для запуска 1 миллиона конкурирующих задач?
В этой статье я углублённо сравню потребление памяти между асинхронными и многопоточными программами популярных языков вроде Rust, Go, Java, C#, Python, Node.js и Elixir.
Недавно я проводил сравнение производительности нескольких программ, предназначенных для обработки большого количества сетевых подключений. В итоге я увидел огромную разницу в потреблении этими программами памяти, порой в 20 раз и больше. Некоторые потребляли при 10К подключений чуть более 100 МБ в то время, как другие занимали почти 3 ГБ. К сожалению, эти программы были довольно сложными и также отличались своим функционалом, поэтому было бы трудно сравнить их непосредственно и сделать какие-то осмысленные выводы. Тут то у меня и возникла идея создать специальный синтетический бенчмарк.
Бенчмарк
Я написал на различных языках простую программу, работающую следующим образом:
Мы запускаем N конкурирующих задач, каждая из которых ожидает 10 секунд. Когда все эти задачи завершаются, программа закрывается. Количество задач управляется аргументом командной строки.
С небольшой помощью ChatGPT я смог создать эту программу всего за несколько минут, даже на языках, которые использую редко. Для вашего удобства весь код бенчмарка доступен в моём репозитории GitHub.
▍ Rust
На Rust я написал 3 программы. Первая использует традиционные потоки. Вот её основная часть:
let mut handles = Vec::new(); for _ in 0..num_threads < let handle = thread::spawn(|| < thread::sleep(Duration::from_secs(10)); >); handles.push(handle); > for handle in handles
В двух остальных версиях иcпользуется асинхронная обработка, одна в среде выполнения tokio , а другая в async-std . Вот основная часть варианта tokio :
let mut tasks = Vec::new(); for _ in 0..num_tasks < tasks.push(task::spawn(async < time::sleep(Duration::from_secs(10)).await; >)); > for task in tasks
Версия async-std очень похожа, поэтому я её приводить не буду.
▍ Go
В Go многопоточность строится на основе горутин. При этом мы не ожидаем их по-отдельности, а используем WaitGroup :
var wg sync.WaitGroup for i := 0; i < numRoutines; i++ < wg.Add(1) go func() < defer wg.Done() time.Sleep(10 * time.Second) >() > wg.Wait()
▍ Java
В Java традиционно используются потоки, но JDK 21 предлагает предварительную версию виртуальных потоков, которые очень похожи на горутины. В связи с этим я создал два варианта бенчмарка. Мне было интересно сравнить потоки Java с потоками Rust.
List threads = new ArrayList<>(); for (int i = 0; i < numTasks; i++) < Thread thread = new Thread(() -> < try < Thread.sleep(Duration.ofSeconds(10)); >catch (InterruptedException e) < >>); thread.start(); threads.add(thread); > for (Thread thread : threads)
А вот версия с виртуальными потоками. Заметьте, насколько она похожа на предыдущую. Почти идентична!
List threads = new ArrayList<>(); for (int i = 0; i < numTasks; i++) < Thread thread = Thread.startVirtualThread(() -> < try < Thread.sleep(Duration.ofSeconds(10)); >catch (InterruptedException e) < >>); threads.add(thread); > for (Thread thread : threads)
▍ C#
В C#, как и в Rust, есть отличная поддержка функционала async/await :
List tasks = new List(); for (int i = 0; i < numTasks; i++) < Task task = Task.Run(async () =>< await Task.Delay(TimeSpan.FromSeconds(10)); >); tasks.Add(task); > await Task.WhenAll(tasks);
▍ Node.js
То же касается Node.js:
const delay = util.promisify(setTimeout); const tasks = []; for (let i = 0; i < numTasks; i++) < tasks.push(delay(10000); >await Promise.all(tasks);
▍ Python
В версии Python 3.5 также появился функционал async/await , поэтому можно написать:
async def perform_task(): await asyncio.sleep(10) tasks = [] for task_id in range(num_tasks): task = asyncio.create_task(perform_task()) tasks.append(task) await asyncio.gather(*tasks)
▍ Elixir
Elixir тоже известен своими асинхронными возможностями:
tasks = for _ :timer.sleep(10000) end) end Task.await_many(tasks, :infinity)
▍ Тестовая среда
Результаты
▍ Минимальная нагрузка
Начнём с небольшой нагрузки.
Поскольку некоторые среды выполнения требуют памяти сами по себе, сначала мы запустим всего одну задачу.
Рис. 1: пиковое потребление памяти при запуске одной задачи
Здесь у нас явно выделяется две группы программ. Программы Go и Rust, скомпилированные статично в нативные бинарники, требуют очень мало памяти. Другие программы, выполняющиеся на управляемых платформах или через интерпретаторы, потребляют её уже больше, хотя Python тут выглядит очень достойно. Потребление памяти между этими группами отличается почти на порядок.
Меня удивило, что .NET умудрилась продемонстрировать худший показатель, но это наверняка можно скорректировать настройками. Поделитесь в комментариях, если знаете какие-либо уместные приёмы. Между режимами Debug и Release я особой разницы не заметил.
▍ 10К задач
Рис. 2: пиковое потребление памяти при запуске 10,000 задач
Здесь у нас обнаружилось несколько сюрпризов. Вы, пожалуй, ожидали, что потоки в этом бенчмарке сильно проиграют. И это оказалось верно для потоков Java, которые действительно потребили почти 250 МБ. Однако нативные потоки Linux, используемые из Rust, оказались достаточно легковесными, чтобы в количестве 10,000 нагружать память меньше, чем при простое во многих других процедурах. Асинхронные задачи, или виртуальные потоки, могут быть легче нативных потоков, но при всего 10К задач это преимущество мы не заметим.
Ещё один сюрприз связан с Go. Ожидалось, что горутины являются очень легковесными, но по факту они потребили более 50% от объёма памяти, задействованной потоками Rust. Честно говоря, я ожидал более значительное отличие в пользу Go. Так что можно сделать вывод, что при 10К конкурирующих задач потоки всё равно оказываются довольно сильной альтернативой. Ядро Linux определённо хорошо здесь справляется.
Go также утратил своё крохотное преимущество перед Rust, продемонстрированное в предыдущем бенчмарке, и теперь потребляет в 6 раз больше памяти, чем наиболее оптимальная программа Rust. При этом его также обошёл Python.
И последним сюрпризом стало то, что при 10К задач потребление памяти программой .NET по сравнению с состоянием простоя возросло незначительно. Возможно, она использует заранее выделенную память, либо потребление этого ресурса в режиме простоя настолько велико, что 10К задач большой разницы не вносят.
▍ 100К задач
Мне не удалось запустить 100,000 потоков в моей системе, поэтому соответствующие бенчмарки пришлось исключить. Возможно, это можно было как-то наладить через настройки, но после часа безуспешных попыток я сдался. Так что при 100К задач вряд ли стоит использовать потоки.
Рис. 3: пиковое потребление памяти при запуске 100,000 задач
В этом тесте программу Go обошёл не только Rust, но также Java, C# и Node.js.
А .NET, похоже, здесь хитрит, поскольку её потребление памяти так и не возросло. Мне пришлось перепроверить, действительно ли она запускает правильное количество задач, но оказалось, что так и есть. При этом она также выходит через 10 секунд, значит основной цикл не блокируется. Просто магия! Хорошая работа, .NET.
▍ 1 миллион задач
Перейдём к экстремальным испытаниям.
При 1 миллионе задач программа Elixir сдалась с ошибкой ** (SystemLimitError) a system limit has been reached .
Дополнено: в комментариях мне указали, что можно увеличить лимит процессов. После добавления в вызов elixir параметра —erl ‘+P 1000000’ программа заработала.
Рис. 4: пиковое потребление памяти при запуске 1 миллиона задач
Наконец, мы видим увеличение потребления памяти программой C#. Но она всё ещё остаётся весьма конкурентной и даже немного обошла одну из сред выполнения Rust.
Отставание Go от соперников увеличилось. Теперь этот язык проигрывает победителю в 12 раз. Он также в 2 раза уступает Java, что противоречит распространённому мнению о том, что JVM является пожирателем памяти, а Go – легковесным языком.
Среда tokio осталась непревзойдённой. И это не удивительно после того, как она показала себя при 100К задач.
Выводы
Как мы пронаблюдали, большое число конкурирующих задач могут потреблять значительный объём памяти, даже без обработки сложных операций. Различные среды выполнения языков несут в себе различные компромиссы. Некоторые являются легковесными и эффективно справляются с небольшим числом задач, плохо масштабируясь на сотни тысяч. При этом другие накладывают большие изначальные издержки, но могут с лёгкостью обрабатывать тяжёлую нагрузку. Важно отметить, что не все среды выполнения смогли осилить очень большое число конкурирующих задач при базовых настройках.
Это сравнение было сосредоточено исключительно на потреблении памяти, хотя другие факторы вроде скорости запуска задачи и передачи данных являются не менее важными. Примечательно, что при 1 миллионе задач стали очевидны издержки на их запуск, и большинству программ для завершения потребовалось более 12 секунд.
Источник: habr.com