Как узнать сколько памяти потребляет программа на c
Linux – довольно гибкая система, выполнить задачи в которой можно разными способами. Сегодня мы рассмотрим, как узнать, сколько оперативной памяти (ОЗУ — оперативное запоминающее устройство или RAM — Random Access Memory) используется определенным процессом. Сама оперативная память представляет собой специальное устройство, которое используется для временного хранения данных и обеспечивает функционирование ПО.
ОЗУ не стоит путать с ПЗУ (постоянным запоминающим устройством): их отличие заключается в том, что оперативная память является энергозависимой (при выключении компьютера все данные исчезнут), в то время как память жесткого диска сохранит все данные даже после выключения машины. Поэтому и функции, которые они выполняют, также разнятся. Процессор использует оперативную память для быстрого доступа к данным - жесткий диск такую скорость обеспечить не может, поэтому при запуске программа загружается в оперативную память.
Мы отследим занимаемую память на примере процесса веб-сервера Apache.
Команда ps
Первым вариантом будет команда ps, с помощью которой можно получить список процессов, запущенных на сервере.
<PID> - идентификатор процесса, в нашем случае это число 897110.
Получим следующий вывод:
ОЗУ будет представлено в процентном соотношении к общему количеству доступной памяти. К сожалению, в данном примере этот процент крайне мал, и столбец %MEM показывает 0,0.
Команда ps имеет свои ключи, которые вы также можете использовать для удобства:
- -A : все процессы;
- -a : процессы, связанные с конкретным терминалом, кроме главных системных процессов сеанса;
- -d : все процессы, кроме главных системных процессов сеанса;
- T : все процессы на конкретном терминале;
- a : процессы, связанные с текущим терминалом, а также процессы других пользователей;
- r : информация только о работающих процессах;
- x : процессы, отсоединённые от терминала.
Команда top
Второй вариант – использование команды top. Более продвинутые пользователи должны знать эту команду, так как она выводит информацию о нагрузке системы и обновляет ее в реальном времени.
Обратите внимание на столбцы VIRT и %MEM. Последний столбец показывает информацию, которая очень похожа на ту, что получили предыдущей командой. А вот столбец VIRT отображает занятую виртуальную память.
Одним словом, виртуальная память – это некая комбинация ОЗУ и swap, которую использует запущенный процесс.
Обратите внимание, что команда top ограничена размерами окна, из-за чего игнорируется вся информация, которая не помещается на экран.
Получение занятой памяти из /proc
Последней командой для получения занятой процессором памяти будет обращение к псевдофайловой системе /proc.
Снова будет выведен результат, который схож с уже полученным ранее – виртуальная память, используемая процессом веб-сервера Apache.
Вот мы и разобрали на примере процесса Apache способы, которыми можно посмотреть занимаемую память.
Программа, которую я пишу для работы, должна отслеживать используемую память. Но он не может контролировать его использование памяти в целом, мне нужно, чтобы он контролировал каждый объект в программе и сколько памяти этот объект использует, таким образом он может затем сказать, что этот объект сократит использование памяти, если он использует выше определенный потенциал. Часть, которая контролирует использование памяти, содержит указатель на все созданные объекты и отслеживает использование их памяти, вызывая метод на этом объекте, который возвращает размер объекта.
Проблема, с которой я сталкиваюсь, заключается в том, что я не могу точно рассчитать размер используемой памяти. Не имеет значения, если мои вычисления немного разрядились, но я получаю большую разницу. Размер, который вычисляет моя программа, варьируется (в зависимости от того, какие действия выполняет программа) от 1/2 до 2/3 от фактического использования памяти программой. Например, была рассчитана программа, которая использовала 3,35 гб оперативной памяти, чтобы использовать только 2,16 ГБ.
Текущий способ расчета размера объекта заключается в добавлении sizeof (* this) к длине любых векторов или массивов в объекте, умноженному на размер элементов в векторе/массиве.
Что-то не так с тем, как я вычисляю используемую память? Или есть что-то еще, что я не принимаю во внимание? Если кто-нибудь знает о программе, в которой вы можете анализировать использование памяти различными аспектами программы, которые также будут очень полезны, таким образом, я могу отслеживать, откуда идет вся эта дополнительная память (предпочтительно такая, которая может работать на Linux без GUI как Я использую сервер Ubuntu, но также имею машину Windows, которую я могу использовать).
Что-то не так с тем, как я вычисляю используемую память?
Да. Прежде всего, размер памяти, потребляемой вашей программой, не может быть полностью использован в какой-либо одной точке. Например, после изменения размера вектора старый блок памяти может быть возвращен системе. Или он может храниться в куче в следующий раз, когда кто-то запрашивает блок памяти того же размера.
Кроме того, имейте в виду, что любые библиотеки, которые вы используете (например, OS API), выделяют память, и эти вещи не являются бесплатными.
Также имейте в виду, что для каждого распределения памяти существуют дополнительные накладные расходы, налагаемые менеджером кучи; обычно по порядку указателя или двух, за выделение.
Если вы хотите отслеживать, что использует память в вашем приложении, используйте для этого профилировщик реальной памяти. Если вы хотите динамически масштабировать объем памяти, который выделяет ваша программа, в первую очередь, используйте профилировщик, чтобы определить средний размер, занимаемый одним из объектов, с которыми вы имеете дело, а затем ограничиваете количество объектов, а не объем памяти.
У меня есть две программы, одна на C ++, другая на ассемблере. Я хочу сравнить, сколько памяти они используют при работе соответственно. Как я могу это сделать?
Я провожу тестирование в Windows, но я также хотел бы знать, как это сделать в Linux.
Запускаем программу в одной оболочке. Откройте другую оболочку и выполните команду «top». он перечислит запущенные процессы и поместит много памяти, которую они потребляют. я думаю, вы можете опросить / proc / yourprocessid / stat, чтобы узнать, сколько памяти он использует с течением времени.
Диспетчер задач Windows может показать использование памяти каждым процессом. Думаю, вы могли бы использовать вместо этого Valgrind, но я не вижу в этом смысла. В Linux используйте Valgrind или ps.
В Windows вы можете использовать функцию GetProcessMemoryInfo .
Зависит от вашей операционной системы - вы ожидаете, что у вас будут инструменты, которые сообщат вам, сколько памяти потребляется при запуске приложений.
Было бы очень сложно получить ответ, изучив код, запустите приложения, используйте диагностику своей платформы.
В зависимости от размера программ это может быть практически невозможно.
Если они не очень большие, вы можете увидеть, сколько памяти они выделяют; например, int займет 4 байта, char - 1 байт и т. д. Сборка очень прозрачна в том, сколько памяти она использует, даже на машине x86. Cpp почти так же прозрачен, если вы точно отслеживаете создание объекта и уничтожение / выделение памяти.
В Windows для этого можно использовать Системный монитор Microsoft. Пуск, Беги, "перфмон". Этот инструмент будет сообщать всевозможные статистические данные о процессах и предоставлять вам графики. В общем, вам будет интересно рассказать о «Частном рабочем наборе». Это покажет вам, сколько памяти ваш процесс зарезервировал для собственного использования.
Если вы хотите просто использовать свою кучу, и вы хотите сделать это программно, вам следует изучить Отладочная куча CRT.
Я не уверен насчет Linux, извините.
В Windows я нашел очень полезным монитор адресного пространства, особенно для того, чтобы посмотреть, насколько фрагментирована ваша память. .
В Linux попробуйте valgrind . Это удивительный инструмент, в котором слишком много функций, чтобы простые смертные могли полностью понять. Взгляните на massif valgrind .
В Windows вы можете использовать Системный монитор.
Использование монитора производительности
Запустите Performance Monitor из меню «Пуск» / «Администрирование» / «Производительность».
Если вы хотите начать регистрацию:
Выберите журнал производительности и предупреждение> Текущий журнал в левой части браузера.
Выберите «Новые настройки журнала».
Дайте журналу подходящее имя, например performance_Server для сервера
Это предложит вам одно меню. На вкладках «Общие» нажмите кнопку «Добавить» и выберите процесс, который вы хотите отслеживать. (Измените объект производительности на процесс, для «выбрать счетчики из списка» выберите «личные байты», для «выбрать экземпляры из списка» выберите процесс, который вы хотите отслеживать.) После этого нажмите «Добавить и закрыть». Теперь измените интервал в соответствии с требованиями тестового примера. Теперь перейдите на вкладку «файлы журнала» и измените тип файла журнала на формат csv или tsv. Теперь примените и нажмите ОК.
Если вы хотите запустить / остановить ведение журнала:
Выберите конкретный журнал, который вы хотите запустить и остановить.
На панели инструментов выше вы увидите кнопки запуска и остановки.
Если вы хотите проверить содержимое файла журнала:
Щелкните Параметры / Данные из…
Выберите файл журнала для просмотра, нажмите ОК.
Перейти к экрану карты (Просмотр / Карта)
Нажмите Edit / Add to chart.
Добавьте необходимые элементы в диаграмму. (В случае, если требуется проверить утечку памяти, вам необходимо просмотреть PrivateBytes процессов и их _Total)
Считайте значения из диаграммы (минимальные и максимальные значения отображаются в нижней части диаграммы)
Если вы хотите отслеживать передачу данных по сети:
Отображение экрана карты (Просмотр / Карта)
Нажмите Edit / Add для входа в журнал и выберите элементы Network Interface \ Bytes Sent, если вы установили его в dl.
Или Network Inerface \ Bytes Received, если вы установите его в CRS-PC +
Мониторинг использования памяти:
В меню Пуск / Программы / Администрирование / запустите программу Performance Monitor.
Нажмите на кнопку, чтобы открыть окно добавления процессов.
Заполните поля следующим образом:
Счетчик: частные байты
Экземпляр: процесс, для которого необходимо отобразить занятость памяти.
Повторите последние два шага для каждого процесса, который необходимо отобразить в памяти.
Закройте окно, которое добавляет процессы
Внизу окна монитора производительности находится список ранее выбранных процессов.
- Теперь откройте файл Perfmon_.csv или Perfmon_.tsv с помощью WordPad или Excel.
Если вы открыли файл в Excel, то с помощью опции «Сохранить как» сохраните файл в формате Microsoft Excel.
Также приглашаем поучаствовать в открытом вебинаре на тему «Методы LINQ, которые сделают всё за вас» — на нем участники обсудят шесть представителей семейства технологий LINQ, три составляющих основной операции запроса, отложенное и немедленное выполнение, параллельные запросы.
Любой, кто работал на крупном корпоративном проекте, знает, что утечки памяти подобны крысам в большом отеле. Вы можете не замечать их, когда их мало, но вы всегда должны быть начеку на случай, если они расплодятся, проберутся на кухню и загадят все вокруг.
В среде со сборкой мусора термин «утечка памяти» представляется немного контринтуитивным. Как может произойти утечка памяти, когда есть сборщик мусора (GC — garbage collector), который берет на себя задачу высвобождения памяти?
На это есть две основные связанные между собой причины. Первая основная причина — это когда у вас есть объекты, на которые все еще ссылаются, но которые фактически не используются. Поскольку на них ссылаются, сборщик мусора не будет их удалять, и они останутся нетронутыми навсегда, занимая память. Это может произойти, например, когда вы подписываетесь на event и никогда не отменяете подписку.
Давайте же перейдем к моему списку лучших практик:
1. Обнаружение утечек памяти с помощью окна средств диагностики
Если вы перейдете в Debug | Windows | Show Diagnostic Tools, вы увидите это окно. Как и я когда-то, вы, вероятно, уже видели это окно после установки Visual Studio, сразу же закрыли его и никогда больше о нем не вспоминали. Окно средств диагностики может быть весьма полезным. Оно может помочь вам легко обнаружить 2 проблемы: утечки памяти и GC Pressure (давление на сборщик мусора).
Когда у вас есть утечки памяти, график использования памяти процессом (Process Memory) выглядит следующим образом:
По желтым линиям, идущим сверху, вы можете наблюдать, как сборщик мусора пытается высвободить память, но загруженность памяти все-равно продолжает расти.
В случае GC Pressure, график использования памяти процессом выглядит следующим образом:
GC Pressure — это когда вы создаете и удаляете новые объекты настолько быстро, что сборщик мусора просто не успевает за вами. Как вы видите на картинке, объем потребляемой памяти близок к своему пределу, а сборка мусора происходит очень часто.
С помощью этого метода вы не сможете найти определенные утечки памяти, но вы навскидку можете обнаружить, что у вас есть проблема с утечкой памяти, что само по себе уже несет пользу. В Visual Studio Enterprise окно средств диагностики также включает встроенный профилировщик памяти, который позволяет обнаружить конкретную утечку. Мы поговорим о профилировании памяти в третьем пункте.
2. Обнаружение утечек памяти с помощью диспетчера задач, Process Explorer или PerfMon
Второй самый простой способ обнаружить серьезные проблемы с утечками памяти — с помощью диспетчера задач (Task Manager) или Process Explorer (от SysInternals). Эти инструменты могут показать объем памяти, который использует ваш процесс. Если она постоянно увеличивается со временем, возможно, у вас утечка памяти.
PerfMon немного сложнее в использовании, но у него есть хороший график потребления памяти с течением времени. Вот график моего приложения, которое бесконечно выделяет память, не освобождая ее. Я использую счетчик Process | Private Bytes.
Обратите внимание, что этот метод заведомо ненадежен. Вы можете наблюдать увеличение потребления памяти только потому, что еще не отработал сборщик мусора. Также стоит вопрос об общей и приватной памяти, поэтому вы можете упустить утечки памяти и/или диагностировать утечки, которые не являются вашими собственными (объяснение). Наконец, вы можете принять утечку памяти за GC Pressure. В этом случае у вас нет утечек памяти, но вы создаете и удаляете объекты так быстро, что сборщик мусора не поспевает за вами.
Несмотря на недостатки, я упоминаю эту технику, потому что она проста в использовании и иногда является вашим единственным подручным инструментом. Это также хороший индикатор того, что что-то не так (при наблюдении в течение очень длительного периода времени).
3. Использование профилировщика памяти для обнаружения утечек
Профилировщик в работе с утечками памяти подобен ножу шеф-повара. Это основной инструмент для их поиска и исправления. Хотя другие методы могут быть проще в использовании или дешевле (лицензии на профилировщики стоят дорого), все таки стоит овладеть навыком работы хотя с бы один профилировщиком памяти, чтобы при необходимости эффективно решать проблемы утечек памяти.
Все профилировщики работают приблизительно одинаково. Вы можете подключиться к запущенному процессу или открыть файл дампа. Профилировщик создаст снапшот текущей памяти из кучи вашего процесса. Вы можете анализировать снапшот различными способами. Например, вот список всех аллоцированных объектов в текущем снапшоте:
Вы можете увидеть, сколько аллоцировано экземпляров каждого типа, сколько памяти они занимают и путь ссылки на GC Root.
Самый быстрый и полезный метод профилирования — это сравнение двух снапшотов, в которых память должна вернуться в одно и то же состояние. Первый снимок делается перед операцией, а второй после выполнения операции. Например, вы можете повторить эти шаги:
Начните с какого-либо состояния бездействия (Idle state) в вашем приложении. Это может быть Главное меню или что-то в этом роде.
Сделайте снапшот с помощью профилировщика памяти, присоединившись к процессу или сохранив дамп.
Запустите операцию, про которую вы подозреваете, что при ней возникла утечка памяти. Вернитесь в состояние бездействия по ее окончании.
Сделайте второй снапшот.
Сравните оба снапшота с помощью своего профилировщика.
Изучите New-Created-Instances, возможно, это утечки памяти. Изучите «path to GC Root» и попытайтесь понять, почему эти объекты не были освобождены.
Вот отличное видео, где в профилировщике памяти SciTech сравниваются два снапшота, в результате чего обнаруживается утечка памяти:
4. Используйте «Make Object ID» для поиска утечек памяти
Предположим, вы подозреваете, что в определенном классе есть утечка памяти. Другими словами, вы подозреваете, что после выполнения определенного сценария этот класс остается ссылочным и никогда не собирается сборщиком мусора. Чтобы узнать, действительно ли сборщик мусора собрал его, выполните следующие действия:
Поместите точку останова туда, где создается экземпляр класса.
Наведите курсор на переменную, чтобы открыть всплывающую подсказку отладчика, затем щелкните правой кнопкой мыши и используйте Make Object ID . Вы можете ввести в окне Immediate $1 , чтобы убедиться, что Object ID был создан правильно.
Завершите сценарий, который должен был освободить ваш экземпляр от ссылок.
Спровоцируйте сборку мусора с помощью известных волшебных строчек кода.
5. В появившемся окне непосредственной отладки введите $1 . Если оно возвращает null , значит, сборщик мусора собрал ваш объект. Если нет, у вас утечка памяти.
Здесь я отлаживаю сценарий с утечкой памяти:
А здесь я отлаживаю аналогичный сценарий, в котором нет утечек памяти:
Вы можете принудительно выполнить сборку мусора, вводя волшебные строки в окне непосредственной отладки, что делает эту технику полноценной отладкой без необходимости изменять код.
5. Избегайте известных способов заиметь утечки памяти
Риск нарваться на утечки памяти есть всегда, но есть определенные паттерны, которые помогут получить их с большей вероятностью. Я предлагаю быть особенно осторожным при их использовании и проактивно проверять утечки памяти с помощью таких методов, как последний приведенный здесь пункт.
Вот некоторые из наиболее распространенных подозреваемых:
Статические переменные, коллекции и, в частности, статические события всегда должны вызывать подозрения. Помните, что все статические переменные являются GC Roots, поэтому сборщик мусора никогда не собирает их.
Кэширование — любой тип механизма кэширования может легко вызвать утечку памяти. Кэширую информацию в памяти, в конечном итоге он переполнится и вызовет исключение OutOfMemory. Решением может быть периодическое удаление старых элементов или ограничение объема кэширования.
Привязки WPF могут быть опасными. Практическое правило — всегда выполнять привязку к DependencyObject или к INotifyPropertyChanged. Если вы этого не сделаете, WPF создаст сильную ссылку на ваш источник привязки (то есть ViewModel) из статической переменной, что приведет к утечке памяти. Дополнительную информацию о WPF утечках можно найти в этом полезном треде StackOverflow.
Захваченные члены. Может быть достаточно очевидно, что метод обработчика событий подразумевает, что на объект ссылаются, но когда переменная захвачена анонимным методом — на нее также ссылаются. Вот пример такой утечки памяти:
Потоки, которые никогда не завершаются. Live Stack каждого из ваших потоков считается GC Root. Это означает, что до тех пор, пока поток не завершится, любые ссылки из его переменных в стеке не будут собираться сборщиком мусора. Это также включает таймеры. Если обработчик тиков вашего таймера является методом, то объект метода считается ссылочным и не собирается. Вот пример такой утечки памяти:
6. Используйте шаблон Dispose для предотвращения утечек неуправляемой памяти
Оператор using за кулисами преобразует код в оператор try / finally , где метод Dispose вызывается в finally .
Когда вы сами выделяете неуправляемые ресурсы, вам определенно следует использовать шаблон Dispose . Вот пример:
Смысл этого шаблона — разрешить явное удаление ресурсов. А также чтобы добавить гарантии того, что ваши ресурсы будут удалены во время сборки мусора (в Finalizer ), если Dispose() не был вызван.
GC.SuppressFinalize(this) также имеет важное значение. Она гарантирует, что Finalizer не будет вызван при сборке мусора, если объект уже был удален. Объекты с Finalizer-ами освобождаются иначе и намного дороже. Finalizer добавляется к F-Reachable-Queue , которая позволяет объекту пережить дополнительную генерацию сборщика мусора. Есть и другие сложности.
7. Добавление телеметрии памяти из кода
Иногда вам может понадобиться периодически регистрировать использование памяти. Возможно, вы подозреваете, что на вашем рабочем сервере есть утечка памяти. Возможно, вы захотите предпринять какие-то действия, когда ваша память достигнет определенного предела. Или, может быть, у вас просто есть хорошая привычка следить за своей памятью.
Из самого приложения мы можем получить много информации. Получить текущую используемую память очень просто:
Для получения дополнительной информации вы можете использовать PerformanceCounter — класс, который используется для PerfMon :
Заключение
Не знаю, как у вас, но моя цель, поставленная на новый год, такова: лучшее управление памятью.
Я надеюсь, что эта статья принесет вам пользу и я буду рад, если вы подпишетесь на мой блог или оставите комментарий ниже. Любые отзывы приветствуются.
Читайте также: