Finalizer being run while class still in use (escape analysis bug)

Luke Hutchison luke.hutch at gmail.com
Wed Oct 10 00:27:23 UTC 2018


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