publicclass Main { publicstaticvoid main(String[] args) throws Exception {
System.loadLibrary(args[0]); if (isAotCompiled(Main.class, "hasJit")) { thrownew Error("This test must be run with --no-prebuild!");
} if (!hasJit()) { return;
}
// Deoptimize callThrough() to ensure it can be JITed afterwards.
deoptimizeNativeMethod(Main.class, "callThrough");
testCompilationUseAndCollection();
testMixedFramesOnStack();
}
publicstaticvoid testCompilationUseAndCollection() { // Test that callThrough() can be JIT-compiled.
assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough"));
assertFalse(hasJitCompiledCode(Main.class, "callThrough"));
ensureCompiledCallThroughEntrypoint(/* call */ true);
assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough"));
assertTrue(hasJitCompiledCode(Main.class, "callThrough"));
// Use callThrough() once again now that the method has a JIT-compiled stub.
callThrough(Main.class, "doNothing");
// Test that GC with the JIT-compiled stub on the stack does not collect it. // Also tests stack walk over the JIT-compiled stub.
callThrough(Main.class, "testGcWithCallThroughStubOnStack");
// Test that the JNI compiled stub can actually be collected.
testStubCanBeCollected();
}
publicstaticvoid testGcWithCallThroughStubOnStack() { // Check that this method was called via JIT-compiled callThrough() stub.
assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); // This assertion also exercises stack walk over the JIT-compiled callThrough() stub.
assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough"));
doJitGcsUntilFullJitGcIsScheduled(); // The callThrough() on the stack above this method is using the compiled stub, // so the JIT GC should not remove the compiled code.
jitGc();
assertTrue(hasJitCompiledCode(Main.class, "callThrough"));
}
publicstaticvoid testMixedFramesOnStack() { // Starts without a compiled JNI stub for callThrough().
assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough"));
assertFalse(hasJitCompiledCode(Main.class, "callThrough"));
callThrough(Main.class, "testMixedFramesOnStackStage2"); // Though the callThrough() is on the stack, that frame is using the GenericJNI // and does not prevent the collection of the JNI stub.
testStubCanBeCollected();
}
publicstaticvoid testMixedFramesOnStackStage2() { // We cannot assert that callThrough() has no JIT compiled stub as that check // may race against the compilation task. Just check the caller.
assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); // Now ensure that the JNI stub is compiled and used.
ensureCompiledCallThroughEntrypoint(/* call */ true);
callThrough(Main.class, "testMixedFramesOnStackStage3");
}
publicstaticvoid testMixedFramesOnStackStage3() { // Check that this method was called via JIT-compiled callThrough() stub.
assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough")); // This assertion also exercises stack walk over the JIT-compiled callThrough() stub.
assertTrue(new Throwable().getStackTrace()[1].getMethodName().equals("callThrough")); // For a good measure, try a JIT GC.
jitGc();
}
publicstaticvoid testStubCanBeCollected() {
jitGc(); // JIT GC without callThrough() on the stack should collect the callThrough() stub.
assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough"));
assertFalse(hasJitCompiledCode(Main.class, "callThrough"));
}
publicstaticvoid doJitGcsUntilFullJitGcIsScheduled() { // We enter with a compiled stub for callThrough() but we also need the entrypoint to be set.
assertTrue(hasJitCompiledCode(Main.class, "callThrough"));
ensureCompiledCallThroughEntrypoint(/* call */ true); // Perform JIT GC until the next GC is marked to do full collection. do {
assertTrue(hasJitCompiledEntrypoint(Main.class, "callThrough"));
callThrough(Main.class, "jitGc"); // JIT GC with callThrough() safely on the stack.
} while (!isNextJitGcFull()); // The JIT GC before the full collection resets entrypoints and waits to see // if the methods are still in use.
assertFalse(hasJitCompiledEntrypoint(Main.class, "callThrough"));
assertTrue(hasJitCompiledCode(Main.class, "callThrough"));
}
publicstaticvoid ensureCompiledCallThroughEntrypoint(boolean call) { int count = 0; while (!hasJitCompiledEntrypoint(Main.class, "callThrough")) { // If `call` is true, also exercise the `callThrough()` method to increase hotness. // Ramp-up the number of calls we do up to 1 << 12. finalint rampUpCutOff = 12; int limit = call ? 1 << Math.min(count, rampUpCutOff) : 0; for (int i = 0; i < limit; ++i) {
callThrough(Main.class, "doNothing");
} try { // Sleep to give a chance for the JIT to compile `callThrough` stub. // After the ramp-up phase, give the JIT even more time to compile. Thread.sleep(count >= rampUpCutOff ? 200 : 100);
} catch (Exception e) { // Ignore
} if (++count == 50) { thrownew Error("TIMEOUT");
}
}
}
// Note that the callThrough()'s shorty differs from shorties of the other native methods used // in this test (except deoptimizeNativeMethod) because of the return type `void.` publicnativestaticvoid callThrough(Class<?> cls, String methodName); publicnativestaticvoid deoptimizeNativeMethod(Class<?> cls, String methodName);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.