8220602: Shenandoah-SA: Enable best-effort implementation of heap walk

Reviewed-by: rkennke, cjplummer
This commit is contained in:
Zhengyu Gu 2019-03-14 09:53:15 -04:00
parent 5730a0413b
commit 6881a91868
10 changed files with 355 additions and 23 deletions

View File

@ -436,6 +436,7 @@ ShenandoahHeap::ShenandoahHeap(ShenandoahCollectorPolicy* policy) :
_cycle_memory_manager("Shenandoah Cycles", "end of GC cycle"),
_gc_timer(new (ResourceObj::C_HEAP, mtGC) ConcurrentGCTimer()),
_soft_ref_policy(),
_log_min_obj_alignment_in_bytes(LogMinObjAlignmentInBytes),
_ref_processor(NULL),
_marking_context(NULL),
_bitmap_size(0),

View File

@ -505,6 +505,8 @@ private:
ConcurrentGCTimer* _gc_timer;
SoftRefPolicy _soft_ref_policy;
// For exporting to SA
int _log_min_obj_alignment_in_bytes;
public:
ShenandoahMonitoringSupport* monitoring_support() { return _monitoring_support; }
GCMemoryManager* cycle_memory_manager() { return &_cycle_memory_manager; }

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2018, Red Hat, Inc. All rights reserved.
* Copyright (c) 2018, 2019, Red Hat, Inc. All rights reserved.
*
* 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
@ -27,13 +27,28 @@
#include "gc/shenandoah/shenandoahHeapRegion.hpp"
#include "gc/shenandoah/shenandoahMonitoringSupport.hpp"
#define VM_STRUCTS_SHENANDOAH(nonstatic_field, volatile_nonstatic_field, static_field) \
static_field(ShenandoahHeapRegion, RegionSizeBytes, size_t) \
nonstatic_field(ShenandoahHeap, _num_regions, size_t) \
volatile_nonstatic_field(ShenandoahHeap, _used, size_t) \
volatile_nonstatic_field(ShenandoahHeap, _committed, size_t) \
#define VM_STRUCTS_SHENANDOAH(nonstatic_field, volatile_nonstatic_field, static_field) \
nonstatic_field(ShenandoahHeap, _num_regions, size_t) \
nonstatic_field(ShenandoahHeap, _regions, ShenandoahHeapRegion**) \
nonstatic_field(ShenandoahHeap, _log_min_obj_alignment_in_bytes, int) \
volatile_nonstatic_field(ShenandoahHeap, _used, size_t) \
volatile_nonstatic_field(ShenandoahHeap, _committed, size_t) \
static_field(ShenandoahHeapRegion, RegionSizeBytes, size_t) \
static_field(ShenandoahHeapRegion, RegionSizeBytesShift, size_t) \
nonstatic_field(ShenandoahHeapRegion, _state, ShenandoahHeapRegion::RegionState) \
nonstatic_field(ShenandoahHeapRegion, _region_number, size_t) \
#define VM_INT_CONSTANTS_SHENANDOAH(declare_constant, declare_constant_with_value)
#define VM_INT_CONSTANTS_SHENANDOAH(declare_constant, declare_constant_with_value) \
declare_constant(ShenandoahHeapRegion::_empty_uncommitted) \
declare_constant(ShenandoahHeapRegion::_empty_committed) \
declare_constant(ShenandoahHeapRegion::_regular) \
declare_constant(ShenandoahHeapRegion::_humongous_start) \
declare_constant(ShenandoahHeapRegion::_humongous_cont) \
declare_constant(ShenandoahHeapRegion::_pinned_humongous_start) \
declare_constant(ShenandoahHeapRegion::_cset) \
declare_constant(ShenandoahHeapRegion::_pinned) \
declare_constant(ShenandoahHeapRegion::_pinned_cset) \
declare_constant(ShenandoahHeapRegion::_trash) \
#define VM_TYPES_SHENANDOAH(declare_type, \
declare_toplevel_type, \
@ -42,5 +57,6 @@
declare_type(ShenandoahHeapRegion, ContiguousSpace) \
declare_toplevel_type(ShenandoahHeap*) \
declare_toplevel_type(ShenandoahHeapRegion*) \
declare_toplevel_type(ShenandoahHeapRegion::RegionState) \
#endif // SHARE_GC_SHENANDOAH_VMSTRUCTS_SHENANDOAH_HPP

View File

