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