Ads
Comments 20
0
К сожалению это не так, грубо говоря, PermGen теперь просто называется Metaspace, и вы все равно получите OutOfMemoryError

Это в том случае, если вы его ограничиваете параметром командной строки, верно? Если не ограничиваете, то описания классов могут занимать хоть всю доступную приложению память. Или это не так?
+1
Я не смотрел подробно как работает Metaspace в Java 8. Но даже если есть возможность дать всю доступную память для описания классов, утечка есть утечка. Рано или поздно она скушает всю доступную память. Другое дело, что в каком-то конкретном случае утечка может быть небольшая, OutOfMemoryError может случиться только после очень большого количества передеплоев, и тут надо решать, что целесообразнее: устранять утечку, или жить так.
+3
>Я не стал глубоко копать и искать официальную документацию, а методом проб и ошибок определил, что для Java 7 и Tomcat 7 необходимо и достаточно добавить JVM опцию -XX:+UseConcMarkSweepGC

Довольно спорный совет. Включив это опцию вы меняете сборщик мусора на Concurrent Mark Sweep, а это может значительно повлиять на работу приложения. При этом возможность выгружать классы во время работы приложения вы получаете просто как бонус.
Если говорить о других сборщиках мусора, то там обычно сборка мусора в PermGen выполняется при Full GC.
0
Согласен с вами, что менять алгоритм GC просто потому что так заработало — спорно. В свое оправдание могу сказать, что CMS вроде бы является рекомендуемым для продакшна (пруф1, пруф2). Ну и если вы дадите ссылку на документацию относительно сборки мусора в PermGen с разными алгоритмами сборки — буду очень благодарен.
0
Первая ссылка:
The CMS garbage collector is the first and most-widely used low-latency collector

Ключевое слово здесь low-latency. Используя CMS вы улучшаете latency, но ухудшаете throughput. Грубо говоря, вы жертвуете производительностью, зато избегаете длинных GC пауз.

По второй ссылке — вы бы еще древнее что-нибудь нашли! Статья времен WebLogic Server 9.2, 2006 г. Да и что-то там c настройками перемудрили — XX:SurvivorRatio=128 — это что-же survivor space занимает 32/130MB ~ 250KB?

По сути, вы можете выполнить прекрасную команду в консоли
java -XX:+UnlockDiagnosticVMOptions -XX:+PrintFlagsFinal -version | grep Unloading
Она покажет вам все опции, влияющие на unloading классов. По дефолту ClassUnloading включена, т.е. по факту классы в permgen будет собираться. А вот как раз если вы используете CMS GC, то для unloading'а классов вам нужно включить -XX:+CMSClassUnloadingEnabled.

А вообще по теме GC рекомендую отличный доклад от Владимира Иванова , а на недавней конференции Joker как раз был доклад про утечки памяти.
0
Насколько я знаю -XX:CMSClassUnloadingEnabled включена по дефолту, при использовании CMS (на не очень древних версиях Hotspot).
0
На моей машине

jdk1.7.0_45
bool CMSClassUnloadingEnabled = false {product}

jdk1.8
bool CMSClassUnloadingEnabled = true {product}
0
Посмотрел как ведут себя разные сборщики мусора с PermGen-ом:
Parallel (дефолтный на моей машине) — PermGen не очищается, падает OutOfMemoryError
Serial — PermGen очищается после достижения 100%
Concurrent Mark Sweep — PermGen очищается после достижения 100%

Вызвать очистку PermGen вручную мне не удалось, на System.gc() не реагирует.

При этом
bool CMSClassUnloadingEnabled = false {product}
bool ClassUnloading = true {product}

Все-таки категорически не хватает документации.

JDK 1.7.0_55-b13, Windows7, amd64, Intel Core i5 650 @ 3.20GHz
0
Тесты в студию!

Собственно, вот мои тесты: gist.github.com/SerCeMan/876fcdfaca602af2df8d

