Finalizer being run while class still in use (escape analysis bug)
David Holmes
david.holmes at oracle.com
Wed Oct 10 00:35:50 UTC 2018
Hi Luke,
Moving this over to hotspot-dev. If it is an issue with the JIT escape
analysis then hotspot-compiler-dev would be the place to raise this. But
a general finalization issue may hit GC and runtime as well, so I
redirected to the broader hotspot-dev.
Also note that finalizers can run while methods of a class instance are
still in progress [1]
"Optimizing transformations of a program can be designed that reduce the
number of objects that are reachable to be less than those which would
naively be considered reachable."
- this is one of the evil things about finalizers. So it may not be a
bug, just surprising.
Cheers,
David
[1] https://docs.oracle.com/javase/specs/jls/se11/html/jls-12.html#jls-12.6
On 10/10/2018 10:27 AM, Luke Hutchison wrote:
> A user of a library I maintain, ClassGraph, has reported that a finalizer
> is being called on a class while methods of the class are still running,
> indicating that the class, even though it overrides finalize(), is not
> being correctly marked as GlobalEscape, so that escape analysis is trying
> to garbage-collect the class too early. Is this mailing list the correct
> place to report this bug?
>
> Here is the Maven rule for ClassGraph (you will also need to add Scala
> deps) -- you will need version 4.2.8, since I put a mitigation in place in
> version 4.2.9:
>
> <dependency>
> <groupId>io.github.classgraph</groupId>
> <artifactId>classgraph</artifactId>
> <version>4.2.8</version>
> </dependency>
>
> Here is the Scala code that reproduces the problem 100% of the time:
>
> import io.github.classgraph.{ClassGraph, ClassInfo}
> import scala.collection.JavaConverters._
> import scala.compat.Platform.ConcurrentModificationException
>
> while (true) {
> try {
> new ClassGraph().scan()
> .getResourcesMatchingPattern(".*".r.pattern)
> .asScala
> .map(_.getURL)
> } catch {
> case cme: ConcurrentModificationException =>
> cme.printStackTrace()
> throw cme
> }
> }
>
> Then start the JVM with -XX:+DoEscapeAnalysis
>
> The code new ClassGraph().scan() returns a ScanResult, and then as soon as
> ScanResult::getResourcesMatchingPattern is called with the ScanResult as
> the invocation target, escape analysis sees that the ScanResult has "gone
> out of scope" (despite the fact that the ScanResult is the invocation
> target for a currently-running method), and immediately tries to garbage
> collect it. This should not happen (1) since an invocation target should
> never be marked for garbage collection while its methods are being run, and
> (2) since ScanResult overrides Object::finalize, so should have been marked
> as "GlobalEscape" by escape analysis.
>
> The finalizer for the ScanResult class calls ScanResult::close, which
> clears the allResources list:
>
> https://github.com/classgraph/classgraph/blob/b9e8165d34575378697a727888457abb164311b8/src/main/java/io/github/classgraph/ScanResult.java#L940
>
> This results in a ConcurrentModificationException since
> ScanResult::getResourcesMatchingPattern is still iterating through
> allResources:
>
> https://github.com/classgraph/classgraph/blob/b9e8165d34575378697a727888457abb164311b8/src/main/java/io/github/classgraph/ScanResult.java#L383
>
> I read somewhere (and it makes sense) that escape analysis is performed by
> the compiler, so maybe this is a Scala bug, but this behavior is triggered
> by the JVM switch -XX:+DoEscapeAnalysis , and I couldn't find any
> references to escape analysis information in the Java classfile format
> spec, so that leads me to think this may be a JVM bug (which is why I am
> asking on this list).
>
> Obviously having a garbage collector run a finalizer run while methods of
> the object are still running is a very serious bug. Any pointers as to
> where to report this issue would be appreciated. Thanks!
>
More information about the jdk-dev
mailing list