Как освободить память на Java?


146

Есть ли способ освободить память в Java, похожий на free()функцию C ? Или установка объекта в null и использование GC - единственный вариант?


151
Хорошо ... давайте сделаем одну вещь прямо. Тот факт, что вы думаете, что что-то является плохой практикой, а не чем-то, что поощряет делать, не делает это достойным голосования. Это ясный и правильный вопрос, спрашивающий, существует ли способ освободить память в Java без использования сборки мусора. Хотя это может быть обескураженным и, как правило, бесполезным или хорошей идеей, вы не можете знать, что не существует сценариев, когда это может потребоваться без знания того, что знает Феликс. Феликс, возможно, даже не планирует использовать его. Он может просто захотеть узнать, возможно ли это. Это никоим образом не заслуживает голосования.
Даниэль Бингхэм

7
Для пояснения, это нацелено на того, кто бы ни проголосовал за это - не обязательно предыдущие комментарии.
Даниэль Бингхэм

Ответы:


96

Java использует управляемую память, поэтому единственный способ распределить память - использовать newоператор, а единственный способ освободить память - использовать сборщик мусора.

Этот технический документ по управлению памятью (PDF) может помочь объяснить, что происходит.

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

Согласно документации Java ,

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


5
Это заставляет сборщика мусора бежать. Это не заставляет его освобождать память, хотя ...
Пабло Санта Круз

13
Нет, Пабло, это не заставляет GC бежать.
Джеспер

1
Очень надежный человек сказал мне, что все сборщики мусора в HotSpotVM System.gc()полностью игнорируют .
Эско

1
На WinXP Java SE GC запускает каждый System.gc () или почти каждый, но API документ не гарантирует это.
teodozjan

2
@Pablo Santa Cruz Что ты имеешь в виду, что это не освобождает память? Я только что проверил это на моей программе, которая, казалось, имела утечку, и использование памяти, как казалось, стабилизировалось? И Даниэль только что сказал, что это только говорит о том, почему процент использования оперативной памяти всегда стабилизировался каждый раз, когда я вызывал метод. Вы, люди, путаете меня.

65

Кажется, никто не упомянул явную установку ссылок на объекты null, что является законным методом «освобождения» памяти, которую вы, возможно, захотите рассмотреть.

Например, допустим, что List<String>в начале метода вы объявили метод, размер которого увеличился до очень большого, но требовался только до середины метода. В этот момент вы можете установить ссылку List, чтобы nullсборщик мусора мог потенциально вернуть этот объект до завершения метода (и ссылка в любом случае выпадает из области видимости).

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


8
Если вы действительно выполняете много работы над объектом, который используется только для части метода, который я предлагаю; ваш метод слишком скомпилирован, разбейте метод на части до и после или используйте блок для первой половины кода (последний более полезен для тестовых сценариев)
Питер Лори

5
Место, где установка ссылки на объект на ноль имеет важное значение, это когда на него ссылаются из другого долгоживущего объекта (или, возможно, из статической переменной). Например, если у вас есть долгоживущий массив больших объектов, и вы перестали использовать один из этих объектов, вы должны установить для ссылки на массив значение null, чтобы сделать объект доступным для GC.
Hot Licks

22
System.gc(); 

Запускает сборщик мусора.

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

Не рекомендуется.

Редактировать: я написал оригинальный ответ в 2009 году. Сейчас 2015.

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

  • Если вы используете GC на ограниченном количестве компьютеров, возможно, стоит установить точку балансировки нагрузки вдали от текущей машины, подождать, пока она завершит обслуживание подключенных клиентов, через некоторое время после завершения зависания соединений, а затем просто -запустите JVM. Это ужасное решение, но если вы посмотрите на System.gc (), принудительный перезапуск может быть возможным временным ограничением.
  • Попробуйте использовать другой сборщик мусора. Например, (новый за последние шесть лет) коллектор G1 представляет собой модель с низкой паузой; в целом он использует больше ресурсов ЦП, но лучше не делать принудительного останова при выполнении. Поскольку серверные процессоры сейчас почти все имеют несколько ядер, это действительно хороший компромисс.
  • Посмотрите на ваши флаги настройки использования памяти. Особенно в новых версиях Java, если у вас не так много долгосрочных объектов, подумайте об увеличении размера newgen в куче. newgen (молодой) - это место, где размещаются новые объекты. Для веб-сервера все, что было создано для запроса, размещено здесь, и если это пространство слишком мало, Java потратит дополнительное время на обновление объектов до более долгоживущей памяти, где их дороже убивать. (Если newgen немного слишком маленький, вы будете платить за него.) Например, в G1:
    • XX: G1NewSizePercent (по умолчанию 5; вероятно, не имеет значения.)
    • XX: G1MaxNewSizePercent (по умолчанию 60; возможно, поднять это.)
  • Подумайте, чтобы сказать сборщику мусора, что вы не в порядке с более длинной паузой. Это будет вызывать более частые запуски GC, чтобы позволить системе сохранить остальные ограничения. В G1:
    • XX: MaxGCPauseMillis (по умолчанию 200.)

