2013-03-12 3 views
1

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

import java.util.ArrayList; 
import java.util.List; 

public class Test { 

public static void main(String[] args) { 

    class Obj { 
     int i; 

     Obj(int i) { 
      this.i = i; 
     } 
    } 

    List<Obj> list; 
    while(true) { 
     list = new ArrayList<Obj>(); 
     for(int i = 0; i < 1000; i++) { 
      Obj o = new Obj(i); 
      list.add(o); 
     } 

     try { 
      Thread.sleep(1); //<-- added to give the gc time to trash the previous iteration 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 
} 

Когда это будет выполнено (особенно с большим количеством объектов, которые добавляются в список каждой итерации), вы можете посмотреть объем памяти, используемый для увеличения быстро. Я смог быстро ударить 2 гигабайта с добавлением 10000 объектов на каждой итерации. Казалось бы, списки (и другие типы объектов, которые используют список связанных списков, таких как функция) не любят удаляться.

Когда я не добавил объекты в список, куча памяти не увеличилась вообще (что означает удаление мусора, выполняющее свою работу). Я попробовал сбросить список до нуля на каждую итерацию, а затем повторно объявить ее, а также вызвать метод clear(). Ничто не работает. Всякий раз, когда я использую List в цикле, моя RAM кричит о помощи.

Итак, почему это происходит? Почему избавление от мусора не избавляет от списка каждую итерацию, а не позволяет им складываться? Разве интерфейс List не позволяет использовать этот вид? Неужели я просто не даю мусорному убору достаточно времени, чтобы избавиться от последней инстанции?

+1

Примечание: 'Thread.sleep (1)' спит в течение 1 миллисекунды, а не 1 секунду. –

+0

Да, я знаю. Я тоже пробовал с большей продолжительностью. – CoderTheTyler

+2

Используйте 'System.gc()' для запуска сборки мусора. сон не делает трюка. Более того, «list» по-прежнему ссылается на текущий фрейм функции, поэтому его нельзя собирать. – kan

ответ

5

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

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

Однако, когда куча молодого поколения заполняется, GC запускается и собирает все недостижимые объекты.

На следующем скриншоте, верхний правый график, показывающий размер кучи:

VisualVM

Как вы можете видеть, есть резкое падение как раз перед 19:47:50. Это когда молодое поколение заполнилось, и когда GC заработала и собрала все старые списки.

Обратите внимание, что если вы используете инструменты ОС для мониторинга использования памяти, вы вряд ли увидите падение при запуске сборщика мусора. Когда некоторые объекты кучи освобождаются, память, которую они занимали, обычно не возвращается в ОС. Однако он может быть повторно использован одним и тем же процессом.

Если проблема с использованием памяти является проблемой, вам необходимо вернуться к параметрам, которые вы поставляете в JVM.

+0

Я делаю подобный процесс позже в программе и на короткие промежутки времени, но память никогда не выпускается. Даже если я позволяю программе сидеть там, память будет медленно накапливаться. – CoderTheTyler

+0

Возможно, вы в этом уверены. Может быть, диспетчер задач не правильно читает использование памяти? Я попытался подключить его, чтобы остановить создание списков, а затем начать снова, но память никогда не отпускается (в соответствии с диспетчером задач. – CoderTheTyler

+0

Я сделал то же самое и получил очень похожий граф. Я в java 7 на linux – digitaljoel

0

Вам необходимо установить list на null после каждой итерации в цикле while. Таким образом, известный GarbageCollector может безопасно очистить.

Также это может привести к поведению. http://docs.oracle.com/cd/E19159-01/819-3681/abebi/index.html

+0

Даже когда я это делаю, проблема сохраняется. – CoderTheTyler

0

(Обычно я просто комментарий и задать для набора опций VM, но у меня нет достаточного количества повторений очков, чтобы сделать это)

Я просто угадывание, но, возможно, вы установили -Xms в 1 Гб? Этот код не протекает, но в зависимости от ваших параметров виртуальной машины процесс может по-прежнему занимать много памяти.

0

В вашем коде отсутствует утечка памяти. Вы можете быть уверены в этом на 100%. Однако это не означает, что вы не можете исчерпать память, даже если все ваши активные объекты меньше вашей кучи.

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

Также важно понять, что утечки памяти протестированы с помощью инструментов профилирования, а не путем мониторинга того, закончилось ли у приложения нехватка памяти.

Смежные вопросы