Судя по тесту, классы выгружаются при любом GC, даже если мы опция CMSClassUnloadingEnabled стоит false.

Конечно же нужно забывать отдавать ClassLoader на откуп сборщику, все таки он хранит в себе ссылки на все загруженные классы.
0
Я тестировал на реальном приложении, передеплоивал его в томкате, выложить не могу по понятным причинам, но думаю можно собрать простенькое веб-приложение со спрингом и получить те же результаты.

С вашим тестом классы действительно выгружаются с любым gc.
Кстати, вот более простой вариант теста gist.github.com/zgmnkv/0e7ac1fe91c7d51d41a3
Попутно более-менее прояснил значение опций:
ClassUnloading — если true, то классы выгружаются; если false, то не выгружаются, и падает OutOfMemoryError, логично в общем то.
CMSClassUnloadingEnabled — влияет только на CMS GC; если true, то классы выгружаются чаще, PermGen не доростает до максимума.
+2
Совсем недавно на CodeFreeze была встреча на эту тему. Только внимание было уделено Eclipse Memory Analyzer.
-2
> Также популярным вариантом утечки является ThreadLocal переменная, которой присвоен объект из веб-приложения для потока из общего пула. В этом случае поток хранит ссылку на объект.

Это совершенно неверно. Поток нигде не хранит ссылку на объект. Внутри ThreadLocal использует структуру похожую на Map для сопоставления объекта с потоком. Сборщик мусора соберет ваш ThreadLocal и все его объекты также как обычный HashMap. При этом потоки естественно останутся нетронутыми.
0
Почему вы так решили, даже не заглянув в исходники? ThreadLocal внутри себя хранит ровно одно нестатическое поле — это int threadLocalHashCode. Сами данные лежат в Thread.threadLocals. Структура ThreadLocalMap действительно похожа на HashMap (точнее это мультимэп), вот только ключи — это не потоки, а идентификаторы переменных. Автор поста как раз прав.
0
По факту там используются слабые ссылки на сами ThreadLocal, но это не решает всей проблемы: объект ThreadLocalMap.Entry хранит также сильную ссылку на значение. Поэтому даже если ThreadLocal протух, и Entry теперь ни на что не ссылается, сам Entry существует, пока ThreadLocalMap явно не освободит этот слот, что может произойти после нескольких обращений к другим ThreadLocal-переменным в том же треде. Если этого никто делать не будет, значение из исходного ThreadLocal (и всё, что из него растёт) останутся сильно связанными.
0
Сорри, действительно. Entry у ThreadLocalMap — это WeakReference на ThreadLocal, так что ThreadLocal все-таки собирается GC. Странность реализации заключается в том, что Entry и value продолжает храниться в таблице, пока Thread не вызовет не set() для любой TheadLocal.
0
Собственно, вся сила ThreadLocal как раз в том, что они не требуют конкурентного доступа: никаких проблем при одновременной записи из десяти тредов, данные полностью разделены. Реализация вида ConcurrentHashMap<Thread, Object> была бы во много раз медленнее из-за рехэшинга разделённого объекта, возможности попасть двум тредам в одну корзину и так далее.
0
Это каждый раз надо делать?
В общем как человек который уже много лет пишет на яве могу сказать что я не использую несколько приложений на одном tomcat
У нас работает несколько экземпляров jetty одного и того же приложения чтобы распределять нагрузку. Запросы распределяет nginx.
Мне казалось так все делают.
0
Что вы имеете ввиду под «каждый раз»? При разработке нового приложения?
Это дело каждого, если вам мешают такие ошибки — исправляйте их, если не мешают — не исправляйте. Если приложение написано правильно, завершает все созданные потоки, убирает проставленные ThreadLocal, не проставляет свои ссылки в системные объекты, то у вас не должно возникать подобных утечек.
По поводу распределения нагрузки — это из другой оперы. Я имел ввиду, что разные приложения или разные части одного приложения задеплоены в одном tomcat-е.
Only those users with full accounts are able to leave comments.  , please.