Подтвердить что ты не робот

Использование памяти Burst в Java

Я пытаюсь получить представление о правильном использовании памяти и сборке мусора в Java. Я никоим образом не новичок-программист, но мне всегда кажется, что, когда Java коснется некоторой памяти, она никогда не будет выпущена для других приложений. В этом случае вы должны убедиться, что ваша пиковая память никогда не бывает слишком высокой, или ваше приложение будет постоянно использовать все, что пиковое использование памяти было.

Я написал небольшую пробную программу, пытающуюся это продемонстрировать. Он в основном имеет 4 кнопки...

  • Заполните область видимости класса BigList = new ArrayList<string>() примерно с 25 000 000 длинными строковыми элементами.
  • Вызов BigList.clear()
  • Повторно перенастройте список - BigList = new ArrayList<string>() (чтобы уменьшить размер списка)
  • Вызов System.gc() - Да, я знаю, это не означает, что GC действительно будет работать, но это то, что у нас есть.

Итак, я провел некоторое тестирование в Windows, Linux и Mac OS при использовании мониторов задач по умолчанию, чтобы проверить использование записанных сообщений о процессах. Вот что я нашел...

  • Windows. Накачка списка, вызов clear, а затем вызов GC несколько раз не уменьшит использование памяти. Однако перераспределение списка с помощью new и последующее вызов GC несколько раз уменьшит использование памяти до начальных уровней. ИМО, это приемлемо.
  • Linux (я использовал дистрибутив Mint 11 с Sun JVM) - Те же результаты, что и Windows.
  • Mac OS. Я выполнил шаги sames, как описано выше, но даже если повторная инициализация вызовов списков в GC, похоже, не имеет никакого эффекта. Программа будет работать с сотнями МБ ОЗУ, хотя я ничего не имею в памяти.

Кто-нибудь может мне это объяснить? Некоторые люди рассказали мне кое-что о памяти "кучи", но я до сих пор не совсем понимаю это, и я не уверен, что это применимо здесь. Из того, что я слышал об этом, я не должен видеть поведение, которое я нахожу в Windows и Linux, в любом случае.

Разве это просто разница в том, как Mac OS Activity Monitor измеряет использование памяти или что-то еще происходит? Я бы предпочел, чтобы моя программа не работала на больших объемах использования ОЗУ. Спасибо за понимание.

4b9b3361

Ответ 1

JVM Sun/Oracle не возвращает ненужную память в систему. Если вы дадите ему большой максимальный размер кучи, и вы в какой-то момент используете это кучное пространство, JVM не вернет его обратно в ОС для других целей. Другие JVM будут делать это (JRockit привык, но я не думаю, что он делает больше).

