blob: 0f91fd6b013635adf83c1db97d19644cee2efb98 [file] [log] [blame]
/*
* Copyright (c) 2017, 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 8166188 8178813
* @summary Test return of JNI weak global refs during concurrent
* marking, verifying the use of the G1 load barrier to keep the
* referent alive.
* @key gc
* @requires vm.gc.G1
* @modules java.base
* @library /test/lib
* @build sun.hotspot.WhiteBox
* @run driver ClassFileInstaller sun.hotspot.WhiteBox
* sun.hotspot.WhiteBox$WhiteBoxPermission
* @run main/othervm/native
* -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:+UseG1GC -XX:MaxTenuringThreshold=2
* -Xint
* TestJNIWeakG1
* @run main/othervm/native
* -Xbootclasspath/a:.
* -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
* -XX:+UseG1GC -XX:MaxTenuringThreshold=2
* -Xcomp
* TestJNIWeakG1
*/
import sun.hotspot.WhiteBox;
import java.lang.ref.Reference;
public final class TestJNIWeakG1 {
static {
System.loadLibrary("TestJNIWeakG1");
}
private static final WhiteBox WB = WhiteBox.getWhiteBox();
private static final class TestObject {
public final int value;
public TestObject(int value) {
this.value = value;
}
}
private volatile TestObject testObject = null;
private static native void registerObject(Object o);
private static native void unregisterObject();
private static native Object getReturnedWeak();
private static native Object getResolvedWeak();
// resolve controls whether getObject returns an explicitly
// resolved jweak value (when resolve is true), or returns the
// jweak directly and invokes the implicit resolution in the
// native call return value handling path (when resolve is false).
private boolean resolve = true;
TestJNIWeakG1(boolean resolve) {
this.resolve = resolve;
}
private Object getObject() {
if (resolve) {
return getResolvedWeak();
} else {
return getReturnedWeak();
}
}
// Create the test object and record it both strongly and weakly.
private void remember(int value) {
TestObject o = new TestObject(value);
registerObject(o);
testObject = o;
}
// Remove both strong and weak references to the current test object.
private void forget() {
unregisterObject();
testObject = null;
}
// Repeatedly perform young-only GC until o is in the old generation.
private void gcUntilOld(Object o) {
while (!WB.isObjectInOldGen(o)) {
WB.youngGC();
}
}
// Verify the weakly recorded object
private void checkValue(int value) throws Exception {
Object o = getObject();
if (o == null) {
throw new RuntimeException("Weak reference unexpectedly null");
}
TestObject t = (TestObject)o;
if (t.value != value) {
throw new RuntimeException("Incorrect value");
}
}
// Verify we can create a weak reference and get it back.
private void checkSanity() throws Exception {
System.out.println("running checkSanity");
try {
// Inhibit concurrent GC during this check.
WB.requestConcurrentGCPhase("IDLE");
int value = 5;
try {
remember(value);
checkValue(value);
} finally {
forget();
}
} finally {
// Remove request.
WB.requestConcurrentGCPhase("ANY");
}
}
// Verify weak ref value survives across collection if strong ref exists.
private void checkSurvival() throws Exception {
System.out.println("running checkSurvival");
try {
int value = 10;
try {
remember(value);
checkValue(value);
gcUntilOld(testObject);
// Run a concurrent collection after object is old.
WB.requestConcurrentGCPhase("CONCURRENT_CYCLE");
WB.requestConcurrentGCPhase("IDLE");
// Verify weak ref still has expected value.
checkValue(value);
} finally {
forget();
}
} finally {
// Remove request.
WB.requestConcurrentGCPhase("ANY");
}
}
// Verify weak ref cleared if no strong ref exists.
private void checkClear() throws Exception {
System.out.println("running checkClear");
try {
int value = 15;
try {
remember(value);
checkValue(value);
gcUntilOld(testObject);
// Run a concurrent collection after object is old.
WB.requestConcurrentGCPhase("CONCURRENT_CYCLE");
WB.requestConcurrentGCPhase("IDLE");
checkValue(value);
testObject = null;
// Run a concurrent collection after strong ref removed.
WB.requestConcurrentGCPhase("CONCURRENT_CYCLE");
WB.requestConcurrentGCPhase("IDLE");
// Verify weak ref cleared as expected.
Object recorded = getObject();
if (recorded != null) {
throw new RuntimeException("expected clear");
}
} finally {
forget();
}
} finally {
// Remove request.
WB.requestConcurrentGCPhase("ANY");
}
}
// Verify weak ref not cleared if no strong ref at start of
// collection but weak ref read during marking.
private void checkShouldNotClear() throws Exception {
System.out.println("running checkShouldNotClear");
try {
int value = 20;
try {
remember(value);
checkValue(value);
gcUntilOld(testObject);
// Block concurrent cycle until we're ready.
WB.requestConcurrentGCPhase("IDLE");
checkValue(value);
testObject = null; // Discard strong ref
// Run through mark_from_roots.
WB.requestConcurrentGCPhase("BEFORE_REMARK");
// Fetch weak ref'ed object. Should be kept alive now.
Object recovered = getObject();
if (recovered == null) {
throw new RuntimeException("unexpected clear during mark");
}
// Finish collection, including reference processing.
// Block any further cycles while we finish check.
WB.requestConcurrentGCPhase("IDLE");
// Fetch weak ref'ed object. Referent is manifestly
// live in recovered; the earlier fetch should have
// kept it alive through collection, so weak ref
// should not have been cleared.
if (getObject() == null) {
// 8166188 problem results in not doing the
// keep-alive of earlier getObject result, so
// recovered is now reachable but not marked.
// We may eventually crash.
throw new RuntimeException("cleared jweak for live object");
}
Reference.reachabilityFence(recovered);
} finally {
forget();
}
} finally {
// Remove request.
WB.requestConcurrentGCPhase("ANY");
}
}
private void check() throws Exception {
checkSanity();
checkSurvival();
checkClear();
checkShouldNotClear();
System.out.println("Check passed");
}
public static void main(String[] args) throws Exception {
// Perform check with direct jweak resolution.
System.out.println("Check with jweak resolved");
new TestJNIWeakG1(true).check();
// Perform check with implicit jweak resolution by native
// call's return value handling.
System.out.println("Check with jweak returned");
new TestJNIWeakG1(false).check();
}
}