--- old/src/hotspot/share/gc/g1/g1CollectedHeap.cpp 2019-02-27 10:23:31.100404212 +0100 +++ new/src/hotspot/share/gc/g1/g1CollectedHeap.cpp 2019-02-27 10:23:30.825395850 +0100 @@ -2113,15 +2113,21 @@ } void G1CollectedHeap::collect(GCCause::Cause cause) { + attempt_collect(cause, true); +} + +bool G1CollectedHeap::attempt_collect(GCCause::Cause cause, bool retry_on_vmop_failure) { assert_heap_not_locked(); - uint gc_count_before; - uint old_marking_count_before; - uint full_gc_count_before; - bool retry_gc; + bool vmop_succeeded; + bool should_retry_vmop; do { - retry_gc = false; + should_retry_vmop = false; + + uint gc_count_before; + uint old_marking_count_before; + uint full_gc_count_before; { MutexLocker ml(Heap_lock); @@ -2142,19 +2148,18 @@ true, /* should_initiate_conc_mark */ g1_policy()->max_pause_time_ms()); VMThread::execute(&op); - if (!op.pause_succeeded()) { + vmop_succeeded = op.pause_succeeded(); + if (!vmop_succeeded && retry_on_vmop_failure) { if (old_marking_count_before == _old_marking_cycles_started) { - retry_gc = op.should_retry_gc(); + should_retry_vmop = op.should_retry_gc(); } else { // A Full GC happened while we were trying to schedule the - // initial-mark GC. No point in starting a new cycle given + // concurrent cycle. No point in starting a new cycle given // that the whole heap was collected anyway. } - if (retry_gc) { - if (GCLocker::is_active_and_needs_gc()) { - GCLocker::stall_until_clear(); - } + if (should_retry_vmop && GCLocker::is_active_and_needs_gc()) { + GCLocker::stall_until_clear(); } } } else { @@ -2169,13 +2174,16 @@ false, /* should_initiate_conc_mark */ g1_policy()->max_pause_time_ms()); VMThread::execute(&op); + vmop_succeeded = op.pause_succeeded(); } else { // Schedule a Full GC. VM_G1CollectFull op(gc_count_before, full_gc_count_before, cause); VMThread::execute(&op); + vmop_succeeded = op.pause_succeeded(); } } - } while (retry_gc); + } while (should_retry_vmop); + return vmop_succeeded; } bool G1CollectedHeap::is_in(const void* p) const { --- old/src/hotspot/share/gc/g1/g1CollectedHeap.hpp 2019-02-27 10:23:32.300440700 +0100 +++ new/src/hotspot/share/gc/g1/g1CollectedHeap.hpp 2019-02-27 10:23:32.026432368 +0100 @@ -1061,6 +1061,11 @@ // "CollectedHeap" supports. virtual void collect(GCCause::Cause cause); + // Perform a collection of the heap with the given cause; if the VM operation + // fails to execute for any reason, retry only if retry_on_vmop_failure is set. + // Returns whether this collection attempt actually executed. + bool attempt_collect(GCCause::Cause cause, bool retry_on_vmop_failure); + // True iff an evacuation has failed in the most-recent collection. bool evacuation_failed() { return _evacuation_failed; } --- old/src/hotspot/share/gc/g1/g1VMOperations.cpp 2019-02-27 10:23:33.450475667 +0100 +++ new/src/hotspot/share/gc/g1/g1VMOperations.cpp 2019-02-27 10:23:33.179467427 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2001, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2001, 2019, 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 @@ -36,7 +36,7 @@ void VM_G1CollectFull::doit() { G1CollectedHeap* g1h = G1CollectedHeap::heap(); GCCauseSetter x(g1h, _gc_cause); - g1h->do_full_collection(false /* clear_all_soft_refs */); + _pause_succeeded = g1h->do_full_collection(true /* explicit_gc */, false /* clear_all_soft_refs */); } VM_G1CollectForAllocation::VM_G1CollectForAllocation(size_t word_size, --- old/src/hotspot/share/gc/g1/g1VMOperations.hpp 2019-02-27 10:23:34.585510178 +0100 +++ new/src/hotspot/share/gc/g1/g1VMOperations.hpp 2019-02-27 10:23:34.314501938 +0100 @@ -35,13 +35,17 @@ // - VM_G1CollectFull class VM_G1CollectFull : public VM_GC_Operation { + bool _pause_succeeded; + public: VM_G1CollectFull(uint gc_count_before, uint full_gc_count_before, GCCause::Cause cause) : - VM_GC_Operation(gc_count_before, cause, full_gc_count_before, true) { } + VM_GC_Operation(gc_count_before, cause, full_gc_count_before, true), + _pause_succeeded(false) { } virtual VMOp_Type type() const { return VMOp_G1CollectFull; } virtual void doit(); + bool pause_succeeded() { return _pause_succeeded; } }; class VM_G1CollectForAllocation : public VM_CollectForAllocation { --- old/src/hotspot/share/gc/g1/g1YoungRemSetSamplingThread.cpp 2019-02-27 10:23:35.720544689 +0100 +++ new/src/hotspot/share/gc/g1/g1YoungRemSetSamplingThread.cpp 2019-02-27 10:23:35.451536510 +0100 @@ -1,5 +1,5 @@ /* - * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2015, 2019, 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 @@ -89,7 +89,10 @@ if ((os::elapsedTime() - _last_periodic_gc_attempt_s) > (G1PeriodicGCInterval / 1000.0)) { log_debug(gc, periodic)("Checking for periodic GC."); if (should_start_periodic_gc()) { - Universe::heap()->collect(GCCause::_g1_periodic_collection); + if (!G1CollectedHeap::heap()->attempt_collect(GCCause::_g1_periodic_collection, + false /* retry_on_vmop_failure */)) { + log_debug(gc, periodic)("GC request denied. Skipping."); + } } _last_periodic_gc_attempt_s = os::elapsedTime(); } --- /dev/null 2019-02-27 09:15:15.863770421 +0100 +++ new/test/hotspot/jtreg/gc/g1/TestPeriodicCollectionJNI.java 2019-02-27 10:23:36.590571142 +0100 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2019, 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. + */ + +package gc.g1; + +/* @test + * @bug 8218880 + * @summary Test that issuing a periodic collection while the GC locker is + * held does not crash the VM. + * @key gc + * @requires vm.gc.G1 + * @modules java.base + * @run main/othervm/native + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UseG1GC -XX:G1PeriodicGCInterval=100 + * -XX:+G1PeriodicGCInvokesConcurrent + * -Xlog:gc,gc+periodic=debug + * gc.g1.TestPeriodicCollectionJNI + * @run main/othervm/native + * -Xbootclasspath/a:. + * -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI + * -XX:+UseG1GC -XX:G1PeriodicGCInterval=100 + * -XX:-G1PeriodicGCInvokesConcurrent + * -Xlog:gc,gc+periodic=debug + * gc.g1.TestPeriodicCollectionJNI + */ + +public class TestPeriodicCollectionJNI { + static { System.loadLibrary("TestPeriodicCollectionJNI"); } + + private static native boolean blockInNative(byte[] array); + private static native void unblock(); + + public static void block() { + if (!blockInNative(new byte[0])) { + throw new RuntimeException("failed to acquire lock to dummy object"); + } + } + + public static void main(String[] args) throws InterruptedException { + long timeout = 2000; + long startTime = System.currentTimeMillis(); + + // start CS locker thread + BlockInNative blocker = new BlockInNative(); + blocker.start(); + + try { + // check timeout to success deadlocking + while (System.currentTimeMillis() < startTime + timeout) { + System.out.println("Sleeping to let periodic GC trigger..."); + Thread.sleep(200); + } + } finally { + unblock(); + } + } +} + +class BlockInNative extends Thread { + + public void run() { + TestPeriodicCollectionJNI.block(); + } + + native void unlock(); +} + --- /dev/null 2019-02-27 09:15:15.863770421 +0100 +++ new/test/hotspot/jtreg/gc/g1/libTestPeriodicCollectionJNI.c 2019-02-27 10:23:37.753606505 +0100 @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2019, 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. + */ + +/* + * Native support for TestPeriodicCollectionJNI test. + */ + +#include "jni.h" + +static volatile int release_critical = 0; + +JNIEXPORT jboolean JNICALL +Java_gc_g1_TestPeriodicCollectionJNI_blockInNative(JNIEnv* env, jobject obj, jintArray dummy) { + void* native_array = (*env)->GetPrimitiveArrayCritical(env, dummy, 0); + + if (native_array == NULL) { + return JNI_FALSE; + } + + while (!release_critical) /* empty */; + + (*env)->ReleasePrimitiveArrayCritical(env, dummy, native_array, 0); + + return JNI_TRUE; +} + +JNIEXPORT void JNICALL Java_gc_g1_TestPeriodicCollectionJNI_unblock(JNIEnv *env, jobject obj) +{ + release_critical = 1; +} +