1
Комментируя мой собственный пост, это часто ничего не делает, и повторный вызов может привести к нестабильной работе JVM и так далее. Это может также наехать на вашу собаку; подходить с осторожностью.
Дин Дж

1
Я бы уделил большое внимание части «подсказок» «Вызов метода gc предполагает, что JVM расширяет усилия»
Мэтт

2
@Jesper, ответ Дина гласит: «предлагает». На самом деле он опубликовал точную документацию из метода Javadocs ...
б

2
@Software Monkey: Да, я мог бы просто отредактировать его. Но так как Дин Дж, очевидно, был активен (опубликовал это всего несколько минут назад), я подумал, что было любезно попросить его сделать это. Если бы он этого не сделал, я бы вернулся сюда, сделал правку и удалил мой комментарий.
Даниэль Приден

1
Также стоит сказать, ПОЧЕМУ это не рекомендуется. Если JVM обратит внимание на «предложение» запустить GC, это почти наверняка замедлит работу вашего приложения, возможно, на много порядков!
Стивен С

11

* «Я лично полагаюсь на обнуление переменных в качестве заполнителя для будущего правильного удаления. Например, я беру время, чтобы аннулировать все элементы массива, прежде чем фактически удалить (сделать ноль) сам массив».

Это не нужно. Способ работы Java GC заключается в том, что он находит объекты, которые не имеют на них ссылки, поэтому, если у меня есть Object x со ссылкой (= variable) a, которая указывает на него, GC не удалит его, потому что есть ссылка к этому объекту:

a -> x

Если вы ноль, чем это происходит:

a -> null
     x

Так что теперь x не имеет ссылки, указывающей на него, и будет удалено. То же самое происходит, когда вы устанавливаете ссылку на объект, отличный от x.

Поэтому, если у вас есть массив arr, который ссылается на объекты x, y и z, и переменная a, которая ссылается на массив, это выглядит так:

a -> arr -> x
         -> y
         -> z

Если вы ноль, чем это происходит:

a -> null
     arr -> x
         -> y
         -> z

Таким образом, GC находит arr как не имеющий ссылки на него и удаляет его, что дает вам такую ​​структуру:

a -> null
     x
     y
     z

Теперь GC находит x, y и z и удаляет их тоже. Обнуление каждой ссылки в массиве не сделает ничего лучше, оно просто израсходует процессорное время и пространство в коде (тем не менее, это не повредит дальше. GC все равно сможет работать так, как должен) ).


5

Причиной желания освободить память из любой программы (Java или нет) является выделение большего объема памяти другим программам на уровне операционной системы. Если мое java-приложение использует 250 МБ, я могу уменьшить его до 1 МБ и сделать 249 МБ доступными для других приложений.


Если вам необходимо явно освободить фрагмент размером 249 МБ в Java-программе, управление памятью не будет первым делом, над которым я бы хотел работать.
Марк ДиМилло

3
Но освобождение хранилища внутри вашей кучи Java не делает (в общем случае) хранилище доступным для других приложений.
Hot Licks

5

Чтобы расширить ответ и комментарий Яниса Ксантофулоса и Hot Licks (извините, я пока не могу комментировать!), Вы можете установить параметры виртуальной машины, как в этом примере:

-XX:+UseG1GC -XX:MinHeapFreeRatio=15 -XX:MaxHeapFreeRatio=30

В моем jdk 7 это освободит неиспользуемую память виртуальной машины, если более 30% кучи освободится после GC, когда виртуальная машина простаивает. Вам, вероятно, нужно будет настроить эти параметры.

Хотя я не видел, чтобы это подчеркивалось в приведенной ниже ссылке, обратите внимание, что некоторые сборщики мусора могут не подчиняться этим параметрам, и по умолчанию java может выбрать один из них для вас, если у вас есть более одного ядра (отсюда и аргумент UseG1GC выше ).

Аргументы ВМ

Обновление: для java 1.8.0_73 я видел, как JVM иногда выпускает небольшие суммы с настройками по умолчанию. По-видимому, делает это только в том случае, если ~ 70% кучи не используется, хотя ... не знаю, будет ли более агрессивное освобождение, если в ОС недостаточно физической памяти.


