8220602: Shenandoah-SA: Enable best-effort implementation of heap walk
Reviewed-by: rkennke, cjplummer
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Tue Apr 02 16:36:00 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.cpp Thu Mar 14 09:53:15 2019 -0400
@@ -436,6 +436,7 @@
_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),
--- a/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp Tue Apr 02 16:36:00 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/shenandoahHeap.hpp Thu Mar 14 09:53:15 2019 -0400
@@ -505,6 +505,8 @@
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; }
--- a/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp Tue Apr 02 16:36:00 2019 -0400
+++ b/src/hotspot/share/gc/shenandoah/vmStructs_shenandoah.hpp Thu Mar 14 09:53:15 2019 -0400
@@ -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
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/CollectedHeap.java Tue Apr 02 16:36:00 2019 -0400
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shared/CollectedHeap.java Thu Mar 14 09:53:15 2019 -0400
@@ -64,6 +64,8 @@
public abstract long capacity();
public abstract long used();
+ public long oopOffset() { return 0; }
+
public MemRegion reservedRegion() {
return new MemRegion(addr.addOffsetTo(reservedFieldOffset));
}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahBitMap.java Thu Mar 14 09:53:15 2019 -0400
@@ -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;
+ }
+}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahHeap.java Tue Apr 02 16:36:00 2019 -0400
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahHeap.java Thu Mar 14 09:53:15 2019 -0400
@@ -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.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 @@
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 @@
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 @@
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 @@
tty.println(" region size " + ShenandoahHeapRegion.regionSizeBytes() / 1024 + " K");
}
- public ShenandoahHeap(Address addr) {
- super(addr);
+ @Override
+ public BitMapInterface createBitMap(long bits) {
+ return new ShenandoahBitMap(this);
}
+
}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahHeapRegion.java Tue Apr 02 16:36:00 2019 -0400
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/gc/shenandoah/ShenandoahHeapRegion.java Thu Mar 14 09:53:15 2019 -0400
@@ -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 @@
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();
+ }
}
--- a/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java Tue Apr 02 16:36:00 2019 -0400
+++ b/src/jdk.hotspot.agent/share/classes/sun/jvm/hotspot/oops/ObjectHeap.java Thu Mar 14 09:53:15 2019 -0400
@@ -255,7 +255,9 @@
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);
--- a/test/hotspot/jtreg/serviceability/sa/ClhsdbJhisto.java Tue Apr 02 16:36:00 2019 -0400
+++ b/test/hotspot/jtreg/serviceability/sa/ClhsdbJhisto.java Thu Mar 14 09:53:15 2019 -0400
@@ -35,7 +35,6 @@
* @bug 8191658
* @summary Test clhsdb jhisto command
* @requires vm.hasSA
- * @requires vm.gc != "Shenandoah"
* @library /test/lib
* @run main/othervm ClhsdbJhisto
*/
--- a/test/hotspot/jtreg/serviceability/sa/TestHeapDumpForLargeArray.java Tue Apr 02 16:36:00 2019 -0400
+++ b/test/hotspot/jtreg/serviceability/sa/TestHeapDumpForLargeArray.java Thu Mar 14 09:53:15 2019 -0400
@@ -47,7 +47,6 @@
* @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