[ 
https://issues.apache.org/jira/browse/GROOVY-7683?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=15067388#comment-15067388
 ] 

Jochen Theodorou commented on GROOVY-7683:
------------------------------------------

How things on the JVM are supposed to be working is the following... imagine a 
class that is nowhere referenced anymore, but in an equally otherwise 
non-referenced class loader and another object. Imagine this other object is 
softly reachable, meaning no strong/weak/phantom reference is pointing to 
it.This is supposed to mean, that the class is softly reachable as well. So in 
theory, as long as the ClassInfo object is not strongly reachable, the 
ClassInfo object could be collected along with the class.
In the practice though, things do not always work like that. I know for sure, 
that the IBM JVM for example needs special options given to it, to even check 
such a case. For the Oracle JVM enabling CMS used to help with this problem. 
For the Oracle JVM the problem is that normally the garbage collection for 
classes and the garbage collection for instances is not done together. So 
unless the ClassInfo object is collected it happens that the class will stay. 
Since ClassInfo objects are normally softly referenced, they will stay till the 
very last moment, increasing the probability of the problem to occur. The fact, 
that there is a multitude of objects referenced by ClassInfo, which hold strong 
references to the class (for example a reflective method object could be one, 
but the type information in the meta methods is already enough) does not help 
the garbage collector at all.
As bad as it sounds, in the end it depends on the implementation of the garbage 
collector if that object can be collected. So it is supposed to work, but can 
for several reasons not sometimes.

Of course that is all theory and past experience. It is very possible, that 
there is somewhere a strong reference, where it should not be. But as I tried 
to explain above, changing ClassInfo to use a weak key for the Class is not 
something I would expect to really help. The patch that is attached here, tries 
it with a SoftReference. WeakReference could be better, but frankly there are 
still many many strong references to the class through other objects hard 
referenced by the ClassInfo object. Could be, that this decreases the number of 
hard references just enough to not be over some threshold optimization in the 
garbage collector and then allows the garbage collector to collect. Or it might 
not have any effect. My expectation is that it won't change anything.

As for the ClassInfoCleanup.. it is not right that it is not stored. When you 
create a Soft/WeakReference, then there will be a reference object and a 
ReferenceQueue. The queue will know the reference. And it is not that those 
references simply vanish. In fact, if you don't clean up the queue, you will 
get a memory leak from that side, even if the objects you reference would be 
collected. But for Weak/SoftReference this is not actually the case, you would 
need PhantomReference for that. On the other hand, you are right, that this 
code has almost zero relevance when it comes to the automatic removal of 
ClassInfo. If the ClassInfo has a strong reference set, the ClassInfo object 
itself will not be weak referenced. Same for the cleanups... there are more to 
support garbage collection a little, than that they are really needed. The real 
code managing the reference is in ManagedLinkedList as far as GlobalClassSet is 
concerned. Or the manged hashmap in GroovyClassValuePreJava7.

Oh, one correction... ClassInfo is mostly weakly reachable in those structures, 
not softly. cachedClassRef and artifactClassLoader are should be soft 
reachable, same for the entries of LocalMap

> Memory leak when using Groovy as JSR-223 scripting language.
> ------------------------------------------------------------
>
>                 Key: GROOVY-7683
>                 URL: https://issues.apache.org/jira/browse/GROOVY-7683
>             Project: Groovy
>          Issue Type: Bug
>          Components: GroovyScriptEngine
>    Affects Versions: 2.4.5
>         Environment: OS: tested on Mac OS X El Capitan and Windows 10.
> JVM: tested on 1.8.0_60 and 1.8.0_65.
>            Reporter: Arkadiusz Gasinski
>              Labels: jsr-223
>         Attachments: 
> 0001-GROOVY-7683-replace-hard-reference-from-ClassInfo-to.patch
>
>
> We have a Java EE 7 web application in production that when handling single 
> HTTP request can load and execute up to several Groovy scripts using the 
> jsr-223 API. This application is deployed to GlassFish 4.1 server cluster 
> with 4 instances, each having 8 GB of RAM available (Xmx=8g). We have to 
> restart them every couple of days (3-4), because of leaking memory. After 
> analyzing a couple of heap dumps, our main suspect is Groovy with its 
> MetaMethodIndex$Entry class (the below table shows the top object from one of 
> the heap dumps).
> ||Class Name||Objects||Shallow Heap||Retained Heap||
> |MetaMethodIndex$Entry| 3 360 001 |  188 160 056 | >= 305 408 024
> To confirm our suspicions, I created simple Maven project with a single test 
> case. The project is available on 
> [GitHub|https://github.com/jigga/groovy-jsr223-leak]. The test case executes 
> 10 different scripts (minimal differences) obtained from a single template 
> 20000 times in 64 worker threads (the main thread is put to sleep for 10 
> seconds before starting worker threads, so that one can attach JVisualVM to 
> the test process). After all threads are done, System.gc() is called to 
> provoke full GC. Attaching to the process in which tests are run with 
> JVisualVM reveals that the memory is not reclaimed.
> To run the test in your local environment, simply clone the 
> [GitHub|https://github.com/jigga/groovy-jsr223-leak] project and run:
> {code}
> mvn test
> {code}
> The same test can be run with the *-Dlanguage=javascript* system option, 
> which switches ScriptEngine from Groovy to Nashorn and uses slightly modified 
> script template (only syntactical differences).
> {code}
> mvn -Dlanguage=javascript test
> {code}
> Running the test case using built-in Nashorn engine reveals no problems - all 
> allocated memory is reclaimed after full GC.
> I know that the test case is run in Java SE environment, but I guess that it 
> clearly reflects the issue. If it's not enough, I can create an arquillian 
> test case.
> This may be a possible duplicate of 
> [GROOVY-7109|https://issues.apache.org/jira/browse/GROOVY-7109].
> Any workarounds for this issue would be greatly appreciated.



--
This message was sent by Atlassian JIRA
(v6.3.4#6332)

Reply via email to