8067247: Crash: assert(method_holder->data() == 0 ...) failed: a) MT-unsafe modification of inline cache
Summary: Made invoker LambdaForm instance & its compiled form lifetime tightly coupled.
Reviewed-by: vlivanov
--- a/hotspot/src/share/vm/code/codeCache.cpp Sun Apr 03 21:44:54 2016 -0700
+++ b/hotspot/src/share/vm/code/codeCache.cpp Tue Apr 05 01:46:40 2016 -0700
@@ -1042,6 +1042,14 @@
}
}
+void CodeCache::cleanup_inline_caches() {
+ assert_locked_or_safepoint(CodeCache_lock);
+ NMethodIterator iter;
+ while(iter.next_alive()) {
+ iter.method()->cleanup_inline_caches(/*clean_all=*/true);
+ }
+}
+
// Keeps track of time spent for checking dependencies
NOT_PRODUCT(static elapsedTimer dependentCheckTime;)
--- a/hotspot/src/share/vm/code/codeCache.hpp Sun Apr 03 21:44:54 2016 -0700
+++ b/hotspot/src/share/vm/code/codeCache.hpp Tue Apr 05 01:46:40 2016 -0700
@@ -201,6 +201,7 @@
static bool needs_cache_clean() { return _needs_cache_clean; }
static void set_needs_cache_clean(bool v) { _needs_cache_clean = v; }
static void clear_inline_caches(); // clear all inline caches
+ static void cleanup_inline_caches();
// Returns true if an own CodeHeap for the given CodeBlobType is available
static bool heap_available(int code_blob_type);
--- a/hotspot/src/share/vm/code/nmethod.cpp Sun Apr 03 21:44:54 2016 -0700
+++ b/hotspot/src/share/vm/code/nmethod.cpp Tue Apr 05 01:46:40 2016 -0700
@@ -1138,8 +1138,7 @@
}
}
-
-void nmethod::cleanup_inline_caches() {
+void nmethod::cleanup_inline_caches(bool clean_all/*=false*/) {
assert_locked_or_safepoint(CompiledIC_lock);
// If the method is not entrant or zombie then a JMP is plastered over the
@@ -1169,7 +1168,7 @@
if( cb != NULL && cb->is_nmethod() ) {
nmethod* nm = (nmethod*)cb;
// Clean inline caches pointing to zombie, non-entrant and unloaded methods
- if (!nm->is_in_use() || (nm->method()->code() != nm)) ic->set_to_clean(is_alive());
+ if (clean_all || !nm->is_in_use() || (nm->method()->code() != nm)) ic->set_to_clean(is_alive());
}
break;
}
@@ -1179,7 +1178,7 @@
if( cb != NULL && cb->is_nmethod() ) {
nmethod* nm = (nmethod*)cb;
// Clean inline caches pointing to zombie, non-entrant and unloaded methods
- if (!nm->is_in_use() || (nm->method()->code() != nm)) csc->set_to_clean();
+ if (clean_all || !nm->is_in_use() || (nm->method()->code() != nm)) csc->set_to_clean();
}
break;
}
--- a/hotspot/src/share/vm/code/nmethod.hpp Sun Apr 03 21:44:54 2016 -0700
+++ b/hotspot/src/share/vm/code/nmethod.hpp Tue Apr 05 01:46:40 2016 -0700
@@ -599,7 +599,7 @@
// Inline cache support
void clear_inline_caches();
void clear_ic_stubs();
- void cleanup_inline_caches();
+ void cleanup_inline_caches(bool clean_all = false);
bool inlinecache_check_contains(address addr) const {
return (addr >= code_begin() && addr < verified_entry_point());
}
--- a/hotspot/src/share/vm/prims/whitebox.cpp Sun Apr 03 21:44:54 2016 -0700
+++ b/hotspot/src/share/vm/prims/whitebox.cpp Tue Apr 05 01:46:40 2016 -0700
@@ -1376,8 +1376,8 @@
return ConstantPool::encode_invokedynamic_index(index);
WB_END
-WB_ENTRY(void, WB_ClearInlineCaches(JNIEnv* env, jobject wb))
- VM_ClearICs clear_ics;
+WB_ENTRY(void, WB_ClearInlineCaches(JNIEnv* env, jobject wb, jboolean preserve_static_stubs))
+ VM_ClearICs clear_ics(preserve_static_stubs == JNI_TRUE);
VMThread::execute(&clear_ics);
WB_END
@@ -1757,7 +1757,7 @@
{CC"isShared", CC"(Ljava/lang/Object;)Z", (void*)&WB_IsShared },
{CC"isSharedClass", CC"(Ljava/lang/Class;)Z", (void*)&WB_IsSharedClass },
{CC"areSharedStringsIgnored", CC"()Z", (void*)&WB_AreSharedStringsIgnored },
- {CC"clearInlineCaches", CC"()V", (void*)&WB_ClearInlineCaches },
+ {CC"clearInlineCaches0", CC"(Z)V", (void*)&WB_ClearInlineCaches },
{CC"addCompilerDirective", CC"(Ljava/lang/String;)I",
(void*)&WB_AddCompilerDirective },
{CC"removeCompilerDirective", CC"(I)V", (void*)&WB_RemoveCompilerDirective },
--- a/hotspot/src/share/vm/runtime/vm_operations.cpp Sun Apr 03 21:44:54 2016 -0700
+++ b/hotspot/src/share/vm/runtime/vm_operations.cpp Tue Apr 05 01:46:40 2016 -0700
@@ -105,6 +105,14 @@
}
}
+void VM_ClearICs::doit() {
+ if (_preserve_static_stubs) {
+ CodeCache::cleanup_inline_caches();
+ } else {
+ CodeCache::clear_inline_caches();
+ }
+}
+
void VM_Deoptimize::doit() {
// We do not want any GCs to happen while we are in the middle of this VM operation
ResourceMark rm;
--- a/hotspot/src/share/vm/runtime/vm_operations.hpp Sun Apr 03 21:44:54 2016 -0700
+++ b/hotspot/src/share/vm/runtime/vm_operations.hpp Tue Apr 05 01:46:40 2016 -0700
@@ -231,9 +231,11 @@
};
class VM_ClearICs: public VM_Operation {
+ private:
+ bool _preserve_static_stubs;
public:
- VM_ClearICs() {}
- void doit() { CodeCache::clear_inline_caches(); }
+ VM_ClearICs(bool preserve_static_stubs) { _preserve_static_stubs = preserve_static_stubs; }
+ void doit();
VMOp_Type type() const { return VMOp_ClearICs; }
};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/hotspot/test/compiler/jsr292/InvokerGC.java Tue Apr 05 01:46:40 2016 -0700
@@ -0,0 +1,75 @@
+/*
+ * 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
+ * @bug 8067247
+ * @library /test/lib /compiler/whitebox /
+ * @run main/bootclasspath -Xcomp -Xbatch
+ * -XX:CompileCommand=compileonly,InvokerGC::test
+ * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
+ * InvokerGC
+ */
+
+import java.lang.invoke.*;
+import sun.hotspot.WhiteBox;
+
+public class InvokerGC {
+ static final WhiteBox WB = WhiteBox.getWhiteBox();
+
+ static MethodHandle mh;
+ static {
+ try {
+ mh = MethodHandles.lookup().findStatic(InvokerGC.class, "dummy", MethodType.methodType(void.class));
+ } catch (Exception e) {
+ throw new Error(e);
+ }
+ }
+
+ static void dummy() {}
+
+ static void test() {
+ try {
+ mh.invoke();
+ } catch (Throwable e) {
+ throw new Error(e);
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ mh.invoke(); // Pre-generate an invoker for ()V signature
+
+ test(); // trigger method compilation
+ test();
+
+ WB.fullGC(); // WB.fullGC has always clear softref policy.
+
+ test();
+
+ WB.clearInlineCaches(true); // Preserve static stubs.
+
+ test(); // Trigger call site re-resolution. Invoker LambdaForm should stay the same.
+
+ System.out.println("TEST PASSED");
+ }
+}