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