Вопрос о сборке мусора Java - PullRequest
2 голосов
/ 17 апреля 2009

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

...

AVLTree tree = new AVLTree();

//insert the data from the first distribution

//get results

...

tree = new AVLTree();

//inser the data from the next distribution

//get results

...

Я делаю это для 3 дистрибутивов. Каждое из них должно быть проверено в среднем 14 раз, а 2 самые низкие / самые высокие значения должны быть удалены для вычисления среднего значения. Это должно быть сделано 2000 раз, каждый раз для 1000 элементов. Другими словами, это 1000, 2000, 3000, ..., 2000000.

Проблема в том, что я могу добраться только до 100000. Когда я попробовал 200000, у меня не хватило места в куче. Я увеличил доступное пространство кучи с -Xmx в командной строке до 1024 м, и он даже не завершил тесты с 200000. Я пробовал 2048 м и снова, это не сработало.

Что я думаю, так это то, что сборщик мусора не избавляется от старых деревьев, когда я делаю tree = new AVL Tree (). Но почему? Я думал, что элементы из старых деревьев больше не будут доступны, и их память будет очищена.

Ответы [ 7 ]

5 голосов
/ 17 апреля 2009

В Java есть хороший инструмент для отслеживания работы GC (или не в вашем случае), JVisualVM, который поставляется с JDK.

Просто запустите это, и он покажет вам, какие объекты занимают кучу, и вы можете как запустить, так и увидеть прогресс GC. Затем вы можете выбрать те из них для пулов, чтобы они могли быть повторно использованы вами, сохраняя ГХ работу.

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

-XX: -UseGCOverheadLimit

5 голосов
/ 17 апреля 2009

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

1 голос
/ 17 апреля 2009

Нет причин, по которым эти деревья не следует собирать, хотя я ожидаю, что до того, как у вас закончится память, вы должны увидеть длинные паузы, поскольку система запускает полный сборщик мусора. Как было отмечено здесь, это не то, что вы видите, вы можете попробовать запустить с флагами, такими как -XX: -PrintGC, -XX: -PrintGCDetails, -XX: -PrintGCTimeStamps, чтобы дать вам больше информации о том, что именно происходит, наряду с, возможно, некоторым подсчетом примерно того, где вы находитесь. Вы также можете явно указать сборщику мусора использовать другой алгоритм сборки мусора.

Однако, это все еще кажется мне маловероятным. Какой другой код работает? Возможно ли, что в самом классе AVLTree есть что-то, что не позволяет его экземплярам быть GC'd? Как насчет регистрации в журнале finalize () для этого класса, чтобы убедиться, что (по крайней мере, некоторые из них) являются коллекционными (например, сделать несколько и вручную вызвать System.gc ())?

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

1 голос
/ 17 апреля 2009

Какую JVM вы используете и какие параметры JVM вы использовали для настройки GC?

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

0 голосов
/ 17 апреля 2009

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

0 голосов
/ 17 апреля 2009

Мы заметили это в серверном продукте. При изготовлении большого количества крошечных предметов, которые быстро выбрасываются, сборщик мусора не может успевать. Проблема становится более заметной, когда крошечные объекты имеют указатели на более крупные объекты (например, объект, который указывает на большой char[]). GC, похоже, не понимает, что если он освобождает крошечный объект, он может освободить более крупный объект. Даже при непосредственном вызове System.gc() это все еще было огромной проблемой (как для 1,5, так и для 1,6 виртуальных машин)!

Что мы в итоге сделали и что я рекомендую вам, так это поддерживать пул объектов. Когда ваш объект больше не нужен, бросьте его в бассейн. Когда вам нужен новый объект, возьмите его из пула или выделите новый, если пул пуст. Это также сэкономит небольшое количество времени по сравнению с чистым распределением, поскольку Java не нужно очищать (bzero) объект.

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

Мы используем пулы char[] и byte[], и мы поддерживаем отдельные «корзины» размеров в пуле (например, мы всегда выделяем массивы размеров, которые имеют степени двух). Наш продукт много строит, и использование пулов показало значительное улучшение производительности.

Примечание: в общем, GC отлично справляется со своей работой. Мы только что заметили, что с небольшими объектами, которые указывают на большие структуры, GC, кажется, не очищает объекты достаточно быстро, особенно когда виртуальная машина находится под нагрузкой процессора. Кроме того, System.gc() - это просто подсказка, которая поможет запланировать поток финализатора для выполнения дополнительной работы. Слишком частый вызов приводит к значительному снижению производительности.

0 голосов
/ 17 апреля 2009

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

Если вы выполняете только тестирование производительности, вы можете использовать функцию java gc (я думаю, в классе System) между тестами или даже повторно запускать программу для каждого дистрибутива.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...