Проблема с PermGen — поддерживает ли Groovy GString внутренние строки?

У меня есть приложение Grails, которое в основном используется как WebService. Он обеспечивает очень настраиваемый тип кэширования, при котором объекты хранятся в параллельной хэш-карте в течение нескольких секунд и потенциально объединяются, прежде чем они могут быть обработаны другим потоком. После нескольких месяцев работы без проблем пространство PermGen в конечном итоге достигает своего максимума, и мне нужно перезапустить сервер, чтобы предотвратить исключение OOMError.

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

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

mOrig.msg += mNew.msg
log.debug("Sending combined message: ${mOrig.msg}")

Мой вопрос заключается в том, используют ли записи GString или Hashmap String.intern(). Если это так, я думаю, это объясняет, почему мое пространство PermGen заполняется. Если это действительно так, как лучше всего справиться с этим? Я уже увеличил объем пространства PermGen в аргументах JVM. Однако это лишь оттягивает неизбежное.

Я использовал Eclipse Memory Analyzer для сравнения дампов кучи после месяца использования. Хотя это мало что говорит мне об использовании PermGen, было ясно, что самая большая разница между двумя кучами — это количество строк и записей Hashmap.

Class Name                                     |   Objects | Shallow Heap
--------------------------------------------------------------------------
                                               |           |             
char[]                                         |    +1,731 |     +268,984
byte[]                                         |       +35 |     +142,240
java.util.HashMap$Entry                        |    +1,684 |      +80,832
java.lang.String                               |    +1,675 |      +67,000
org.apache.tomcat.util.buf.ByteChunk           |      +202 |      +12,928
org.apache.tomcat.util.buf.MessageBytes        |      +146 |      +11,680
org.apache.tomcat.util.buf.CharChunk           |      +162 |       +9,072
java.lang.Object[]                             |       -31 |       +7,840
java.text.DecimalFormat                        |       +32 |       +6,912
int[]                                          |       +87 |       +6,824
java.lang.String[]                             |       +68 |       +5,872

Кто-нибудь знает, что здесь может быть и как с этим бороться? Я хотел бы избежать перезапуска Tomcat каждые несколько недель/месяцев.

Спасибо!

ОБНОВЛЕНИЕ: Добавлен вывод из jmap -permstat. Кто-нибудь знает, как это читать?

29045 intern Strings occupying 4025008 bytes.
class_loader    classes bytes   parent_loader   alive?  type

<bootstrap>     2221    12948256          null          live    <internal>
0x00002aaac0c40048      1       1952    0x00002aaabe2108a8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac0c3c388      10      211456  0x00002aaac04f0cc8      dead    org/codehaus/groovy/runtime/callsite/CallSiteClassLoader@0x00002aaaaf4cf338
0x00002aaac1a2c698      1       3112    0x00002aaabe2108a8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac0159380      1       3128    0x00002aaac04f0cc8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac0158a28      1       3096    0x00002aaac04f0cc8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1dfa188      1       3248    0x00002aaac04f0cc8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1a2f910      1       1952    0x00002aaabe2108a8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac0c6e390      12      239656    null          dead    org/codehaus/groovy/runtime/callsite/CallSiteClassLoader@0x00002aaaaf4cf338
0x00002aaac1df0a68      1       3200    0x00002aaac0afd9b8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1a2db68      1       3112    0x00002aaabe2108a8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1a36f58      1       3112    0x00002aaabe2108a8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1df9688      1       1952      null          dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac0171e70      1       1968    0x00002aaac04f0cc8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1979200      1       1952      null          dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac01712a8      1       1952    0x00002aaac04f0cc8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1dec140      1       3096    0x00002aaac04f0cc8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac197ad80      1       1952      null          dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1df13e8      1       3120      null          dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1a2f1a0      1       1952    0x00002aaabe2108a8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac197c380      1       1952      null          dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac0c451c0      1       1952    0x00002aaabe2108a8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaabe2183b8      4       14192     null          dead    javax/management/remote/rmi/NoCallStackClassLoader@0x00002aaaae81ef70
0x00002aaabfd24688      1       1952    0x00002aaac04f0cc8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaabecd6250      2       19984   0x00002aaabe2108a8      dead    org/apache/catalina/loader/WebappClassLoader@0x00002aaaaee35f48
0x00002aaabe217da0      94      900176  0x00002aaabe210930      dead    sun/misc/Launcher$AppClassLoader@0x00002aaaae446690
0x00002aaac0170f68      1       1952    0x00002aaac04f0cc8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac0c43bc0      1       1952    0x00002aaabe2108a8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac0c45d60      1       3112    0x00002aaabe2108a8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1df0e68      1       1968      null          dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac01716b8      1       1952    0x00002aaac04f0cc8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1a35c58      1       3112    0x00002aaabe2108a8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac0c462e0      1       1952    0x00002aaabe2108a8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac0c87750      1       1968      null          dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1a2e4b0      1       3112    0x00002aaabe2108a8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaabfd246f0      1       3128    0x00002aaac04f0cc8      dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1de9830      1       3136      null          dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac1982680      1       3120      null          dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20
0x00002aaac0171a60      1       3112      null          dead    sun/reflect/DelegatingClassLoader@0x00002aaaae276b20

person Jeff    schedule 07.12.2012    source источник
comment
Я бы, вероятно, отправил этот вопрос в список рассылки пользователей Groovy, так как вы, вероятно, получите более быстрый и точный ответ.   -  person tim_yates    schedule 07.12.2012


Ответы (1)


Нет, Groovy не интернирует строки (будь то GString или что-то еще). Я бы посоветовал вам использовать jmap -permstat, чтобы получить некоторую информацию о том, что ест ваш PermGen.

person melix    schedule 07.12.2012
comment
Я обновил сообщение, включив в него вывод jmap -permstat. Хотя я не совсем уверен, как это читать. Вы видите что-нибудь просветляющее? - person Jeff; 08.12.2012