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.server;
042:
043: import org.netbeans.lib.profiler.server.system.GC;
044: import org.netbeans.lib.profiler.server.system.Threads;
045: import java.lang.ref.ReferenceQueue;
046: import java.lang.ref.WeakReference;
047:
048: /**
049: * This class contains instrumentation methods for object liveness profiling.
050: *
051: * @author Misha Dmitriev
052: */
053: public class ProfilerRuntimeObjLiveness extends ProfilerRuntimeMemory {
054: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
055:
056: // ------------------------------------- Support classes --------------------------------------------------
057: static class ProfilerRuntimeObjLivenessWeakRef extends
058: WeakReference {
059: //~ Instance fields ------------------------------------------------------------------------------------------------------
060:
061: long objId;
062:
063: //~ Constructors ---------------------------------------------------------------------------------------------------------
064:
065: ProfilerRuntimeObjLivenessWeakRef(Object obj,
066: ReferenceQueue rq, long objId) {
067: super (obj, rq);
068: this .objId = objId;
069: }
070: }
071:
072: /** A thread that waits on a ReferenceQueue managing our marked objects */
073: static class ReferenceManagerThread extends Thread {
074: //~ Instance fields ------------------------------------------------------------------------------------------------------
075:
076: private boolean terminated;
077:
078: //~ Constructors ---------------------------------------------------------------------------------------------------------
079:
080: ReferenceManagerThread() {
081: ThreadInfo.addProfilerServerThread(this );
082: setName(PROFILER_SPECIAL_EXEC_THREAD_NAME + " 3"); // NOI18N
083: }
084:
085: //~ Methods --------------------------------------------------------------------------------------------------------------
086:
087: public void run() {
088: while (!terminated) {
089: try {
090: ProfilerRuntimeObjLivenessWeakRef wr = (ProfilerRuntimeObjLivenessWeakRef) rq
091: .remove(200);
092:
093: if (wr != null) {
094: signalObjGC(wr);
095: }
096: } catch (InterruptedException ex) { /* Should not happen */
097: }
098: }
099:
100: ThreadInfo.removeProfilerServerThread(this );
101: }
102:
103: public void terminate() {
104: terminated = true;
105:
106: try {
107: Thread.sleep(300);
108: } catch (InterruptedException ex) { /* Should not happen */
109: }
110: }
111: }
112:
113: /** A hashtable keeping a set of all tracked objects */
114: static class WeakRefSet {
115: //~ Instance fields ------------------------------------------------------------------------------------------------------
116:
117: private WeakReference[] keys;
118: private int capacity;
119: private int nObjects;
120: private int threshold;
121:
122: //~ Constructors ---------------------------------------------------------------------------------------------------------
123:
124: WeakRefSet() {
125: capacity = 1003;
126: setThreshold();
127: keys = new WeakReference[capacity];
128: }
129:
130: //~ Methods --------------------------------------------------------------------------------------------------------------
131:
132: public synchronized void put(WeakReference key) {
133: if (nObjects > threshold) {
134: rehash();
135: }
136:
137: int pos = key.hashCode() % capacity;
138:
139: while (keys[pos] != null) {
140: pos = (pos + 1) % capacity;
141: }
142:
143: keys[pos] = key;
144: nObjects++;
145: }
146:
147: public synchronized void remove(WeakReference key) {
148: int pos = key.hashCode() % capacity;
149:
150: while (keys[pos] != key) {
151: pos = (pos + 1) % capacity;
152: }
153:
154: keys[pos] = null;
155: nObjects--;
156: }
157:
158: private void setThreshold() {
159: threshold = (capacity * 3) / 4;
160: }
161:
162: private void rehash() {
163: WeakReference[] oldKeys = keys;
164: int oldCapacity = capacity;
165: capacity = (capacity * 2) + 1;
166: keys = new WeakReference[capacity];
167:
168: for (int i = 0; i < oldCapacity; i++) {
169: if (oldKeys[i] != null) {
170: int pos = oldKeys[i].hashCode() % capacity;
171:
172: while (keys[pos] != null) {
173: pos = (pos + 1) % capacity;
174: }
175:
176: keys[pos] = oldKeys[i];
177: }
178: }
179:
180: setThreshold();
181: }
182: }
183:
184: //~ Static fields/initializers -----------------------------------------------------------------------------------------------
185:
186: protected static ReferenceQueue rq;
187: protected static WeakRefSet objSet;
188: protected static ReferenceManagerThread rmt;
189: protected static boolean runGCOnGetResults;
190: protected static boolean objLivenessProfilingDisabled = true;
191:
192: //~ Methods ------------------------------------------------------------------------------------------------------------------
193:
194: public static void enableProfiling(boolean v) {
195: if (v) {
196: createNewDataStructures();
197: // activateGCEpochCounter(true); We don't do this anymore, it's activated forever in profiler.server.Monitors
198: GC.resetGCEpochCounter();
199: ProfilerRuntimeMemory.enableProfiling(true);
200: objLivenessProfilingDisabled = false;
201: } else {
202: objLivenessProfilingDisabled = true;
203: ProfilerRuntimeMemory.enableProfiling(false);
204:
205: // Give the threads that are currently executing instrumentation enough time to finish
206: // before we nullify the data structures that are used in instrumentation code.
207: try {
208: Thread.sleep(100);
209: } catch (Exception ex) {
210: }
211:
212: clearDataStructures();
213: }
214: }
215:
216: public static void resetProfilerCollectors() {
217: if (rmt != null) {
218: GC.runGC();
219: rmt.terminate();
220: }
221:
222: createNewDataStructures();
223:
224: // We don't reset the epoch counter anymore, since there is still a chance that some objects with a higher
225: // epoch counter are reported after this event, which may confuse the tool. So we keep the ever-growing epoch counter.
226: //GC.resetGCEpochCounter();
227:
228: // TODO [ian] - check this furhter - it was reported by Jon Christianssen that objects with high surviving gen
229: // numbers were reported after resetting the results, which he (rightfully so) considered wrong
230: }
231:
232: public static void signalObjGC(ProfilerRuntimeObjLivenessWeakRef wr) {
233: long objectId = wr.objId;
234: objSet.remove(wr);
235: writeObjGCEvent(objectId);
236: }
237:
238: public static void traceObjAlloc(Object object, char classId) {
239: if (objLivenessProfilingDisabled) {
240: return;
241: }
242:
243: if (ThreadInfo.profilingSuspended()
244: || ThreadInfo.isCurrentThreadProfilerServerThread()) {
245: // Avoid counting objects allocated by our own agent threads, or by this method's callees
246: return;
247: }
248:
249: ThreadInfo ti = ThreadInfo.getThreadInfo();
250:
251: if (!ti.isInitialized()) {
252: ti.initialize(true);
253: }
254:
255: if (ti.inProfilingRuntimeMethod > 0) {
256: return;
257: }
258:
259: ti.inProfilingRuntimeMethod++;
260:
261: // See comment marked with (***) in ProfilerRuntimeCPUFullInstr
262: classId = (char) ((int) classId);
263:
264: int objCount = 0;
265:
266: synchronized (allocatedInstancesCount) {
267: objCount = ++allocatedInstancesCount[classId];
268: }
269:
270: if (allocatedInstThreshold[classId] <= 0) {
271: //System.out.print("+++ Alloc object "); //System.out.print((int) classId); System.out.print(" "); System.out.println(object);
272: char epoch = (char) GC.getCurrentGCEpoch();
273:
274: // Generate a 64-bit object id. Make sure the function is the same at the tool side!
275: long objectId = (((long) classId) << 48)
276: | (((long) epoch) << 32) | ((long) objCount);
277: ProfilerRuntimeObjLivenessWeakRef wr = new ProfilerRuntimeObjLivenessWeakRef(
278: object, rq, objectId);
279: objSet.put(wr);
280:
281: long objSize = getCachedObjectSize(classId, object);
282:
283: getAndSendCurrentStackTrace(classId, epoch, objCount,
284: objSize);
285:
286: allocatedInstThreshold[classId] = nextRandomizedInterval();
287: }
288:
289: allocatedInstThreshold[classId]--;
290: ti.inProfilingRuntimeMethod--;
291: }
292:
293: protected static void setRunGCOnGetResults(boolean v) {
294: runGCOnGetResults = v;
295: }
296:
297: protected static boolean getRunGCOnGetResults() {
298: return runGCOnGetResults;
299: }
300:
301: protected static void clearDataStructures() {
302: ProfilerRuntimeMemory.clearDataStructures();
303:
304: if (rmt != null) {
305: GC.runGC();
306: rmt.terminate();
307: }
308:
309: rq = null;
310: objSet = null;
311: rmt = null;
312:
313: // activateGCEpochCounter(false); See the comment in enableProfiling() above
314: }
315:
316: protected static void createNewDataStructures() {
317: ProfilerRuntimeMemory.createNewDataStructures();
318: rq = new ReferenceQueue();
319: objSet = new WeakRefSet();
320: rmt = new ReferenceManagerThread();
321: Threads.recordAdditionalProfilerOwnThread(rmt);
322: rmt.start();
323: }
324: }
|