4

Я провел эксперименты на этом.

Это правда, что System.gc(); предлагает только запустить сборщик мусора.

Но вызов System.gc();после установки всех ссылок на null, улучшит производительность и занятость памяти.


Я думаю, что вы не можете сказать наверняка, что «вызов System.gc (); после установки всех ссылок на null улучшится производительность и использование памяти». Потому что System.gc () имеет огромную вычислительную сложность. И даже после вызова System.gc () и сбора мусора jvm может не вернуть память ОС или системе. JVM может сохранить память для дальнейшего использования. Смотрите этот ответ .
Г-н Абу Нафи Ибна Захид

3

Если вы действительно хотите выделить и освободить блок памяти, вы можете сделать это с помощью прямых байтовых буферов. Существует даже непереносимый способ освобождения памяти.

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

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


3

Полностью от javacoffeebreak.com/faq/faq0012.html

Поток с низким приоритетом автоматически заботится о сборке мусора для пользователя. Во время простоя поток может быть вызван, и он может начать освобождать память, ранее выделенную объекту в Java. Но не волнуйтесь - это не удалит ваши объекты на вас!

Когда нет ссылок на объект, он становится честной игрой для сборщика мусора. Вместо вызова некоторой подпрограммы (например, free в C ++) вы просто присваиваете всем ссылкам на объект значение null или присваиваете ссылку новому классу.

Пример :

public static void main(String args[])
{
  // Instantiate a large memory using class
  MyLargeMemoryUsingClass myClass = new MyLargeMemoryUsingClass(8192);

  // Do some work
  for ( .............. )
  {
      // Do some processing on myClass
  }

  // Clear reference to myClass
  myClass = null;

  // Continue processing, safe in the knowledge
  // that the garbage collector will reclaim myClass
}

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

System.gc();

Сборщик мусора попытается освободить свободное пространство, и ваше приложение может продолжить работу с максимально возможным объемом памяти (проблемы фрагментации памяти могут возникнуть на некоторых платформах).


1

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

Я лично полагаюсь на обнуление переменных в качестве заполнителя для будущего правильного удаления. Например, я беру время, чтобы аннулировать все элементы массива, прежде чем фактически удалить (сделать ноль) сам массив.

Но мой случай очень специфичен, и я знаю, что при этом бьюсь об эффективности.


1

* "Например, допустим, вы объявили List в начале метода, размер которого увеличился до очень большого, но требовался только до середины метода. В этот момент вы можете установить для ссылки List значение null позволить сборщику мусора потенциально восстановить этот объект до завершения метода (и ссылка в любом случае выходит из области видимости). " *

Это правильно, но это решение не может быть обобщено. При установке ссылки на объект List на null -will- сделать память доступной для сборки мусора, это верно только для объекта List примитивных типов. Если вместо этого объект List содержит ссылочные типы, установка объекта List = null не разыменует -any- ссылочных типов, содержащихся в списке. В этом случае установка параметра List object = null приведет к потере всех содержащихся ссылочных типов, чьи объекты не будут доступны для сборки мусора, если алгоритм сбора мусора не достаточно умен, чтобы определить, что объекты были осиротевшими.


1
Это на самом деле не правда. Сборщик мусора Java достаточно умен, чтобы справиться с этим правильно. Если вы обнулите список (а объекты в списке не имеют других ссылок на них), GC может восстановить все объекты в списке. Он может предпочесть не делать этого в настоящее время, но он в конечном итоге восстановит их. То же самое касается циклических ссылок. По сути, способ, которым работает GC, заключается в том, чтобы явно искать объекты-сироты и затем возвращать их. Это вся работа GC. То, как вы это описываете, сделает ГК совершенно бесполезным.
Даккарон

1

По всей Java обеспечивает автоматическую сборку мусора, иногда вам нужно знать, насколько велик объект и сколько его осталось. Свободно использовать память программно import java.lang;и Runtime r=Runtime.getRuntime(); получать значения памяти, используя mem1=r.freeMemory();для освобождения памяти вызов r.gc();метода и вызоваfreeMemory()


1

Рекомендация от JAVA - присвоить нуль

С https://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html

Явное присвоение нулевого значения переменным, которые больше не нужны, помогает сборщику мусора идентифицировать части памяти, которые можно безопасно восстановить. Хотя Java обеспечивает управление памятью, она не предотвращает утечки памяти и не использует чрезмерное количество памяти.

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

Одним из способов обнаружения утечек памяти является использование инструментов профилирования и создание снимков памяти после каждой транзакции. Приложение без утечек в устойчивом состоянии будет показывать устойчивую активную память кучи после сборок мусора.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.