8273559: Shenandoah: Shenandoah should support multi-threaded heap dump

Reviewed-by: shade, rkennke, sgehwolf
This commit is contained in:
Zhengyu Gu 2021-09-15 13:10:13 +00:00
parent f531b5c796
commit 8132bfd23f
6 changed files with 196 additions and 45 deletions

@ -725,7 +725,7 @@ private:
ShenandoahVMWeakRoots<true /*concurrent*/> _vm_roots;
// Roots related to concurrent class unloading
ShenandoahClassLoaderDataRoots<true /* concurrent */, true /* single thread*/>
ShenandoahClassLoaderDataRoots<true /* concurrent */>
_cld_roots;
ShenandoahConcurrentNMethodIterator _nmethod_itr;
ShenandoahPhaseTimings::Phase _phase;
@ -734,7 +734,7 @@ public:
ShenandoahConcurrentWeakRootsEvacUpdateTask(ShenandoahPhaseTimings::Phase phase) :
AbstractGangTask("Shenandoah Evacuate/Update Concurrent Weak Roots"),
_vm_roots(phase),
_cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers()),
_cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers(), false /*heap iteration*/),
_nmethod_itr(ShenandoahCodeRoots::table()),
_phase(phase) {
if (ShenandoahHeap::heap()->unload_classes()) {
@ -838,7 +838,8 @@ class ShenandoahConcurrentRootsEvacUpdateTask : public AbstractGangTask {
private:
ShenandoahPhaseTimings::Phase _phase;
ShenandoahVMRoots<true /*concurrent*/> _vm_roots;
ShenandoahClassLoaderDataRoots<true /*concurrent*/, false /*single threaded*/> _cld_roots;
ShenandoahClassLoaderDataRoots<true /*concurrent*/>
_cld_roots;
ShenandoahConcurrentNMethodIterator _nmethod_itr;
public:
@ -846,7 +847,7 @@ public:
AbstractGangTask("Shenandoah Evacuate/Update Concurrent Strong Roots"),
_phase(phase),
_vm_roots(phase),
_cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers()),
_cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers(), false /*heap iteration*/),
_nmethod_itr(ShenandoahCodeRoots::table()) {
if (!ShenandoahHeap::heap()->unload_classes()) {
MutexLocker mu(CodeCache_lock, Mutex::_no_safepoint_check_flag);

@ -1317,7 +1317,8 @@ void ShenandoahHeap::scan_roots_for_iteration(ShenandoahScanObjectStack* oop_sta
// This populates the work stack with initial objects
// It is important to relinquish the associated locks before diving
// into heap dumper
ShenandoahHeapIterationRootScanner rp;
uint n_workers = safepoint_workers() != NULL ? safepoint_workers()->active_workers() : 1;
ShenandoahHeapIterationRootScanner rp(n_workers);
rp.roots_do(oops);
}

@ -124,7 +124,7 @@ ShenandoahSTWRootScanner::ShenandoahSTWRootScanner(ShenandoahPhaseTimings::Phase
ShenandoahRootProcessor(phase),
_thread_roots(phase, ShenandoahHeap::heap()->workers()->active_workers() > 1),
_code_roots(phase),
_cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers()),
_cld_roots(phase, ShenandoahHeap::heap()->workers()->active_workers(), false /*heap iteration*/),
_vm_roots(phase),
_unload_classes(ShenandoahHeap::heap()->unload_classes()) {
}
@ -154,7 +154,7 @@ ShenandoahConcurrentRootScanner::ShenandoahConcurrentRootScanner(uint n_workers,
ShenandoahRootProcessor(phase),
_java_threads(phase, n_workers),
_vm_roots(phase),
_cld_roots(phase, n_workers),
_cld_roots(phase, n_workers, false /*heap iteration*/),
_codecache_snapshot(NULL),
_phase(phase) {
if (!ShenandoahHeap::heap()->unload_classes()) {
@ -213,7 +213,7 @@ void ShenandoahConcurrentRootScanner::update_tlab_stats() {
ShenandoahRootUpdater::ShenandoahRootUpdater(uint n_workers, ShenandoahPhaseTimings::Phase phase) :
ShenandoahRootProcessor(phase),
_vm_roots(phase),
_cld_roots(phase, n_workers),
_cld_roots(phase, n_workers, false /*heap iteration*/),
_thread_roots(phase, n_workers > 1),
_weak_roots(phase),
_code_roots(phase) {
@ -222,7 +222,7 @@ ShenandoahRootUpdater::ShenandoahRootUpdater(uint n_workers, ShenandoahPhaseTimi
ShenandoahRootAdjuster::ShenandoahRootAdjuster(uint n_workers, ShenandoahPhaseTimings::Phase phase) :
ShenandoahRootProcessor(phase),
_vm_roots(phase),
_cld_roots(phase, n_workers),
_cld_roots(phase, n_workers, false /*heap iteration*/),
_thread_roots(phase, n_workers > 1),
_weak_roots(phase),
_code_roots(phase) {
@ -248,18 +248,18 @@ void ShenandoahRootAdjuster::roots_do(uint worker_id, OopClosure* oops) {
_thread_roots.oops_do(oops, NULL, worker_id);
}
ShenandoahHeapIterationRootScanner::ShenandoahHeapIterationRootScanner() :
ShenandoahHeapIterationRootScanner::ShenandoahHeapIterationRootScanner(uint n_workers) :
ShenandoahRootProcessor(ShenandoahPhaseTimings::heap_iteration_roots),
_thread_roots(ShenandoahPhaseTimings::heap_iteration_roots, false /*is par*/),
_vm_roots(ShenandoahPhaseTimings::heap_iteration_roots),
_cld_roots(ShenandoahPhaseTimings::heap_iteration_roots, 1),
_cld_roots(ShenandoahPhaseTimings::heap_iteration_roots, n_workers, true /*heap iteration*/),
_weak_roots(ShenandoahPhaseTimings::heap_iteration_roots),
_code_roots(ShenandoahPhaseTimings::heap_iteration_roots) {
}
void ShenandoahHeapIterationRootScanner::roots_do(OopClosure* oops) {
// Must use _claim_none to avoid interfering with concurrent CLDG iteration
CLDToOopClosure clds(oops, ClassLoaderData::_claim_none);
// Must use _claim_other to avoid interfering with concurrent CLDG iteration
CLDToOopClosure clds(oops, ClassLoaderData::_claim_other);
MarkingCodeBlobClosure code(oops, !CodeBlobToOopClosure::FixRelocations);
ShenandoahParallelOopsDoThreadClosure tc_cl(oops, &code, NULL);
AlwaysTrueClosure always_true;

@ -107,15 +107,13 @@ public:
void code_blobs_do(CodeBlobClosure* blob_cl, uint worker_id);
};
template <bool CONCURRENT, bool SINGLE_THREADED>
template <bool CONCURRENT>
class ShenandoahClassLoaderDataRoots {
private:
ShenandoahSharedSemaphore _semaphore;
ShenandoahPhaseTimings::Phase _phase;
static uint worker_count(uint n_workers) {
if (SINGLE_THREADED) return 1u;
// Limit concurrency a bit, otherwise it wastes resources when workers are tripping
// over each other. This also leaves free workers to process other parts of the root
// set, while admitted workers are busy with doing the CLDG walk.
@ -123,7 +121,7 @@ private:
}
public:
ShenandoahClassLoaderDataRoots(ShenandoahPhaseTimings::Phase phase, uint n_workers);
ShenandoahClassLoaderDataRoots(ShenandoahPhaseTimings::Phase phase, uint n_workers, bool heap_iteration);
~ShenandoahClassLoaderDataRoots();
void always_strong_cld_do(CLDClosure* clds, uint worker_id);
@ -164,7 +162,7 @@ class ShenandoahSTWRootScanner : public ShenandoahRootProcessor {
private:
ShenandoahThreadRoots _thread_roots;
ShenandoahCodeCacheRoots _code_roots;
ShenandoahClassLoaderDataRoots<false /*concurrent*/, false /* single_thread*/>
ShenandoahClassLoaderDataRoots<false /*concurrent*/>
_cld_roots;
ShenandoahVMRoots<false /*concurrent*/>
_vm_roots;
@ -180,7 +178,7 @@ class ShenandoahConcurrentRootScanner : public ShenandoahRootProcessor {
private:
ShenandoahJavaThreadsIterator _java_threads;
ShenandoahVMRoots<true /*concurrent*/> _vm_roots;
ShenandoahClassLoaderDataRoots<true /*concurrent*/, false /* single-threaded*/>
ShenandoahClassLoaderDataRoots<true /*concurrent*/>
_cld_roots;
ShenandoahNMethodTableSnapshot* _codecache_snapshot;
ShenandoahPhaseTimings::Phase _phase;
@ -201,13 +199,12 @@ class ShenandoahHeapIterationRootScanner : public ShenandoahRootProcessor {
private:
ShenandoahThreadRoots _thread_roots;
ShenandoahVMRoots<false /*concurrent*/> _vm_roots;
ShenandoahClassLoaderDataRoots<false /*concurrent*/, true /*single threaded*/>
_cld_roots;
ShenandoahClassLoaderDataRoots<false /*concurrent*/> _cld_roots;
ShenandoahVMWeakRoots<false /*concurrent*/> _weak_roots;
ShenandoahCodeCacheRoots _code_roots;
public:
ShenandoahHeapIterationRootScanner();
ShenandoahHeapIterationRootScanner(uint n_workers);
void roots_do(OopClosure* cl);
};
@ -216,8 +213,7 @@ public:
class ShenandoahRootUpdater : public ShenandoahRootProcessor {
private:
ShenandoahVMRoots<false /*concurrent*/> _vm_roots;
ShenandoahClassLoaderDataRoots<false /*concurrent*/, false /*single threaded*/>
_cld_roots;
ShenandoahClassLoaderDataRoots<false /*concurrent*/> _cld_roots;
ShenandoahThreadRoots _thread_roots;
ShenandoahVMWeakRoots<false /*concurrent*/> _weak_roots;
ShenandoahCodeCacheRoots _code_roots;
@ -233,8 +229,7 @@ public:
class ShenandoahRootAdjuster : public ShenandoahRootProcessor {
private:
ShenandoahVMRoots<false /*concurrent*/> _vm_roots;
ShenandoahClassLoaderDataRoots<false /*concurrent*/, false /*single threaded*/>
_cld_roots;
ShenandoahClassLoaderDataRoots<false /*concurrent*/> _cld_roots;
ShenandoahThreadRoots _thread_roots;
ShenandoahVMWeakRoots<false /*concurrent*/> _weak_roots;
ShenandoahCodeCacheRoots _code_roots;

@ -75,14 +75,17 @@ void ShenandoahVMRoots<CONCURRENT>::oops_do(T* cl, uint worker_id) {
_strong_roots.oops_do(cl);
}
template <bool CONCURRENT, bool SINGLE_THREADED>
ShenandoahClassLoaderDataRoots<CONCURRENT, SINGLE_THREADED>::ShenandoahClassLoaderDataRoots(ShenandoahPhaseTimings::Phase phase, uint n_workers) :
template <bool CONCURRENT>
ShenandoahClassLoaderDataRoots<CONCURRENT>::ShenandoahClassLoaderDataRoots(ShenandoahPhaseTimings::Phase phase, uint n_workers, bool heap_iteration) :
_semaphore(worker_count(n_workers)),
_phase(phase) {
if (!SINGLE_THREADED) {
if (heap_iteration) {
ClassLoaderDataGraph::clear_claimed_marks(ClassLoaderData::_claim_other);
} else {
ClassLoaderDataGraph::clear_claimed_marks();
}
if (CONCURRENT && !SINGLE_THREADED) {
if (CONCURRENT) {
ClassLoaderDataGraph_lock->lock();
}
@ -90,24 +93,19 @@ ShenandoahClassLoaderDataRoots<CONCURRENT, SINGLE_THREADED>::ShenandoahClassLoad
assert(CONCURRENT || SafepointSynchronize::is_at_safepoint(), "Must be at a safepoint");
}
template <bool CONCURRENT, bool SINGLE_THREADED>
ShenandoahClassLoaderDataRoots<CONCURRENT, SINGLE_THREADED>::~ShenandoahClassLoaderDataRoots() {
if (CONCURRENT && !SINGLE_THREADED) {
template <bool CONCURRENT>
ShenandoahClassLoaderDataRoots<CONCURRENT>::~ShenandoahClassLoaderDataRoots() {
if (CONCURRENT) {
ClassLoaderDataGraph_lock->unlock();
}
}
template <bool CONCURRENT, bool SINGLE_THREADED>
void ShenandoahClassLoaderDataRoots<CONCURRENT, SINGLE_THREADED>::cld_do_impl(CldDo f, CLDClosure* clds, uint worker_id) {
template <bool CONCURRENT>
void ShenandoahClassLoaderDataRoots<CONCURRENT>::cld_do_impl(CldDo f, CLDClosure* clds, uint worker_id) {
if (CONCURRENT) {
if (_semaphore.try_acquire()) {
ShenandoahWorkerTimingsTracker timer(_phase, ShenandoahPhaseTimings::CLDGRoots, worker_id);
if (SINGLE_THREADED){
MutexLocker ml(ClassLoaderDataGraph_lock, Mutex::_no_safepoint_check_flag);
f(clds);
} else {
f(clds);
}
f(clds);
_semaphore.claim_all();
}
} else {
@ -116,13 +114,13 @@ void ShenandoahClassLoaderDataRoots<CONCURRENT, SINGLE_THREADED>::cld_do_impl(Cl
}
}
template <bool CONCURRENT, bool SINGLE_THREADED>
void ShenandoahClassLoaderDataRoots<CONCURRENT, SINGLE_THREADED>::always_strong_cld_do(CLDClosure* clds, uint worker_id) {
template <bool CONCURRENT>
void ShenandoahClassLoaderDataRoots<CONCURRENT>::always_strong_cld_do(CLDClosure* clds, uint worker_id) {
cld_do_impl(&ClassLoaderDataGraph::always_strong_cld_do, clds, worker_id);
}
template <bool CONCURRENT, bool SINGLE_THREADED>
void ShenandoahClassLoaderDataRoots<CONCURRENT, SINGLE_THREADED>::cld_do(CLDClosure* clds, uint worker_id) {
template <bool CONCURRENT>
void ShenandoahClassLoaderDataRoots<CONCURRENT>::cld_do(CLDClosure* clds, uint worker_id) {
cld_do_impl(&ClassLoaderDataGraph::cld_do, clds, worker_id);
}

@ -0,0 +1,156 @@
/*
* Copyright (c) 2021, Red Hat, Inc. 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 id=passive
* @library /test/lib
* @modules jdk.attach/com.sun.tools.attach
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCMode=passive
* -XX:+ShenandoahDegeneratedGC
* TestJcmdHeapDump
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCMode=passive
* -XX:-ShenandoahDegeneratedGC
* TestJcmdHeapDump
*/
/*
* @test id=aggressive
* @library /test/lib
* @modules jdk.attach/com.sun.tools.attach
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:+ShenandoahOOMDuringEvacALot
* TestJcmdHeapDump
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* -XX:+ShenandoahAllocFailureALot
* TestJcmdHeapDump
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=aggressive
* TestJcmdHeapDump
*/
/*
* @test id=adaptive
* @library /test/lib
* @modules jdk.attach/com.sun.tools.attach
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=adaptive
* -Dtarget=10000
* TestJcmdHeapDump
*/
/*
* @test id=static
* @library /test/lib
* @modules jdk.attach/com.sun.tools.attach
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=static
* TestJcmdHeapDump
*/
/*
* @test id=compact
* @library /test/lib
* @modules jdk.attach/com.sun.tools.attach
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCHeuristics=compact
* TestJcmdHeapDump
*/
/*
* @test id=iu-aggressive
* @library /test/lib
* @modules jdk.attach/com.sun.tools.attach
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive
* -XX:+ShenandoahOOMDuringEvacALot
* TestJcmdHeapDump
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive
* -XX:+ShenandoahAllocFailureALot
* TestJcmdHeapDump
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu -XX:ShenandoahGCHeuristics=aggressive
* TestJcmdHeapDump
*/
/*
* @test id=iu
* @requires vm.gc.Shenandoah
* @library /test/lib
* @modules jdk.attach/com.sun.tools.attach
*
* @run main/othervm/timeout=480 -Xmx16m -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions
* -XX:+UseShenandoahGC -XX:ShenandoahGCMode=iu
* TestJcmdHeapDump
*/
import jdk.test.lib.JDKToolLauncher;
import jdk.test.lib.process.OutputAnalyzer;
import java.io.File;
public class TestJcmdHeapDump {
public static void main(String[] args) {
long pid = ProcessHandle.current().pid();
JDKToolLauncher jcmd = JDKToolLauncher.createUsingTestJDK("jcmd");
jcmd.addToolArg(String.valueOf(pid));
jcmd.addToolArg("GC.heap_dump");
String dumpFileName = "myheapdump" + String.valueOf(pid);
jcmd.addToolArg(dumpFileName);
try {
ProcessBuilder pb = new ProcessBuilder(jcmd.getCommand());
Process jcmdProc = pb.start();
OutputAnalyzer output = new OutputAnalyzer(jcmdProc);
jcmdProc.waitFor();
output.shouldHaveExitValue(0);
} catch (Exception e) {
throw new RuntimeException("Test failed: " + e);
}
File f = new File(dumpFileName);
if (f.exists()) {
f.delete();
} else {
throw new RuntimeException("Dump file not created");
}
}
}