@ -64,6 +64,8 @@ public abstract class CollectedHeap extends VMObject {
public abstract long capacity();
public abstract long used();
public long oopOffset() { return 0; }
public MemRegion reservedRegion() {
return new MemRegion(addr.addOffsetTo(reservedFieldOffset));
}

View File

@ -0,0 +1,94 @@
/*
* Copyright (c) 2019, Red Hat, Inc. All rights reserved.
*
* 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 sun.jvm.hotspot.gc.shenandoah;
import sun.jvm.hotspot.utilities.BitMap;
import sun.jvm.hotspot.utilities.BitMapInterface;
import java.util.HashMap;
public class ShenandoahBitMap implements BitMapInterface {
private HashMap<ShenandoahHeapRegion, BitMap> regionToBitMap = new HashMap<>();
private ShenandoahHeap heap;
ShenandoahBitMap(ShenandoahHeap heap) {
this.heap = heap;
}
@Override
public boolean at(long offset) {
ShenandoahHeapRegion region = heap.regionAtOffset(offset);
BitMap bitmap = regionToBitMap.get(region);
if (bitmap == null) {
return false;
} else {
int index = toBitMapOffset(offset, region);
return bitmap.at(index);
}
}
@Override
public void atPut(long offset, boolean value) {
ShenandoahHeapRegion region = heap.regionAtOffset(offset);
BitMap bitmap = getOrAddBitMap(region);
int index = toBitMapOffset(offset, region);
bitmap.atPut(index, value);
}
@Override
public void clear() {
for (BitMap bitMap : regionToBitMap.values()) {
bitMap.clear();
}
}
private int toBitMapOffset(long offset, ShenandoahHeapRegion region) {
long regionSize = ShenandoahHeapRegion.regionSizeBytes();
long regionOffset = region.regionNumber() * regionSize;
long offsetInRegion = offset - regionOffset;
if (offsetInRegion < 0 || offsetInRegion >= regionSize) {
throw new RuntimeException("Unexpected negative offset: " + offsetInRegion);
}
return (int)(offsetInRegion >>> heap.getLogMinObjAlignmentInBytes());
}
private BitMap getOrAddBitMap(ShenandoahHeapRegion region) {
BitMap bitMap = regionToBitMap.get(region);
if (bitMap == null) {
long regionSize = ShenandoahHeapRegion.regionSizeBytes();
long maxNumObjects = regionSize >>> heap.getLogMinObjAlignmentInBytes();
if (maxNumObjects > Integer.MAX_VALUE) {
throw new RuntimeException("int overflow");
}
int intMaxNumObjects = (int)maxNumObjects;
bitMap = new BitMap(intMaxNumObjects);
regionToBitMap.put(region, bitMap);
}
return bitMap;
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved.
* Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved.
*
* 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
@ -28,10 +28,14 @@ import sun.jvm.hotspot.gc.shared.CollectedHeapName;
import sun.jvm.hotspot.gc.shared.LiveRegionsClosure;
import sun.jvm.hotspot.debugger.Address;
import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.runtime.VMObjectFactory;
import sun.jvm.hotspot.types.AddressField;
import sun.jvm.hotspot.types.Type;
import sun.jvm.hotspot.types.TypeDataBase;
import sun.jvm.hotspot.memory.MemRegion;
import sun.jvm.hotspot.types.CIntegerField;
import sun.jvm.hotspot.utilities.BitMapInterface;
import java.io.PrintStream;
import java.util.Observable;
import java.util.Observer;
@ -40,6 +44,11 @@ public class ShenandoahHeap extends CollectedHeap {
static private CIntegerField numRegions;
static private CIntegerField used;
static private CIntegerField committed;
static private AddressField regions;
static private CIntegerField logMinObjAlignmentInBytes;
static private long regionPtrFieldSize;
static private long brookPtrSize;
static {
VM.registerVMInitializedObserver(new Observer() {
public void update(Observable o, Object data) {
@ -53,6 +62,21 @@ public class ShenandoahHeap extends CollectedHeap {
numRegions = type.getCIntegerField("_num_regions");
used = type.getCIntegerField("_used");
committed = type.getCIntegerField("_committed");
regions = type.getAddressField("_regions");
logMinObjAlignmentInBytes = type.getCIntegerField("_log_min_obj_alignment_in_bytes");
brookPtrSize = db.lookupIntConstant("HeapWordSize").longValue();
Type regionPtrType = db.lookupType("ShenandoahHeapRegion*");
regionPtrFieldSize = regionPtrType.getSize();
}
public ShenandoahHeap(Address addr) {
super(addr);
}
@Override
public long oopOffset() {
return brookPtrSize;
}
@Override
@ -78,10 +102,35 @@ public class ShenandoahHeap extends CollectedHeap {
return committed.getValue(addr);
}
public int getLogMinObjAlignmentInBytes() {
return logMinObjAlignmentInBytes.getJInt(addr);
}
public ShenandoahHeapRegion getRegion(long index) {
if (index < numOfRegions()) {
Address arrayAddr = regions.getValue(addr);
Address regAddr = arrayAddr.getAddressAt(index * regionPtrFieldSize);
ShenandoahHeapRegion region = VMObjectFactory.newObject(ShenandoahHeapRegion.class, regAddr);
region.setHeap(this);
return region;
}
return null;
}
public ShenandoahHeapRegion regionAtOffset(long offset) {
long index = offset >>> ShenandoahHeapRegion.regionSizeBytesShift();
if (index < 0 || index >= numOfRegions()) {
throw new RuntimeException("Invalid offset: " + offset);
}
return getRegion(index);
}
@Override
public void liveRegionsIterate(LiveRegionsClosure closure) {
// Operation (currently) not supported with Shenandoah GC.
System.err.println("Warning: Operation not supported with Shenandoah GC");
for (long index = 0; index < numOfRegions(); index ++) {
ShenandoahHeapRegion region = getRegion(index);
closure.doLiveRegions(region);
}
}
@Override
@ -92,7 +141,9 @@ public class ShenandoahHeap extends CollectedHeap {
tty.println(" region size " + ShenandoahHeapRegion.regionSizeBytes() / 1024 + " K");
}
public ShenandoahHeap(Address addr) {
super(addr);
@Override
public BitMapInterface createBitMap(long bits) {
return new ShenandoahBitMap(this);
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2017, 2018, Red Hat, Inc. All rights reserved.
* Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved.
*
* 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
@ -23,19 +23,43 @@
package sun.jvm.hotspot.gc.shenandoah;
import sun.jvm.hotspot.debugger.OopHandle;
import sun.jvm.hotspot.gc.shared.ContiguousSpace;
import sun.jvm.hotspot.types.CIntegerField;
import sun.jvm.hotspot.gc.shared.LiveRegionsProvider;
import sun.jvm.hotspot.memory.MemRegion;
import sun.jvm.hotspot.oops.Mark;
import sun.jvm.hotspot.oops.Oop;
import sun.jvm.hotspot.oops.UnknownOopException;
import sun.jvm.hotspot.types.*;
import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.types.Type;
import sun.jvm.hotspot.types.TypeDataBase;
import sun.jvm.hotspot.debugger.Address;
import sun.jvm.hotspot.utilities.AddressOps;
import java.util.ArrayList;
import java.util.List;
import java.util.Observable;
import java.util.Observer;
public class ShenandoahHeapRegion extends ContiguousSpace {
private static CIntegerField RegionSizeBytes;
public class ShenandoahHeapRegion extends ContiguousSpace implements LiveRegionsProvider {
private static int EmptyUncommitted;
private static int EmptyCommitted;
private static int Regular;
private static int HumongousStart;
private static int HumongousCont;
private static int PinnedHumongousStart;
private static int CSet;
private static int Pinned;
private static int PinnedCSet;
private static int Trash;
private static CIntegerField RegionSizeBytesField;
private static Field RegionStateField;
private static CIntegerField RegionNumberField;
private static CIntegerField RegionSizeBytesShiftField;
private ShenandoahHeap heap;
static {
VM.registerVMInitializedObserver(new Observer() {
public void update(Observable o, Object data) {
@ -46,12 +70,154 @@ public class ShenandoahHeapRegion extends ContiguousSpace {
static private synchronized void initialize(TypeDataBase db) {
Type type = db.lookupType("ShenandoahHeapRegion");
RegionSizeBytes = type.getCIntegerField("RegionSizeBytes");
RegionSizeBytesField = type.getCIntegerField("RegionSizeBytes");
RegionStateField = type.getField("_state");
RegionNumberField = type.getCIntegerField("_region_number");
RegionSizeBytesShiftField = type.getCIntegerField("RegionSizeBytesShift");
EmptyUncommitted = db.lookupIntConstant("ShenandoahHeapRegion::_empty_uncommitted").intValue();
EmptyCommitted = db.lookupIntConstant("ShenandoahHeapRegion::_empty_committed").intValue();
Regular = db.lookupIntConstant("ShenandoahHeapRegion::_regular").intValue();
HumongousStart = db.lookupIntConstant("ShenandoahHeapRegion::_humongous_start").intValue();
HumongousCont = db.lookupIntConstant("ShenandoahHeapRegion::_humongous_cont").intValue();
PinnedHumongousStart = db.lookupIntConstant("ShenandoahHeapRegion::_pinned_humongous_start").intValue();
CSet = db.lookupIntConstant("ShenandoahHeapRegion::_cset").intValue();
Pinned = db.lookupIntConstant("ShenandoahHeapRegion::_pinned").intValue();
PinnedCSet = db.lookupIntConstant("ShenandoahHeapRegion::_pinned_cset").intValue();
Trash = db.lookupIntConstant("ShenandoahHeapRegion::_trash").intValue();
}
public static long regionSizeBytes() { return RegionSizeBytes.getValue(); }
public static long regionSizeBytes() {
return RegionSizeBytesField.getValue();
}
public static int regionSizeBytesShift() {
return RegionSizeBytesShiftField.getJInt();
}
public ShenandoahHeapRegion(Address addr) {
super(addr);
}
public void setHeap(ShenandoahHeap heap) {
this.heap = heap;
}
@Override
public int hashCode() {
return (int)regionNumber();
}
@Override
public boolean equals(Object other) {
if (other instanceof ShenandoahHeapRegion) {
ShenandoahHeapRegion otherRegion = (ShenandoahHeapRegion)other;
return otherRegion.regionNumber() == regionNumber();
}
return false;
}
public List<MemRegion> getLiveRegions() {
List<MemRegion> res = new ArrayList<>();
int state = regionState();
if (state == EmptyUncommitted || state == EmptyCommitted || state == Trash) {
// No live data
} else if (state == HumongousCont) {
// Handled by HumongousStart
} else if (state == HumongousStart || state == PinnedHumongousStart) {
handleHumongousRegion(res);
} else if (state == Regular || state == Pinned) {
handleRegularRegion(res);
} else if (state == CSet || state == PinnedCSet) {
// CSet
handleCSetRegion(res);
} else {
throw new RuntimeException("Unknown region state: " + state);
}
return res;
}
/*
* Note: RegionState is an enum on JVM side. Seems that there is not
* a standard way to read enum value. We read it as an integer
* from the field's offset.
*/
private int regionState() {
long offset = RegionStateField.getOffset();
return addr.getJIntAt(offset);
}
private void handleHumongousRegion(List<MemRegion> res) {
long index = regionNumber();
Address topAddr = top();
ShenandoahHeapRegion region = heap.getRegion(++ index);
while (region.regionState() == HumongousCont) {
topAddr = region.top();
region = heap.getRegion(++ index);
}
res.add(new MemRegion(bottom(), topAddr));
}
private void handleRegularRegion(List<MemRegion> res) {
res.add(new MemRegion(bottom(), top()));
}
// Filter out forwarded objects, they should be counted in other regions
private void handleCSetRegion(List<MemRegion> res) {
Address end = top();
Address start = bottom();
Address regionStart = null;
Address regionEnd = null;
while (AddressOps.lessThan(start, end)) {
long size = getObjectSize(start);
if (hasForwardee(start)) {
// has to-space object, skip this one
if (regionEnd != null) {
MemRegion mr = new MemRegion(regionStart, regionEnd);
res.add(mr);
regionStart = null;
regionEnd = null;
}
} else {
if (regionStart == null) {
regionStart = start;
} else {
regionEnd = start.addOffsetTo(heap.oopOffset() + size);
}
}
start = start.addOffsetTo(heap.oopOffset() + size);
}
if (regionStart != null) {
MemRegion mr = new MemRegion(regionStart, top());
res.add(mr);
}
}
public long regionNumber() {
return RegionNumberField.getValue(addr);
}
private boolean hasForwardee(Address rawPtr) {
// Use Mark as a helper to read forward pointer value.
Mark mark = new Mark(rawPtr);
Address forwardee = mark.valueAsAddress();
return (forwardee != rawPtr.addOffsetTo(heap.oopOffset()));
}
private long getObjectSize(Address rawPtr) {
// Dealing with a raw pointer, offsets forward pointer to find real Oop.
OopHandle handle = rawPtr.addOffsetToAsOopHandle(heap.oopOffset());
Oop obj = null;
try {
// Best effort, may fail
obj = VM.getVM().getObjectHeap().newOop(handle);
} catch (UnknownOopException exp) {
throw new RuntimeException(" UnknownOopException " + exp);
}
return obj.getObjectSize();
}
}

View File

@ -255,7 +255,9 @@ public class ObjectHeap {
OopHandle handle = bottom.addOffsetToAsOopHandle(0);
while (handle.lessThan(top)) {
Oop obj = null;
Oop obj = null;
// Raw pointer walk
handle = handle.addOffsetToAsOopHandle(heap.oopOffset());
try {
obj = newOop(handle);

View File

@ -35,7 +35,6 @@ import jtreg.SkippedException;
* @bug 8191658
* @summary Test clhsdb jhisto command
* @requires vm.hasSA
* @requires vm.gc != "Shenandoah"
* @library /test/lib
* @run main/othervm ClhsdbJhisto
*/

View File

@ -47,7 +47,6 @@ import jdk.test.lib.Asserts;
* @library /test/lib
* @bug 8171084
* @requires vm.hasSAandCanAttach & (vm.bits == "64" & os.maxMemory > 8g)
* @requires vm.gc != "Shenandoah"
* @modules java.base/jdk.internal.misc
* jdk.hotspot.agent/sun.jvm.hotspot
* jdk.hotspot.agent/sun.jvm.hotspot.utilities