7177745: JSR292: Many Callsite relinkages cause target method to always run in interpreter mode
Reviewed-by: jrose, kvn
--- a/hotspot/src/share/vm/code/codeCache.cpp Fri Feb 19 10:06:19 2016 +0100
+++ b/hotspot/src/share/vm/code/codeCache.cpp Fri Feb 19 20:40:20 2016 +0300
@@ -1023,7 +1023,7 @@
// Keeps track of time spent for checking dependencies
NOT_PRODUCT(static elapsedTimer dependentCheckTime;)
-int CodeCache::mark_for_deoptimization(DepChange& changes) {
+int CodeCache::mark_for_deoptimization(KlassDepChange& changes) {
MutexLockerEx mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);
int number_of_marked_CodeBlobs = 0;
--- a/hotspot/src/share/vm/code/codeCache.hpp Fri Feb 19 10:06:19 2016 +0100
+++ b/hotspot/src/share/vm/code/codeCache.hpp Fri Feb 19 20:40:20 2016 +0300
@@ -72,7 +72,7 @@
// Solaris and BSD.
class OopClosure;
-class DepChange;
+class KlassDepChange;
class CodeCache : AllStatic {
friend class VMStructs;
@@ -223,7 +223,7 @@
// Deoptimization
private:
- static int mark_for_deoptimization(DepChange& changes);
+ static int mark_for_deoptimization(KlassDepChange& changes);
#ifdef HOTSWAP
static int mark_for_evol_deoptimization(instanceKlassHandle dependee);
#endif // HOTSWAP
--- a/hotspot/src/share/vm/code/dependencies.hpp Fri Feb 19 10:06:19 2016 +0100
+++ b/hotspot/src/share/vm/code/dependencies.hpp Fri Feb 19 20:40:20 2016 +0300
@@ -664,6 +664,8 @@
virtual bool is_klass_change() const { return false; }
virtual bool is_call_site_change() const { return false; }
+ virtual void mark_for_deoptimization(nmethod* nm) = 0;
+
// Subclass casting with assertions.
KlassDepChange* as_klass_change() {
assert(is_klass_change(), "bad cast");
@@ -753,6 +755,10 @@
// What kind of DepChange is this?
virtual bool is_klass_change() const { return true; }
+ virtual void mark_for_deoptimization(nmethod* nm) {
+ nm->mark_for_deoptimization(/*inc_recompile_counts=*/true);
+ }
+
Klass* new_type() { return _new_type(); }
// involves_context(k) is true if k is new_type or any of the super types
@@ -772,6 +778,10 @@
// What kind of DepChange is this?
virtual bool is_call_site_change() const { return true; }
+ virtual void mark_for_deoptimization(nmethod* nm) {
+ nm->mark_for_deoptimization(/*inc_recompile_counts=*/false);
+ }
+
oop call_site() const { return _call_site(); }
oop method_handle() const { return _method_handle(); }
};
--- a/hotspot/src/share/vm/code/dependencyContext.cpp Fri Feb 19 10:06:19 2016 +0100
+++ b/hotspot/src/share/vm/code/dependencyContext.cpp Fri Feb 19 20:40:20 2016 +0300
@@ -73,7 +73,7 @@
nm->print();
nm->print_dependencies();
}
- nm->mark_for_deoptimization();
+ changes.mark_for_deoptimization(nm);
found++;
}
}
--- a/hotspot/src/share/vm/code/nmethod.cpp Fri Feb 19 10:06:19 2016 +0100
+++ b/hotspot/src/share/vm/code/nmethod.cpp Fri Feb 19 20:40:20 2016 +0300
@@ -536,7 +536,7 @@
_has_method_handle_invokes = 0;
_lazy_critical_native = 0;
_has_wide_vectors = 0;
- _marked_for_deoptimization = 0;
+ _mark_for_deoptimization_status = not_marked;
_lock_count = 0;
_stack_traversal_mark = 0;
_unload_reported = false; // jvmti state
@@ -1459,7 +1459,7 @@
SharedRuntime::get_handle_wrong_method_stub());
}
- if (is_in_use()) {
+ if (is_in_use() && update_recompile_counts()) {
// It's a true state change, so mark the method as decompiled.
// Do it only for transition from alive.
inc_decompile_count();
--- a/hotspot/src/share/vm/code/nmethod.hpp Fri Feb 19 10:06:19 2016 +0100
+++ b/hotspot/src/share/vm/code/nmethod.hpp Fri Feb 19 20:40:20 2016 +0300
@@ -107,6 +107,7 @@
// [Implicit Null Pointer exception table]
// - implicit null table array
+class DepChange;
class Dependencies;
class ExceptionHandlerTable;
class ImplicitExceptionTable;
@@ -188,7 +189,13 @@
bool _has_flushed_dependencies; // Used for maintenance of dependencies (CodeCache_lock)
bool _marked_for_reclamation; // Used by NMethodSweeper (set only by sweeper)
- bool _marked_for_deoptimization; // Used for stack deoptimization
+
+ enum MarkForDeoptimizationStatus {
+ not_marked,
+ deoptimize,
+ deoptimize_noupdate };
+
+ MarkForDeoptimizationStatus _mark_for_deoptimization_status; // Used for stack deoptimization
// used by jvmti to track if an unload event has been posted for this nmethod.
bool _unload_reported;
@@ -462,8 +469,16 @@
void set_unloading_clock(unsigned char unloading_clock);
unsigned char unloading_clock();
- bool is_marked_for_deoptimization() const { return _marked_for_deoptimization; }
- void mark_for_deoptimization() { _marked_for_deoptimization = true; }
+ bool is_marked_for_deoptimization() const { return _mark_for_deoptimization_status != not_marked; }
+ void mark_for_deoptimization(bool inc_recompile_counts = true) {
+ _mark_for_deoptimization_status = (inc_recompile_counts ? deoptimize : deoptimize_noupdate);
+ }
+ bool update_recompile_counts() const {
+ // Update recompile counts when either the update is explicitly requested (deoptimize)
+ // or the nmethod is not marked for deoptimization at all (not_marked).
+ // The latter happens during uncommon traps when deoptimized nmethod is made not entrant.
+ return _mark_for_deoptimization_status != deoptimize_noupdate;
+ }
void make_unloaded(BoolObjectClosure* is_alive, oop cause);
--- a/hotspot/src/share/vm/oops/instanceKlass.cpp Fri Feb 19 10:06:19 2016 +0100
+++ b/hotspot/src/share/vm/oops/instanceKlass.cpp Fri Feb 19 20:40:20 2016 +0300
@@ -1879,7 +1879,7 @@
return dep_context;
}
-int InstanceKlass::mark_dependent_nmethods(DepChange& changes) {
+int InstanceKlass::mark_dependent_nmethods(KlassDepChange& changes) {
return dependencies().mark_dependent_nmethods(changes);
}
--- a/hotspot/src/share/vm/oops/instanceKlass.hpp Fri Feb 19 10:06:19 2016 +0100
+++ b/hotspot/src/share/vm/oops/instanceKlass.hpp Fri Feb 19 20:40:20 2016 +0300
@@ -56,7 +56,7 @@
// forward declaration for class -- see below for definition
class BreakpointInfo;
class ClassFileParser;
-class DepChange;
+class KlassDepChange;
class DependencyContext;
class fieldDescriptor;
class jniIdMapBase;
@@ -821,7 +821,7 @@
// maintenance of deoptimization dependencies
inline DependencyContext dependencies();
- int mark_dependent_nmethods(DepChange& changes);
+ int mark_dependent_nmethods(KlassDepChange& changes);
void add_dependent_nmethod(nmethod* nm);
void remove_dependent_nmethod(nmethod* nm, bool delete_immediately);
--- a/hotspot/src/share/vm/runtime/vmStructs.cpp Fri Feb 19 10:06:19 2016 +0100
+++ b/hotspot/src/share/vm/runtime/vmStructs.cpp Fri Feb 19 20:40:20 2016 +0300
@@ -960,7 +960,6 @@
nonstatic_field(nmethod, _compile_id, int) \
nonstatic_field(nmethod, _comp_level, int) \
nonstatic_field(nmethod, _exception_cache, ExceptionCache*) \
- nonstatic_field(nmethod, _marked_for_deoptimization, bool) \
\
unchecked_c2_static_field(Deoptimization, _trap_reason_name, void*) \
\
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/jsr292/ContinuousCallSiteTargetChange.java Fri Feb 19 20:40:20 2016 +0300
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2016, 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
+ * @library /testlibrary
+ * @run main ContinuousCallSiteTargetChange
+ */
+import java.lang.invoke.*;
+import jdk.test.lib.*;
+
+public class ContinuousCallSiteTargetChange {
+ static void testServer() throws Exception {
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ "-XX:+IgnoreUnrecognizedVMOptions",
+ "-server", "-XX:-TieredCompilation", "-Xbatch",
+ "-XX:PerBytecodeRecompilationCutoff=10", "-XX:PerMethodRecompilationCutoff=10",
+ "-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining",
+ "ContinuousCallSiteTargetChange$Test", "100");
+
+ OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
+
+ analyzer.shouldHaveExitValue(0);
+
+ analyzer.shouldNotContain("made not compilable");
+ analyzer.shouldNotContain("decompile_count > PerMethodRecompilationCutoff");
+ }
+
+ static void testClient() throws Exception {
+ ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
+ "-XX:+IgnoreUnrecognizedVMOptions",
+ "-client", "-XX:+TieredCompilation", "-XX:TieredStopAtLevel=1", "-Xbatch",
+ "-XX:PerBytecodeRecompilationCutoff=10", "-XX:PerMethodRecompilationCutoff=10",
+ "-XX:+PrintCompilation", "-XX:+UnlockDiagnosticVMOptions", "-XX:+PrintInlining",
+ "ContinuousCallSiteTargetChange$Test", "100");
+
+ OutputAnalyzer analyzer = new OutputAnalyzer(pb.start());
+
+ analyzer.shouldHaveExitValue(0);
+
+ analyzer.shouldNotContain("made not compilable");
+ analyzer.shouldNotContain("decompile_count > PerMethodRecompilationCutoff");
+ }
+
+ public static void main(String[] args) throws Exception {
+ testServer();
+ testClient();
+ }
+
+ static class Test {
+ static final MethodType mt = MethodType.methodType(void.class);
+ static final CallSite cs = new MutableCallSite(mt);
+
+ static final MethodHandle mh = cs.dynamicInvoker();
+
+ static void f() {
+ }
+
+ static void test1() throws Throwable {
+ mh.invokeExact();
+ }
+
+ static void test2() throws Throwable {
+ cs.getTarget().invokeExact();
+ }
+
+ static void iteration() throws Throwable {
+ MethodHandle mh1 = MethodHandles.lookup().findStatic(ContinuousCallSiteTargetChange.Test.class, "f", mt);
+ cs.setTarget(mh1);
+ for (int i = 0; i < 20_000; i++) {
+ test1();
+ test2();
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ int iterations = Integer.parseInt(args[0]);
+ for (int i = 0; i < iterations; i++) {
+ iteration();
+ }
+ }
+ }
+}