1 /*
2  * Copyright (c) 2017, 2019, Red Hat, Inc. All rights reserved.
3  *
4  * This code is free software; you can redistribute it and/or modify it
5  * under the terms of the GNU General Public License version 2 only, as
6  * published by the Free Software Foundation.
7  *
8  * This code is distributed in the hope that it will be useful, but WITHOUT
9  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
10  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
11  * version 2 for more details (a copy is included in the LICENSE file that
12  * accompanied this code).
13  *
14  * You should have received a copy of the GNU General Public License version
15  * 2 along with this work; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
19  * or visit www.oracle.com if you need additional information or have any
20  * questions.
21  *
22  */
23 
24 package sun.jvm.hotspot.gc.shenandoah;
25 
26 import sun.jvm.hotspot.debugger.OopHandle;
27 import sun.jvm.hotspot.gc.shared.ContiguousSpace;
28 import sun.jvm.hotspot.gc.shared.LiveRegionsProvider;
29 import sun.jvm.hotspot.memory.MemRegion;
30 import sun.jvm.hotspot.oops.Mark;
31 import sun.jvm.hotspot.oops.Oop;
32 import sun.jvm.hotspot.oops.UnknownOopException;
33 import sun.jvm.hotspot.types.*;
34 import sun.jvm.hotspot.runtime.VM;
35 import sun.jvm.hotspot.runtime.VMObject;
36 import sun.jvm.hotspot.debugger.Address;
37 import sun.jvm.hotspot.utilities.AddressOps;
38 
39 import java.util.ArrayList;
40 import java.util.List;
41 import java.util.Observable;
42 import java.util.Observer;
43 
44 
45 public class ShenandoahHeapRegion extends VMObject implements LiveRegionsProvider {
46     private static int EmptyUncommitted;
47     private static int EmptyCommitted;
48     private static int Regular;
49     private static int HumongousStart;
50     private static int HumongousCont;
51     private static int PinnedHumongousStart;
52     private static int CSet;
53     private static int Pinned;
54     private static int PinnedCSet;
55     private static int Trash;
56 
57     private static CIntegerField RegionSizeBytesField;
58     private static Field         RegionStateField;
59     private static CIntegerField RegionIndexField;
60     private static CIntegerField RegionSizeBytesShiftField;
61 
62     private static AddressField BottomField;
63     private static AddressField TopField;
64     private static AddressField EndField;
65 
66     private ShenandoahHeap heap;
67 
68     static {
VM.registerVMInitializedObserver(new Observer() { public void update(Observable o, Object data) { initialize(VM.getVM().getTypeDataBase()); } })69         VM.registerVMInitializedObserver(new Observer() {
70             public void update(Observable o, Object data) {
71                 initialize(VM.getVM().getTypeDataBase());
72             }
73         });
74     }
75 
initialize(TypeDataBase db)76     static private synchronized void initialize(TypeDataBase db) {
77         Type type = db.lookupType("ShenandoahHeapRegion");
78         RegionSizeBytesField = type.getCIntegerField("RegionSizeBytes");
79         RegionStateField = type.getField("_state");
80         RegionIndexField = type.getCIntegerField("_index");
81         BottomField = type.getAddressField("_bottom");
82         TopField = type.getAddressField("_top");
83         EndField = type.getAddressField("_end");
84 
85         RegionSizeBytesShiftField = type.getCIntegerField("RegionSizeBytesShift");
86 
87         EmptyUncommitted     = db.lookupIntConstant("ShenandoahHeapRegion::_empty_uncommitted").intValue();
88         EmptyCommitted       = db.lookupIntConstant("ShenandoahHeapRegion::_empty_committed").intValue();
89         Regular              = db.lookupIntConstant("ShenandoahHeapRegion::_regular").intValue();
90         HumongousStart       = db.lookupIntConstant("ShenandoahHeapRegion::_humongous_start").intValue();
91         HumongousCont        = db.lookupIntConstant("ShenandoahHeapRegion::_humongous_cont").intValue();
92         PinnedHumongousStart = db.lookupIntConstant("ShenandoahHeapRegion::_pinned_humongous_start").intValue();
93         CSet                 = db.lookupIntConstant("ShenandoahHeapRegion::_cset").intValue();
94         Pinned               = db.lookupIntConstant("ShenandoahHeapRegion::_pinned").intValue();
95         PinnedCSet           = db.lookupIntConstant("ShenandoahHeapRegion::_pinned_cset").intValue();
96         Trash                = db.lookupIntConstant("ShenandoahHeapRegion::_trash").intValue();
97     }
98 
regionSizeBytes()99     public static long regionSizeBytes() {
100         return RegionSizeBytesField.getValue();
101     }
102 
regionSizeBytesShift()103     public static int  regionSizeBytesShift() {
104         return RegionSizeBytesShiftField.getJInt();
105     }
106 
ShenandoahHeapRegion(Address addr)107     public ShenandoahHeapRegion(Address addr) {
108         super(addr);
109     }
110 
setHeap(ShenandoahHeap heap)111     public void setHeap(ShenandoahHeap heap) {
112         this.heap = heap;
113     }
114 
bottom()115     public Address bottom() {
116         return BottomField.getValue(addr);
117     }
118 
top()119     public Address top() {
120         return TopField.getValue(addr);
121     }
122 
end()123     public Address end() {
124         return EndField.getValue(addr);
125     }
126 
127     @Override
hashCode()128     public int hashCode() {
129         return (int)index();
130     }
131 
132     @Override
equals(Object other)133     public boolean equals(Object other) {
134         if (other instanceof ShenandoahHeapRegion) {
135             ShenandoahHeapRegion otherRegion = (ShenandoahHeapRegion)other;
136             return otherRegion.index() == index();
137         }
138         return false;
139     }
140 
getLiveRegions()141     public List<MemRegion> getLiveRegions() {
142         List<MemRegion> res = new ArrayList<>();
143         int state = regionState();
144         if (state == EmptyUncommitted || state == EmptyCommitted || state == Trash) {
145             // No live data
146         } else if (state == HumongousCont) {
147             // Handled by HumongousStart
148         } else if (state == HumongousStart || state == PinnedHumongousStart) {
149             handleHumongousRegion(res);
150         } else if (state == Regular || state == Pinned) {
151             handleRegularRegion(res);
152         } else if (state == CSet || state == PinnedCSet) {
153             // CSet
154             handleCSetRegion(res);
155         } else {
156             throw new RuntimeException("Unknown region state: " + state);
157         }
158         return res;
159     }
160 
161     /*
162      * Note: RegionState is an enum on JVM side. Seems that there is not
163      *       a standard way to read enum value. We read it as an integer
164      *       from the field's offset.
165      */
regionState()166     private int regionState() {
167         long offset = RegionStateField.getOffset();
168         return addr.getJIntAt(offset);
169     }
170 
handleHumongousRegion(List<MemRegion> res)171     private void handleHumongousRegion(List<MemRegion> res) {
172         long index = index();
173         Address topAddr = top();
174         ShenandoahHeapRegion region = heap.getRegion(++ index);
175         while (region.regionState() == HumongousCont) {
176             topAddr = region.top();
177             region = heap.getRegion(++ index);
178         }
179         res.add(new MemRegion(bottom(), topAddr));
180     }
181 
handleRegularRegion(List<MemRegion> res)182     private void handleRegularRegion(List<MemRegion> res) {
183         res.add(new MemRegion(bottom(), top()));
184     }
185 
186     // Filter out forwarded objects, they should be counted in other regions
handleCSetRegion(List<MemRegion> res)187     private void handleCSetRegion(List<MemRegion> res) {
188         Address end = top();
189         Address start = bottom();
190 
191         Address regionStart = null;
192         Address regionEnd = null;
193         while (AddressOps.lessThan(start, end)) {
194             long size = getObjectSize(start);
195             if (hasForwardee(start)) {
196                 // has to-space object, skip this one
197                 if (regionEnd != null) {
198                     MemRegion mr = new MemRegion(regionStart, regionEnd);
199                     res.add(mr);
200                     regionStart = null;
201                     regionEnd = null;
202                 }
203             } else {
204                 if (regionStart == null) {
205                     regionStart = start;
206                 } else {
207                     regionEnd = start.addOffsetTo(size);
208                 }
209             }
210             start = start.addOffsetTo(size);
211         }
212 
213         if (regionStart != null) {
214             MemRegion mr = new MemRegion(regionStart, top());
215             res.add(mr);
216         }
217     }
218 
index()219     public long index() {
220         return RegionIndexField.getValue(addr);
221     }
222 
hasForwardee(Address rawPtr)223     private boolean hasForwardee(Address rawPtr) {
224         // Forwarding pointer is stored in mark word when it is flagged "marked"
225         Mark mark = new Mark(rawPtr);
226         return mark.isMarked();
227     }
228 
getObjectSize(Address rawPtr)229     private long getObjectSize(Address rawPtr) {
230         OopHandle handle = rawPtr.addOffsetToAsOopHandle(0);
231         Oop obj = null;
232 
233         try {
234             // Best effort, may fail
235             obj = VM.getVM().getObjectHeap().newOop(handle);
236         } catch (UnknownOopException exp) {
237             throw new RuntimeException(" UnknownOopException  " + exp);
238         }
239         return obj.getObjectSize();
240     }
241 }
242