Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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
  • Loading branch information
3 people committed Dec 8, 2021
1 parent ec7cb6d commit d7ad546
Show file tree
Hide file tree
Showing 13 changed files with 265 additions and 17 deletions.
1 change: 1 addition & 0 deletions make/data/hotspot-symbols/symbols-unix
Expand Up @@ -147,6 +147,7 @@ JVM_IsArrayClass
JVM_IsCDSDumpingEnabled
JVM_IsConstructorIx
JVM_IsDumpingClassList
JVM_IsFinalizationEnabled
JVM_IsHiddenClass
JVM_IsInterface
JVM_IsPrimitiveClass
Expand Down
6 changes: 4 additions & 2 deletions src/hotspot/share/classfile/classFileParser.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}

Expand Down
3 changes: 3 additions & 0 deletions src/hotspot/share/include/jvm.h
Expand Up @@ -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
************************************************************************/
Expand Down
1 change: 1 addition & 0 deletions src/hotspot/share/oops/instanceKlass.cpp
Expand Up @@ -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) {
Expand Down
10 changes: 10 additions & 0 deletions src/hotspot/share/oops/instanceKlass.hpp
Expand Up @@ -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;
Expand Down
4 changes: 4 additions & 0 deletions src/hotspot/share/prims/jvm.cpp
Expand Up @@ -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))
Expand Down
12 changes: 12 additions & 0 deletions src/hotspot/share/runtime/arguments.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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) {
Expand Down
30 changes: 20 additions & 10 deletions src/java.base/share/classes/java/lang/ref/Finalizer.java
Expand Up @@ -61,9 +61,17 @@ static ReferenceQueue<Object> getQueue() {
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) {
Expand Down Expand Up @@ -130,7 +138,7 @@ public Void run() {

/* Called by Runtime.runFinalization() */
static void runFinalization() {
if (VM.initLevel() == 0) {
if (VM.initLevel() == 0 || ! ENABLED) {
return;
}

Expand Down Expand Up @@ -182,14 +190,16 @@ public void run() {
}

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();
}
}

}
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion src/java.base/share/native/libjava/Finalizer.c
Expand Up @@ -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 changes: 122 additions & 0 deletions test/jdk/java/lang/Object/FinalizationOption.java
@@ -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 changes: 52 additions & 0 deletions test/jdk/java/lang/Object/InvalidFinalizationOption.java
@@ -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);
}
}
}

1 comment on commit d7ad546

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.