Jeff
Jeff

Reputation: 1554

PermGen Issue - Does Groovy GString Intern Strings?

I have a Grails app that is primarily used as a WebService. It provides a very customized type of caching where it stores objects in concurrent hashmap for a number of seconds and potentially combines like messages before they can be processed by another thread. After several months of running without issue, PermGen space eventually reaches its max and I need to restart the server to prevent an OOMError Exception.

I don't think my issue is related to leaking Classloaders, which seems to be the cause of most PermGen issues. I am not ever redeploying this app. PermGen is filling up just from normal use.

I have a feeling my issue may be due to the large number of unique strings my app is handling. My app is making heavy use of GStrings for both logging and for processing before storing in the hashmap, like so:

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

My question is whether GStrings or Hashmap Entries use String.intern(). If so, I think that would explain why my PermGen Space is filling up. If this is indeed the case, what is the best way to handle this? I have already increased the amount of PermGen space in JVM args. However, it just prolongs the inevitable.

I used the Eclipse Memory Analyzer to compare heap dumps after a month of usage. While this doesn't tell me much about PermGen usage, it was clear that the largest difference between the two heaps are the number of Strings and Hashmap Entries.

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

Does anyone have an idea what might be going on here and how to address it? I would like to avoid having to restart Tomcat every few weeks/months.

Thanks!

UPDATE: Adding output from jmap -permstat. Does anyone know how to read this?

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

Upvotes: 2

Views: 536

Answers (1)

melix
melix

Reputation: 1530

No, Groovy doesn't intern strings (be it with GString or anything else). I would suggest you use jmap -permstat to get some information about what eats your PermGen.

Upvotes: 1

Related Questions