001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.heap;
042:
043: import java.io.IOException;
044: import java.util.HashSet;
045: import java.util.Iterator;
046: import java.util.List;
047: import java.util.Set;
048:
049: /**
050: *
051: * @author Tomas Hurka
052: */
053: class NearestGCRoot {
054: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
055:
056: private static final int BUFFER_SIZE = (64 * 1024) / 8;
057:
058: //~ Instance fields ----------------------------------------------------------------------------------------------------------
059:
060: private Field referentFiled;
061: private HprofHeap heap;
062: private LongBuffer readBuffer;
063: private LongBuffer writeBuffer;
064: private Set referenceClasses;
065: private boolean gcRootsComputed;
066:
067: //~ Constructors -------------------------------------------------------------------------------------------------------------
068:
069: NearestGCRoot(HprofHeap h) {
070: heap = h;
071: }
072:
073: //~ Methods ------------------------------------------------------------------------------------------------------------------
074:
075: synchronized Instance getNearestGCRootPointer(Instance instance) {
076: if (heap.getGCRoot(instance) != null) {
077: return instance;
078: }
079:
080: long nextGCPathId = heap.idToOffsetMap.get(
081: instance.getInstanceId()).getNearestGCRootPointer();
082:
083: if (nextGCPathId == 0L) {
084: nextGCPathId = computeGCRootsFor(instance);
085: }
086:
087: if (nextGCPathId != 0L) {
088: return heap.getInstanceByID(nextGCPathId);
089: }
090:
091: return null;
092: }
093:
094: private boolean isWeakOrSoftReference(FieldValue value,
095: Instance instance) {
096: Field f = value.getField();
097:
098: return f.equals(referentFiled)
099: && referenceClasses.contains(instance.getJavaClass()); // NOI18N
100: }
101:
102: private long computeGCRootsFor(Instance instance) {
103: if (gcRootsComputed) {
104: return 0L;
105: }
106:
107: JavaClass weakRef = heap
108: .getJavaClassByName("java.lang.ref.WeakReference"); // NOI18N
109: JavaClass softRef = heap
110: .getJavaClassByName("java.lang.ref.SoftReference"); // NOI18N
111: referenceClasses = new HashSet();
112: referenceClasses.add(weakRef);
113: referenceClasses.addAll(weakRef.getSubClasses());
114: referenceClasses.add(softRef);
115: referenceClasses.addAll(softRef.getSubClasses());
116: referentFiled = computeReferentFiled();
117:
118: try {
119: createBuffers();
120: fillZeroLevel();
121:
122: do {
123: switchBuffers();
124: computeOneLevel();
125: } while (hasMoreLevels());
126: } catch (IOException ex) {
127: ex.printStackTrace();
128: }
129:
130: deleteBuffers();
131: gcRootsComputed = true;
132:
133: return heap.idToOffsetMap.get(instance.getInstanceId())
134: .getNearestGCRootPointer();
135: }
136:
137: private void computeOneLevel() throws IOException {
138: for (;;) {
139: long instanceId = readLong();
140: Instance instance;
141: List fieldValues;
142: Iterator valuesIt;
143:
144: if (instanceId == 0L) { // end of level
145:
146: break;
147: }
148:
149: instance = heap.getInstanceByID(instanceId);
150:
151: if (instance instanceof ObjectArrayInstance) {
152: Iterator instanceIt = ((ObjectArrayInstance) instance)
153: .getValues().iterator();
154:
155: while (instanceIt.hasNext()) {
156: Instance refInstance = (Instance) instanceIt.next();
157: writeConnection(instanceId, refInstance);
158: }
159:
160: continue;
161: } else if (instance instanceof PrimitiveArrayInstance) {
162: continue;
163: } else if (instance instanceof ClassDumpInstance) {
164: ClassDump javaClass = ((ClassDumpInstance) instance).classDump;
165:
166: fieldValues = javaClass.getStaticFieldValues();
167: } else if (instance instanceof InstanceDump) {
168: fieldValues = instance.getFieldValues();
169: } else {
170: if (instance == null) {
171: System.err
172: .println("HeapWalker Warning - null instance for "
173: + instanceId); // NOI18N
174:
175: continue;
176: }
177:
178: throw new IllegalArgumentException("Illegal type "
179: + instance.getClass()); // NOI18N
180: }
181:
182: valuesIt = fieldValues.iterator();
183:
184: while (valuesIt.hasNext()) {
185: FieldValue val = (FieldValue) valuesIt.next();
186:
187: if (val instanceof ObjectFieldValue) {
188: if (!isWeakOrSoftReference(val, instance)) { // skip Soft and Weak References
189:
190: Instance refInstance = ((ObjectFieldValue) val)
191: .getInstance();
192: writeConnection(instanceId, refInstance);
193: }
194: }
195: }
196: }
197: }
198:
199: private Field computeReferentFiled() {
200: JavaClass reference = heap
201: .getJavaClassByName("java.lang.ref.Reference"); // NOI18N
202: Iterator fieldRef = reference.getFields().iterator();
203:
204: while (fieldRef.hasNext()) {
205: Field f = (Field) fieldRef.next();
206:
207: if (f.getName().equals("referent")) { // NOI18N
208:
209: return f;
210: }
211: }
212:
213: throw new IllegalArgumentException(
214: "reference field not found in " + reference.getName()); // NOI18N
215: }
216:
217: private void createBuffers() {
218: readBuffer = new LongBuffer(BUFFER_SIZE);
219: writeBuffer = new LongBuffer(BUFFER_SIZE);
220: }
221:
222: private void deleteBuffers() {
223: readBuffer.delete();
224: writeBuffer.delete();
225: }
226:
227: private void fillZeroLevel() throws IOException {
228: Iterator gcIt = heap.getGCRoots().iterator();
229:
230: while (gcIt.hasNext()) {
231: HprofGCRoot root = (HprofGCRoot) gcIt.next();
232:
233: writeLong(root.getInstanceId());
234: }
235: }
236:
237: private boolean hasMoreLevels() {
238: return writeBuffer.hasData();
239: }
240:
241: private long readLong() throws IOException {
242: return readBuffer.readLong();
243: }
244:
245: private void switchBuffers() {
246: LongBuffer b = readBuffer;
247: readBuffer = writeBuffer;
248: writeBuffer = b;
249: readBuffer.startReading();
250: writeBuffer.reset();
251: }
252:
253: private void writeConnection(long instanceId, Instance refInstance)
254: throws IOException {
255: if (refInstance != null) {
256: long refInstanceId = refInstance.getInstanceId();
257: LongMap.Entry entry = heap.idToOffsetMap.get(refInstanceId);
258:
259: if (entry.getNearestGCRootPointer() == 0L) {
260: writeLong(refInstanceId);
261: entry.setNearestGCRootPointer(instanceId);
262: }
263: }
264: }
265:
266: private void writeLong(long instanceId) throws IOException {
267: writeBuffer.writeLong(instanceId);
268: }
269: }
|