Итак, для JVM Oracles вам нужно настроить приложение и свою систему на максимальный уровень использования, а именно, как это работает. Если используемая память может управляться с помощью массивов байтов (например, работа с изображениями или что-то еще), вы можете использовать отображенные байт-буферы вместо массивов байт Java. Буферы с байт-байтами берутся прямо из системы и не являются частью кучи. Когда вы освобождаете эти объекты (и они GC'd, я считаю, но не уверен), память будет возвращена системе. Скорее всего, вам придется играть с тем, кто предполагает, что он вообще применим вообще.

Ответ 2

... но мне всегда кажется, что как только Java коснется некоторой памяти, она ушла навсегда. Вы никогда не получите его обратно.

Это зависит от того, что вы подразумеваете под "уходом навсегда".

Я также слышал, что он сказал, что некоторые JVM возвращают ОС обратно, когда они готовы и способны. К сожалению, с учетом того, что API-интерфейсы низкоуровневой памяти обычно работают, JVM должна возвращать целые сегменты и, как правило, сложно "эвакуировать" сегмент, чтобы он мог быть возвращен.

Но я не стал бы полагаться на это... потому что есть разные вещи, которые могут помешать возврату памяти. Скорее всего, JVM не вернет память ОС. Но он не "ушел навсегда" в том смысле, что JVM будет продолжать использовать его. Даже если JVM никогда не подходит к пиковому использованию снова, вся эта память поможет сделать сборщик мусора более эффективным.

В этом случае вы должны убедиться, что ваша пиковая память никогда не бывает слишком высокой, или ваше приложение будет постоянно потреблять сотни МБ ОЗУ.

Это неверно. Предполагая, что вы принимаете стратегию начинать с небольшой кучи и позволяя ей расти, JVM не будет запрашивать значительно больше памяти, чем пиковая память. JVM не будет постоянно потреблять больше памяти... если ваше приложение не имеет утечки памяти и (в результате) его пиковая память не имеет ограничений.

(Комментарии, приведенные ниже, показывают, что это не то, что он пытался сказать. Тем не менее, это то, что он сказал.)


По вопросу эффективности сбора мусора мы можем моделировать стоимость запуска эффективного сборщика мусора, как:

cost ~= (amount_of_live_data * W1) + (amount_of_garbage * W2)

где W1 и W2 - (мы предполагаем) константы, зависящие от коллектора. (На самом деле это чрезмерное упрощение. Первая часть не является линейной функцией числа живых объектов. Однако я утверждаю, что это не имеет значения для следующего.)

Эффективность коллектора может быть заявлена ​​как:

efficiency = cost / amount_of_garbage_collected

который (если мы предположим, что GC собирает все данные) расширяется до

efficiency ~= (amount_of_live_data * W1) / amount_of_garbage + W2.

Когда GC запускается,

heap_size ~= amount_of_live_data + amount_of_garbage

так

efficiency ~= W1 * (amount_of_live_data / (heap_size - amount_of_live_data) )
              + W2.

Другими словами:

  • когда вы увеличиваете размер кучи, эффективность стремится к константе (W2), но
  • вам нужно большое соотношение heap_size и amount_of_live_data, чтобы это произошло.

Другое дело, что для эффективного копировального коллектора W2 покрывает только стоимость обнуления пространства, занимаемого мусорными объектами, в "из космоса". Остальное (отслеживание, копирование живых объектов в "космос" и обнуление "из пространства", которое они занимали) является частью первого члена исходного уравнения, т.е. Охвачено W1. Что это означает, что W2 вероятен быть значительно меньше, чем W1... и что первый член окончательного уравнения значителен для дольше.

Теперь, очевидно, это теоретический анализ, а модель затрат - это упрощение работы реальных сборщиков мусора. (И он не учитывает "настоящую" работу, выполняемую приложением, или эффекты на системном уровне, связывающие слишком много памяти.) Однако математика говорит мне, что с точки зрения эффективности GC большая куча действительно очень помогает.

Ответ 3

Как только программа завершается, используется ли использование памяти в taskmanager в окнах? Я думаю, что память освобождается, но не отображается, как выпущено мониторами задач по умолчанию в ОС, которую вы контролируете. Пройдите этот вопрос на С++ Проблема с удалением вектора указателей

Ответ 4

Распространенное заблуждение состоит в том, что Java использует память по мере ее запуска, и там для нее должна быть возможность вернуть память в ОС. На самом деле Oracle/Sun JVM резервирует виртуальную память как непрерывный блок памяти сразу же после ее запуска. Если недостаточно непрерывной виртуальной памяти, она не работает при запуске, даже если программа не собирается использовать это.

Что происходит, так это то, что ОС достаточно умна, чтобы не выделять физическую память программе до ее использования. Он не может легко восстановить память, но ее можно поменять на диск, если это необходимо, и оно не использовалось какое-то время. Java не справляется с тем, что части кучи меняются на диск очень хорошо, поэтому этого следует избегать.

Ответ 5

Большинство JVM не могут или не могут выпустить ранее приобретенную память обратно в ОС хоста, если это не требуется atm. Это потому, что это дорогостоящая и сложная задача. Сборщик мусора применяется только к памяти кучи в виртуальной машине Java. Поэтому он не возвращает обратно (free() in C terms) память в ОС. Например. если большой объект больше не используется, память будет помечена как свободная в куче JVM с помощью GC и не будет выпущена в ОС.

Ответ 6

Java выделяет память только объектам. Нет явного выделения памяти. На самом деле Java даже рассматривает типы массивов как объекты. Каждый раз, когда объект создается, он приходит в кучу.

В среде выполнения Java используется сборщик мусора, который восстанавливает память, занятую объектом, когда он определяет, что объект больше не доступен. Это автоматический процесс.

Вызов System.gc() может не собирать мусор в то время, когда вы его вызываете; поэтому ваша память не уменьшается. В общем, лучше разрешить системе решать, когда нужно собирать кучу, и делать ли полную коллекцию.

System.gc() даже не вызывает сбор мусора; это просто намек на JVM, который "теперь может быть хорошим временем для очистки"

Явная память Java объяснила здесь link2

Ответ 7

Есть несколько отличных документов, созданных Sun/Oracle, описывающих сборку Java Garbage Collection. Быстрый поиск по "настройке коллекции мусора Java", например: http://www.oracle.com/technetwork/java/gc-tuning-5-138395.html а также http://java.sun.com/docs/hotspot/gc1.4.2/

Введение состояний документа Oracle;

Стандартная версия платформы Java TM 2 (платформа J2SE TM) используется для широкий спектр приложений от небольших апплетов на настольных компьютерах до веб-сайтов услуг на больших серверах. В платформе J2SE версии 1.4.2 там были четыре сборщика мусора, из которых можно было выбирать, но без явный выбор пользователем последовательного сборщика мусора всегда выбран. В версии 5.0 выбор коллектора основан на класс машины, на которой запущено приложение.

Этот "разумный выбор" сборщика мусора обычно лучше, но не всегда лучшая. Для пользователя, который хочет сделать свой собственный выбор сборщиков мусора, этот документ предоставит информацию на котором основывается этот выбор. Сначала это будет общее функции сборщиков мусора и варианты настройки для лучшее преимущество этих функций. Примеры приведены в контекст серийного, стоп-мирового коллектора. Затем конкретные функции других коллекционеров будут обсуждаться вместе с факторами которые должны учитываться при выборе одного из других коллекционеров.

Они описывают различные типы доступных коллекционеров и ситуации, в которых они должны использоваться. Я помню, что использовал это JConsole, чтобы определить, как приложение выполняется при запуске с различными различными опциями.

Эти документы дадут вам немного больше информации о том, как происходит сбор, в зависимости от параметров, которые вы используете.

Ответ 8

Я столкнулся с этой проблемой в Windows и нашел решение, поэтому я отправляю его как ответ, если он может помочь другим.

Многие ответы здесь говорят о том, что поведение Java является 1. хорошим и/или 2. неизбежным следствием сбора мусора. Они оба ложны.

Проблема:

Если вы похожи на меня, и вы хотите написать Java для написания небольших приложений для рабочей станции или даже запускать несколько меньших процессов на сервере, то поведение распределения памяти Oracle JVM делает его почти совершенно бесполезным. Даже при работе с -client каждый процесс JVM кладет память, когда-то выделяется и никогда не возвращает ее. Это поведение невозможно отключить. Как отмечает OP: каждый процесс jvm держится на неиспользуемой памяти неограниченно, даже если он никогда не будет использовать его снова и даже тогда, когда другие jvm-процессы голодают. Это необъяснимое поведение делает Oracle бесполезной реализацией для всех, кроме монолитных сценариев с одним приложением.

Также: это НЕ является следствием сбора мусора. Witness.Net, которые работают в Windows, используют сборку мусора и вообще не страдают от этой проблемы.

Решение:

Решение, которое я нашел, это использовать IKVM.NET JVM, который вы используете в качестве замены для замены java.exe на окна. Он компилирует байт-код Java в .Net IL-код и запускается как .Net-процесс. Он также содержит утилиты для преобразования .jar файлов в сборки .NET.dll и .exe. Производительность часто лучше, чем Oracle JVM, и после GC память мгновенно возвращается в ОС. (Примечание: это также работает в Linux с Mono)

Чтобы быть ясным, я все еще полагаюсь на Oracle JVM для всех, кроме моих небольших приложений, а также для отладки моих небольших приложений, но после стабильной работы я использую ikvm для их запуска, как если бы они были родными приложениями для Windows, и это работает так хорошо, Я был поражен. Он имеет множество полезных побочных эффектов. После компиляции библиотеки DLL, связанные между процессами, загружаются только один раз, а приложения отображаются в диспетчере задач как .exe, а не отображаются как javaw.exe.

К сожалению, не все могут использовать ikvm для решения этой проблемы, но я надеюсь, что это поможет тем, кто в моей ситуации.