New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[FLINK-14522] Introduce JavaGcCleanerWrapper to find Java GC Cleaner depending on JVM version #9997
Conversation
Thanks a lot for your contribution to the Apache Flink project. I'm the @flinkbot. I help the community Automated ChecksLast check on commit 765f629 (Wed Dec 04 14:53:45 UTC 2019) Warnings:
Mention the bot in a comment to re-run the automated checks. Review Progress
Please see the Pull Request Review Guide for a full explanation of the review process. The Bot is tracking the review progress through labels. Labels are applied according to the order of the review items. For consensus, approval by a Flink committer of PMC member is required Bot commandsThe @flinkbot bot supports the following commands:
|
|
||
if (foundCleanerClass == null) { | ||
throw new FlinkRuntimeException( | ||
String.format("A proper Java Cleaner class is not found among %s", CLEANER_PROVIDERS), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
public static void main(String[] args) {
System.out.println(Arrays.asList(Foo.INSTANCE));
}
private enum Foo {
INSTANCE
}
output:
[INSTANCE]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you think CLEANER_PROVIDERS
should be an enum?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error will be [...] among [INSTANCE, INSTANCE]
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, I will convert it to the list of CleanerProvider::getCleanerClassName
AtomicInteger callCounter = new AtomicInteger(); | ||
Runnable cleaner = JavaGcCleanerWrapper.create(new Object(), callCounter::incrementAndGet); | ||
System.gc(); | ||
Thread.sleep(100); // more chance for GC to run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you explain this test? It would also pass without System.gc()
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I will rename the test: testCleanOperationRunsOnlyOnceEitherOnGcOrExplicitly
.
If GC does not run immediately (it should run 99%) then explicit cleaner running will do the clean operation. Also the cleaner lambda should not capture the owner object in the error log message, I will fix it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pause is too long imo.
fef7b76
to
060ca00
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for this PR @azagrebin. The changes look good to me. I had some minor comments which you could address while merging the PR. +1 for merging if you've tested this code also with Java 11.
* | ||
* <p>The wrapper looks up the underlying Java GC Cleaner class in different packages | ||
*/ | ||
public class JavaGcCleanerWrapper { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be enum or needs a private constructor as an utility class.
private static final Logger LOG = LoggerFactory.getLogger(JavaGcCleanerWrapper.class); | ||
|
||
private static final String LEGACY_CLEANER_CLASS_NAME = "sun.misc.Cleaner"; // before Java 9 | ||
private static final String JAVA9_CLEANER_CLASS_NAME = "java.lang.ref.Cleaner"; // since Java 9+ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't these fields be part of the implementation of the different CleanerProviders
?
private interface CleanerProvider { | ||
String getCleanerClassName(); | ||
|
||
CleanerFactory createCleanerFactory(Class<?> cleanerClass); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The interface is a bit strange that the provider knows about the class name, but needs to get the Class<?>
passed into the createCleanerFactory
. Couldn't the createCleanerFactory
method do the class lookup?
} | ||
} | ||
|
||
private static class LegacyCleanerFactory implements CleanerFactory { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final
missing.
} | ||
} | ||
|
||
private static class Java9CleanerFactory implements CleanerFactory { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
final
missing.
throw new FlinkRuntimeException("Failed to find Java 9 Cleaner$Cleanable#clean method", e); | ||
} | ||
return new Java9CleanerFactory(cleaner, cleanerRegisterMethod, cleanMethod); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This method could be a bit cleaner if we factor out the individual steps into methods which are responsible for the error handling.
flink-core/src/main/java/org/apache/flink/util/JavaGcCleanerWrapper.java
Outdated
Show resolved
Hide resolved
flink-core/src/main/java/org/apache/flink/util/JavaGcCleanerWrapper.java
Outdated
Show resolved
Hide resolved
AtomicInteger callCounter = new AtomicInteger(); | ||
Runnable cleaner = JavaGcCleanerWrapper.create(new Object(), callCounter::incrementAndGet); | ||
System.gc(); | ||
Thread.sleep(100); // more chance for GC to run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The pause is too long imo.
…depending on JVM version
375bb34
to
765f629
Compare
Thanks for the reviews @tillrohrmann @GJL |
AtomicInteger callCounter = new AtomicInteger(); | ||
Runnable cleaner = JavaGcCleanerWrapper.create(new Object(), callCounter::incrementAndGet); | ||
System.gc(); // not guaranteed to be run always but should in practice | ||
Thread.sleep(10); // more chance for GC to run |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just an idea: CountDownLatch
has a method boolean await(long timeout, TimeUnit unit)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, I just wanted to avoid test instabilities if GC is not called for some reason at once.
What is the purpose of the change
sun.misc.Cleaner
is not available since Java 9.It was moved to
jdk.internal.ref.Cleaner
ofjava.base
module (Open JDK-8148117), but another new public API was introduced to achieve the same behaviour:java.lang.ref.Cleaner
;A popular solution is use reflection to look up for the location of the
Cleaner
class depending on running JVM version.Brief change log
JavaGcCleanerWrapper
which uses reflection to look up and provide properCleaner
implementation depending on JVM version on runtimeJavaGcCleanerWrapperTest
to test that the clean method is calledVerifying this change
Run
JavaGcCleanerWrapperTest
Does this pull request potentially affect one of the following parts:
@Public(Evolving)
: (no)Documentation