8276422: Add command-line option to disable finalization
Co-authored-by: David Holmes <dholmes@openjdk.org> Co-authored-by: Brent Christian <bchristi@openjdk.org> Reviewed-by: dholmes, kbarrett, bchristi
This commit is contained in:
parent
ec7cb6d5d3
commit
d7ad546758
@ -147,6 +147,7 @@ JVM_IsArrayClass
|
||||
JVM_IsCDSDumpingEnabled
|
||||
JVM_IsConstructorIx
|
||||
JVM_IsDumpingClassList
|
||||
JVM_IsFinalizationEnabled
|
||||
JVM_IsHiddenClass
|
||||
JVM_IsInterface
|
||||
JVM_IsPrimitiveClass
|
||||
|
@ -2835,7 +2835,8 @@ Method* ClassFileParser::parse_method(const ClassFileStream* const cfs,
|
||||
annotation_default_length,
|
||||
CHECK_NULL);
|
||||
|
||||
if (name == vmSymbols::finalize_method_name() &&
|
||||
if (InstanceKlass::is_finalization_enabled() &&
|
||||
name == vmSymbols::finalize_method_name() &&
|
||||
signature == vmSymbols::void_method_signature()) {
|
||||
if (m->is_empty_method()) {
|
||||
_has_empty_finalizer = true;
|
||||
@ -4171,7 +4172,8 @@ void ClassFileParser::set_precomputed_flags(InstanceKlass* ik) {
|
||||
bool f = false;
|
||||
const Method* const m = ik->lookup_method(vmSymbols::finalize_method_name(),
|
||||
vmSymbols::void_method_signature());
|
||||
if (m != NULL && !m->is_empty_method()) {
|
||||
if (InstanceKlass::is_finalization_enabled() &&
|
||||
(m != NULL) && !m->is_empty_method()) {
|
||||
f = true;
|
||||
}
|
||||
|
||||
|
@ -759,6 +759,9 @@ JVM_SupportsCX8(void);
|
||||
JNIEXPORT void JNICALL
|
||||
JVM_ReportFinalizationComplete(JNIEnv *env, jobject finalizee);
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
JVM_IsFinalizationEnabled(JNIEnv *env);
|
||||
|
||||
/*************************************************************************
|
||||
PART 2: Support for the Verifier and Class File Format Checker
|
||||
************************************************************************/
|
||||
|
@ -141,6 +141,7 @@
|
||||
|
||||
#endif // ndef DTRACE_ENABLED
|
||||
|
||||
bool InstanceKlass::_finalization_enabled = true;
|
||||
|
||||
static inline bool is_class_loader(const Symbol* class_name,
|
||||
const ClassFileParser& parser) {
|
||||
|
@ -329,7 +329,17 @@ class InstanceKlass: public Klass {
|
||||
|
||||
static bool _disable_method_binary_search;
|
||||
|
||||
// Controls finalizer registration
|
||||
static bool _finalization_enabled;
|
||||
|
||||
public:
|
||||
|
||||
// Queries finalization state
|
||||
static bool is_finalization_enabled() { return _finalization_enabled; }
|
||||
|
||||
// Sets finalization state
|
||||
static void set_finalization_enabled(bool val) { _finalization_enabled = val; }
|
||||
|
||||
// The three BUILTIN class loader types
|
||||
bool is_shared_boot_class() const {
|
||||
return (_misc_flags & _misc_is_shared_boot_class) != 0;
|
||||
|
@ -690,6 +690,10 @@ JVM_ENTRY(void, JVM_ReportFinalizationComplete(JNIEnv * env, jobject finalizee))
|
||||
MANAGEMENT_ONLY(FinalizerService::on_complete(JNIHandles::resolve_non_null(finalizee), THREAD);)
|
||||
JVM_END
|
||||
|
||||
JVM_ENTRY(jboolean, JVM_IsFinalizationEnabled(JNIEnv * env))
|
||||
return InstanceKlass::is_finalization_enabled();
|
||||
JVM_END
|
||||
|
||||
// java.io.File ///////////////////////////////////////////////////////////////
|
||||
|
||||
JVM_LEAF(char*, JVM_NativePath(char* path))
|
||||
|
@ -40,6 +40,7 @@
|
||||
#include "logging/logStream.hpp"
|
||||
#include "logging/logTag.hpp"
|
||||
#include "memory/allocation.inline.hpp"
|
||||
#include "oops/instanceKlass.hpp"
|
||||
#include "oops/oop.inline.hpp"
|
||||
#include "prims/jvmtiExport.hpp"
|
||||
#include "runtime/arguments.hpp"
|
||||
@ -2887,6 +2888,17 @@ jint Arguments::parse_each_vm_init_arg(const JavaVMInitArgs* args, bool* patch_m
|
||||
if (FLAG_SET_CMDLINE(ErrorFileToStdout, true) != JVMFlag::SUCCESS) {
|
||||
return JNI_EINVAL;
|
||||
}
|
||||
} else if (match_option(option, "--finalization=", &tail)) {
|
||||
if (strcmp(tail, "enabled") == 0) {
|
||||
InstanceKlass::set_finalization_enabled(true);
|
||||
} else if (strcmp(tail, "disabled") == 0) {
|
||||
InstanceKlass::set_finalization_enabled(false);
|
||||
} else {
|
||||
jio_fprintf(defaultStream::error_stream(),
|
||||
"Invalid finalization value '%s', must be 'disabled' or 'enabled'.\n",
|
||||
tail);
|
||||
return JNI_EINVAL;
|
||||
}
|
||||
} else if (match_option(option, "-XX:+ExtendedDTraceProbes")) {
|
||||
#if defined(DTRACE_ENABLED)
|
||||
if (FLAG_SET_CMDLINE(ExtendedDTraceProbes, true) != JVMFlag::SUCCESS) {
|
||||
|
@ -61,9 +61,17 @@ final class Finalizer extends FinalReference<Object> { /* Package-private; must
|
||||
return queue;
|
||||
}
|
||||
|
||||
static final boolean ENABLED = isFinalizationEnabled();
|
||||
|
||||
private static native boolean isFinalizationEnabled();
|
||||
|
||||
/* Invoked by VM */
|
||||
static void register(Object finalizee) {
|
||||
new Finalizer(finalizee);
|
||||
if (ENABLED) {
|
||||
new Finalizer(finalizee);
|
||||
} else {
|
||||
throw new InternalError("unexpected call to Finalizer::register when finalization is disabled");
|
||||
}
|
||||
}
|
||||
|
||||
private void runFinalizer(JavaLangAccess jla) {
|
||||
@ -130,7 +138,7 @@ final class Finalizer extends FinalReference<Object> { /* Package-private; must
|
||||
|
||||
/* Called by Runtime.runFinalization() */
|
||||
static void runFinalization() {
|
||||
if (VM.initLevel() == 0) {
|
||||
if (VM.initLevel() == 0 || ! ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -182,14 +190,16 @@ final class Finalizer extends FinalReference<Object> { /* Package-private; must
|
||||
}
|
||||
|
||||
static {
|
||||
ThreadGroup tg = Thread.currentThread().getThreadGroup();
|
||||
for (ThreadGroup tgn = tg;
|
||||
tgn != null;
|
||||
tg = tgn, tgn = tg.getParent());
|
||||
Thread finalizer = new FinalizerThread(tg);
|
||||
finalizer.setPriority(Thread.MAX_PRIORITY - 2);
|
||||
finalizer.setDaemon(true);
|
||||
finalizer.start();
|
||||
if (ENABLED) {
|
||||
ThreadGroup tg = Thread.currentThread().getThreadGroup();
|
||||
for (ThreadGroup tgn = tg;
|
||||
tgn != null;
|
||||
tg = tgn, tgn = tg.getParent());
|
||||
Thread finalizer = new FinalizerThread(tg);
|
||||
finalizer.setPriority(Thread.MAX_PRIORITY - 2);
|
||||
finalizer.setDaemon(true);
|
||||
finalizer.start();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -194,7 +194,10 @@ java.launcher.X.usage=\n\
|
||||
\ override or augment a module with classes and resources\n\
|
||||
\ in JAR files or directories.\n\
|
||||
\ --source <version>\n\
|
||||
\ set the version of the source in source-file mode.\n\n\
|
||||
\ set the version of the source in source-file mode.\n\
|
||||
\ --finalization=<value>\n\
|
||||
\ controls finalization\n\
|
||||
\ <value> is one of "enabled" or "disabled"\n\n\
|
||||
These extra options are subject to change without notice.\n
|
||||
|
||||
# Translators please note do not translate the options themselves
|
||||
|
@ -32,4 +32,7 @@ Java_java_lang_ref_Finalizer_reportComplete(JNIEnv* env, jclass cls, jobject fin
|
||||
JVM_ReportFinalizationComplete(env, finalizee);
|
||||
}
|
||||
|
||||
|
||||
JNIEXPORT jboolean JNICALL
|
||||
Java_java_lang_ref_Finalizer_isFinalizationEnabled(JNIEnv* env, jclass cls) {
|
||||
return JVM_IsFinalizationEnabled(env);
|
||||
}
|
||||
|
122
test/jdk/java/lang/Object/FinalizationOption.java
Normal file
122
test/jdk/java/lang/Object/FinalizationOption.java
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8276422
|
||||
* @summary add command-line option to disable finalization
|
||||
* @run main/othervm FinalizationOption yes
|
||||
* @run main/othervm --finalization=enabled FinalizationOption yes
|
||||
* @run main/othervm --finalization=disabled FinalizationOption no
|
||||
*/
|
||||
public class FinalizationOption {
|
||||
static volatile boolean finalizerWasCalled = false;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void finalize() {
|
||||
finalizerWasCalled = true;
|
||||
}
|
||||
|
||||
static void create() {
|
||||
new FinalizationOption();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the finalizer thread is or is not running. The finalizer thread
|
||||
* is a thread in the root thread group whose named is "Finalizer".
|
||||
* @param expected boolean indicating whether a finalizer thread should exist
|
||||
* @return boolean indicating whether the expectation was met
|
||||
*/
|
||||
static boolean checkFinalizerThread(boolean expected) {
|
||||
ThreadGroup root = Thread.currentThread().getThreadGroup();
|
||||
for (ThreadGroup parent = root;
|
||||
parent != null;
|
||||
root = parent, parent = root.getParent())
|
||||
;
|
||||
|
||||
int nt = 100;
|
||||
Thread[] threads;
|
||||
while (true) {
|
||||
threads = new Thread[nt];
|
||||
nt = root.enumerate(threads);
|
||||
if (nt < threads.length)
|
||||
break;
|
||||
threads = new Thread[nt + 100];
|
||||
}
|
||||
|
||||
Thread ft = null;
|
||||
for (int i = 0; i < nt; i++) {
|
||||
if ("Finalizer".equals(threads[i].getName())) {
|
||||
ft = threads[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
String msg = (ft == null) ? "(none)" : ft.toString();
|
||||
boolean passed = (ft != null) == expected;
|
||||
System.out.printf("Finalizer thread. Expected: %s Actual: %s %s%n",
|
||||
expected, msg, passed ? "Passed." : "FAILED!");
|
||||
return passed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether there was a call to the finalize() method.
|
||||
* @param expected boolean whether finalize() should be called
|
||||
* @return boolean indicating whether the expecation was met
|
||||
*/
|
||||
static boolean checkFinalizerCalled(boolean expected) {
|
||||
create();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
System.gc();
|
||||
try {
|
||||
Thread.sleep(10L);
|
||||
} catch (InterruptedException ie) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
if (finalizerWasCalled) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
boolean passed = (expected == finalizerWasCalled);
|
||||
System.out.printf("Call to finalize(). Expected: %s Actual: %s %s%n",
|
||||
expected, finalizerWasCalled,
|
||||
passed ? "Passed." : "FAILED!");
|
||||
return passed;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
boolean finalizationEnabled = switch (args[0]) {
|
||||
case "yes" -> true;
|
||||
case "no" -> false;
|
||||
default -> {
|
||||
throw new AssertionError("usage: FinalizationOption yes|no");
|
||||
}
|
||||
};
|
||||
|
||||
boolean threadPass = checkFinalizerThread(finalizationEnabled);
|
||||
boolean calledPass = checkFinalizerCalled(finalizationEnabled);
|
||||
|
||||
if (!threadPass || !calledPass)
|
||||
throw new AssertionError("Test failed.");
|
||||
}
|
||||
}
|
52
test/jdk/java/lang/Object/InvalidFinalizationOption.java
Normal file
52
test/jdk/java/lang/Object/InvalidFinalizationOption.java
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
|
||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||
*
|
||||
* This code is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License version 2 only, as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This code is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* version 2 for more details (a copy is included in the LICENSE file that
|
||||
* accompanied this code).
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License version
|
||||
* 2 along with this work; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*
|
||||
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
||||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8276422
|
||||
* @summary Invalid/missing values for the finalization option should be rejected
|
||||
* @library /test/lib
|
||||
* @run driver InvalidFinalizationOption
|
||||
*/
|
||||
|
||||
import jdk.test.lib.process.ProcessTools;
|
||||
import jdk.test.lib.process.OutputAnalyzer;
|
||||
|
||||
public class InvalidFinalizationOption {
|
||||
public static void main(String[] args) throws Exception {
|
||||
record TestData(String arg, String expected) { }
|
||||
|
||||
TestData[] testData = {
|
||||
new TestData("--finalization", "Unrecognized option"),
|
||||
new TestData("--finalization=", "Invalid finalization value"),
|
||||
new TestData("--finalization=azerty", "Invalid finalization value")
|
||||
};
|
||||
|
||||
for (var data : testData) {
|
||||
ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(data.arg);
|
||||
OutputAnalyzer output = new OutputAnalyzer(pb.start());
|
||||
output.shouldContain(data.expected);
|
||||
output.shouldHaveExitValue(1);
|
||||
}
|
||||
}
|
||||
}
|
@ -34,17 +34,20 @@ import jdk.test.lib.jfr.TestClassLoader;
|
||||
|
||||
/**
|
||||
* @test
|
||||
* @bug 8266936 8276422
|
||||
* @summary The test verifies that classes overriding finalize() are represented as events.
|
||||
* @key jfr
|
||||
* @requires vm.hasJFR
|
||||
* @library /test/lib /test/jdk
|
||||
* @run main/othervm -Xlog:class+unload,finalizer -Xmx16m jdk.jfr.event.runtime.TestFinalizerStatisticsEvent
|
||||
* @run main/othervm -Xlog:class+unload,finalizer -Xmx16m --finalization=disabled jdk.jfr.event.runtime.TestFinalizerStatisticsEvent disabled
|
||||
*/
|
||||
|
||||
public final class TestFinalizerStatisticsEvent {
|
||||
private final static String TEST_CLASS_NAME = "jdk.jfr.event.runtime.TestFinalizerStatisticsEvent$TestClassOverridingFinalize";
|
||||
private final static String TEST_CLASS_UNLOAD_NAME = "jdk.jfr.event.runtime.TestFinalizerStatisticsEvent$TestClassUnloadOverridingFinalize";
|
||||
private final static String EVENT_PATH = EventNames.FinalizerStatistics;
|
||||
private static boolean disabled = false;
|
||||
|
||||
// Declare as public static to prevent the compiler from optimizing away all unread writes
|
||||
public static TestClassLoader unloadableClassLoader;
|
||||
@ -52,6 +55,10 @@ public final class TestFinalizerStatisticsEvent {
|
||||
public static Object overridingInstance;
|
||||
|
||||
public static void main(String[] args) throws Throwable {
|
||||
if (args.length > 0 && "disabled".equals(args[0])) {
|
||||
disabled = true;
|
||||
System.out.println("Testing with finalization disabled");
|
||||
}
|
||||
Recording recording1 = new Recording();
|
||||
recording1.enable(EVENT_PATH);
|
||||
Recording recording2 = new Recording();
|
||||
@ -69,8 +76,12 @@ public final class TestFinalizerStatisticsEvent {
|
||||
recording1.stop(); // rotation writes an event for TEST_CLASS_NAME into recording1 which now has 4 events reflecting this test case (3 chunks + 1 unload)
|
||||
|
||||
try {
|
||||
verify(recording2);
|
||||
verify(recording1);
|
||||
if (disabled) {
|
||||
verifyDisabled(recording1);
|
||||
} else {
|
||||
verifyEnabled(recording2);
|
||||
verifyEnabled(recording1);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
recording2.close();
|
||||
@ -84,7 +95,8 @@ public final class TestFinalizerStatisticsEvent {
|
||||
System.gc();
|
||||
}
|
||||
|
||||
private static void verify(Recording recording) throws Throwable {
|
||||
/* Verify correct operation with finalization enabled */
|
||||
private static void verifyEnabled(Recording recording) throws Throwable {
|
||||
boolean foundTestClassName = false;
|
||||
boolean foundTestClassUnloadName = false;
|
||||
List<RecordedEvent> events = Events.fromRecording(recording);
|
||||
@ -108,6 +120,19 @@ public final class TestFinalizerStatisticsEvent {
|
||||
Asserts.assertTrue(foundTestClassUnloadName, "The class: " + TEST_CLASS_UNLOAD_NAME + " overriding finalize() is not found");
|
||||
}
|
||||
|
||||
/* Verify no jdk.FinalizerStatistics events with finalization disabled */
|
||||
private static void verifyDisabled(Recording recording) throws Throwable {
|
||||
int f10nEvents = 0;
|
||||
List<RecordedEvent> events = Events.fromRecording(recording);
|
||||
for (RecordedEvent event : events) {
|
||||
System.out.println("Event:" + event);
|
||||
if ("jdk.FinalizerStatistics".equals(event.getEventType().getName())) {
|
||||
f10nEvents++;
|
||||
}
|
||||
}
|
||||
Asserts.assertEquals(f10nEvents, 0, "Finalization disabled, but recorded " + f10nEvents + " jdk.FinalizerStatistics events");
|
||||
}
|
||||
|
||||
static public class TestClassOverridingFinalize {
|
||||
public boolean finalized = false;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user