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.results.memory;
042:
043: import org.netbeans.lib.profiler.ProfilerClient;
044: import org.netbeans.lib.profiler.ProfilerLogger;
045: import org.netbeans.lib.profiler.client.ClientUtils;
046: import org.netbeans.lib.profiler.client.RuntimeProfilingPoint;
047: import org.netbeans.lib.profiler.global.CommonConstants;
048: import org.netbeans.lib.profiler.global.ProfilingSessionStatus;
049: import org.netbeans.lib.profiler.global.TransactionalSupport;
050: import org.netbeans.lib.profiler.results.BaseCallGraphBuilder;
051: import org.netbeans.lib.profiler.results.RuntimeCCTNode;
052: import java.util.ArrayList;
053: import java.util.HashSet;
054: import java.util.Iterator;
055: import java.util.List;
056: import java.util.Set;
057:
058: /**
059: * @author Misha Dmitirev
060: * @author Jaroslav Bachorik
061: */
062: public class MemoryCallGraphBuilder extends BaseCallGraphBuilder
063: implements MemoryProfilingResultsListener, MemoryCCTProvider {
064: //~ Inner Classes ------------------------------------------------------------------------------------------------------------
065:
066: /**
067: * A hashtable specialized for mapping object ids to references to the respective CCT terminating nodes.
068: * <p/>
069: * Only used for updating of existing structures with new data.
070: */
071: private static class ObjIdToCCTNodeMap {
072: //~ Instance fields ------------------------------------------------------------------------------------------------------
073:
074: /** [0-capacity] individual tracked instances Ids, also replicated at the server side -
075: * bit combination opf epoch, classId & sequential counter */
076: private long[] keys;
077:
078: /** [0-capacity] size in Bytes of tracked instance */
079: private long[] objSize;
080:
081: /** [0-capacity] pointer to the "Term" node containing data - see RuntimeObjLivenessTermCCTNode */
082: private RuntimeObjLivenessTermCCTNode[] values;
083: private int capacity;
084: private int k;
085: private int nObjects;
086: private int threshold; // nObjects - number of tracked objects
087: private long a = 5700357409661599241L;
088: private long lastRemovedObjSize;
089:
090: //~ Constructors ---------------------------------------------------------------------------------------------------------
091:
092: ObjIdToCCTNodeMap() {
093: init();
094: }
095:
096: //~ Methods --------------------------------------------------------------------------------------------------------------
097:
098: public long getLastRemovedObjSize() {
099: return lastRemovedObjSize;
100: }
101:
102: public RuntimeObjLivenessTermCCTNode getNode(long key) {
103: int pos = hash(key);
104: long keyAtPos = keys[pos];
105: int iter = capacity >> 2;
106:
107: while ((keyAtPos != key) && (iter > 0)) { // -1 after a large num of iterations can happen only if "reset collectors" was performed
108: pos = (pos + 1) % capacity;
109: keyAtPos = keys[pos];
110: iter--;
111: }
112:
113: if (iter == 0) {
114: return null;
115: }
116:
117: keys[pos] = -1;
118:
119: RuntimeObjLivenessTermCCTNode ret = values[pos];
120: values[pos] = null;
121: lastRemovedObjSize = objSize[pos];
122: nObjects--;
123:
124: return ret;
125: }
126:
127: public void clear() {
128: keys = null;
129: values = null;
130: init();
131: }
132:
133: public void put(long key, RuntimeObjLivenessTermCCTNode value,
134: long size) {
135: if (nObjects > threshold) {
136: rehash();
137: }
138:
139: int pos = hash(key);
140:
141: while (keys[pos] != -1) {
142: pos = (pos + 1) % capacity;
143: }
144:
145: keys[pos] = key;
146: values[pos] = value;
147: objSize[pos] = size;
148: nObjects++;
149: }
150:
151: public int sizeInBytes() {
152: return (keys.length * 8) + (values.length * 4)
153: + (objSize.length * 8);
154: }
155:
156: private void setThreshold() {
157: threshold = (capacity * 3) / 4;
158: }
159:
160: private int hash(long key) {
161: return (int) ((key * a) >>> (64 - k));
162: }
163:
164: private void init() {
165: capacity = 1024;
166: k = 10; // 2^k == capacity
167: nObjects = 0;
168: setThreshold();
169: keys = new long[capacity];
170:
171: for (int i = 0; i < capacity; i++) {
172: keys[i] = -1;
173: }
174:
175: values = new RuntimeObjLivenessTermCCTNode[capacity];
176: objSize = new long[capacity];
177: }
178:
179: private void rehash() {
180: long[] oldKeys = keys;
181: RuntimeObjLivenessTermCCTNode[] oldValues = values;
182: long[] oldObjSize = objSize;
183: int oldCapacity = capacity;
184: capacity = capacity * 2;
185: k++;
186: keys = new long[capacity];
187:
188: for (int i = 0; i < capacity; i++) {
189: keys[i] = -1;
190: }
191:
192: values = new RuntimeObjLivenessTermCCTNode[capacity];
193: objSize = new long[capacity];
194:
195: for (int i = 0; i < oldCapacity; i++) {
196: if (oldKeys[i] != -1) {
197: int pos = hash(oldKeys[i]);
198:
199: while (keys[pos] != -1) {
200: pos = (pos + 1) % capacity;
201: }
202:
203: keys[pos] = oldKeys[i];
204: values[pos] = oldValues[i];
205: objSize[pos] = oldObjSize[i];
206: }
207: }
208:
209: setThreshold();
210: }
211: }
212:
213: //~ Instance fields ----------------------------------------------------------------------------------------------------------
214:
215: /** used to allow us to update the structures dynamically at runtime (maps internal objId to its corresponding
216: * RuntimeObjLivenessTermNode - table of surviving generations (same class & same allocation path))
217: */
218: private ObjIdToCCTNodeMap objMap;
219: private final TransactionalSupport transaction = new TransactionalSupport();
220:
221: /**
222: * [0 - nProfiledClasses] index: classId, average object age for class
223: */
224: private float[] avgObjectAge;
225:
226: /**
227: * [0 - nProfiledClasses] index: classId
228: */
229: private int[] maxSurvGen;
230:
231: /** [0 - nProfiledClasses] index: classId, contains tracked allocated instrances # for this class */
232: private long[] nTrackedAllocObjects;
233:
234: /** [0 - nProfiledClasses] index: classId,
235: * contains tracked live instrances # for this class - assumption: smaller than allocated, thus only int
236: */
237: private int[] nTrackedLiveObjects;
238: private long[] objectsSizePerClass; // [0-nProfiledClasses] total size in bytes for tracked instances of this class
239: private RuntimeMemoryCCTNode[] stacksForClasses; // [0-nProfiledClasses] class Id -> root of its allocation traces tree
240:
241: /**
242: * [0 - nProfiledClasses] index: classId, if true, the class has been selected for not being profiled
243: */
244: private boolean[] unprofiledClass;
245:
246: /**
247: * the latest GC, numbered from 0 (the first one)
248: */
249: private int currentEpoch;
250: private int nProfiledClasses; // total number of profiled classes
251:
252: //~ Methods ------------------------------------------------------------------------------------------------------------------
253:
254: public long[] getAllocObjectNumbers() {
255: transaction.beginTrans(false);
256:
257: try {
258: long[] res = new long[nProfiledClasses];
259: System
260: .arraycopy(objectsSizePerClass, 0, res, 0,
261: res.length);
262:
263: return res;
264: } finally {
265: transaction.endTrans();
266: }
267: }
268:
269: // temporary methods for exposing builder's internals
270: public int getCurrentEpoch() {
271: transaction.beginTrans(false);
272:
273: try {
274: return currentEpoch;
275: } finally {
276: transaction.endTrans();
277: }
278: }
279:
280: public MemoryCCTProvider.ObjectNumbersContainer getLivenessObjectNumbers() {
281: transaction.beginTrans(false);
282:
283: try {
284: if (getClient().getCurrentInstrType() != CommonConstants.INSTR_OBJECT_LIVENESS) {
285: throw new IllegalStateException(
286: "MemoryCallGraphBuilder must be running in TRACKING_LIVENESS mode in order to provide liveness statistics"); // NOI18N
287: }
288:
289: // Note that for average ages and epoch tails to be realistic we have to have all dead object removed from our data.
290: // That's currently more or less the case, since we call System.gc() before invoking "get results" when doing
291: // Object Liveness Profiling.
292: // However, it would be better if we had a native call to perform full GC, since System.gc() may be disabled easily.
293: updateNumberOfClasses();
294: calculateAverageObjectAges();
295: calculateTotalNumberOfSurvGens();
296:
297: ObjectNumbersContainer res = new ObjectNumbersContainer(
298: nTrackedAllocObjects, nTrackedLiveObjects,
299: objectsSizePerClass, avgObjectAge, maxSurvGen,
300: unprofiledClass, nProfiledClasses);
301:
302: return res;
303: } finally {
304: transaction.endTrans();
305: }
306: }
307:
308: public int getNProfiledClasses() {
309: transaction.beginTrans(false);
310:
311: try {
312: return nProfiledClasses;
313: } finally {
314: transaction.endTrans();
315: }
316: }
317:
318: public long[] getObjectsSizePerClass() {
319: transaction.beginTrans(false);
320:
321: try {
322: return objectsSizePerClass;
323: } finally {
324: transaction.endTrans();
325: }
326: }
327:
328: public RuntimeMemoryCCTNode[] getStacksForClasses() {
329: transaction.beginTrans(false);
330:
331: try {
332: return stacksForClasses;
333: } finally {
334: transaction.endTrans();
335: }
336: }
337:
338: public void beginTrans(boolean mutable) {
339: transaction.beginTrans(mutable);
340: }
341:
342: public boolean classMarkedUnprofiled(int classId) {
343: transaction.beginTrans(false);
344:
345: try {
346: return unprofiledClass[classId];
347: } finally {
348: transaction.endTrans();
349: }
350: }
351:
352: public PresoObjAllocCCTNode createPresentationCCT(int classId,
353: boolean dontShowZeroLiveObjAllocPaths)
354: throws ClientUtils.TargetAppOrVMTerminated {
355: transaction.beginTrans(false);
356:
357: try {
358: PresoObjAllocCCTNode presNode = null;
359: RuntimeMemoryCCTNode classNode = getClassNode(classId);
360: String className = getClassName(classId);
361:
362: if ((classNode == null) || (className == null)) {
363: return null;
364: }
365:
366: switch (getClient().getCurrentInstrType()) {
367: case CommonConstants.INSTR_OBJECT_LIVENESS: {
368: presNode = PresoObjLivenessCCTNode
369: .createPresentationCCTFromVM(getClient(),
370: classNode, className, currentEpoch,
371: dontShowZeroLiveObjAllocPaths);
372:
373: break;
374: }
375: case CommonConstants.INSTR_OBJECT_ALLOCATIONS: {
376: presNode = PresoObjAllocCCTNode
377: .createPresentationCCTFromVM(getClient(),
378: classNode, className);
379:
380: break;
381: }
382: default:
383: throw new IllegalStateException(
384: "MemoryCallGraphBuilder runs in an illegal mode"); // NOI18N
385: }
386:
387: return presNode;
388: } finally {
389: transaction.endTrans();
390: }
391: }
392:
393: public void endTrans() {
394: transaction.endTrans();
395: }
396:
397: public void markClassUnprofiled(int classId) {
398: transaction.beginTrans(true);
399:
400: try {
401: unprofiledClass[classId] = true;
402: } finally {
403: transaction.endTrans();
404: }
405: }
406:
407: public void onAllocStackTrace(char classId, long objSize,
408: int[] methodIds) {
409: RuntimeObjAllocTermCCTNode termNode = (RuntimeObjAllocTermCCTNode) processStackTrace(
410: classId, methodIds, false);
411:
412: if (termNode != null) {
413: termNode.updateForNewObject(objSize);
414: objectsSizePerClass[classId] += objSize;
415: }
416:
417: batchNotEmpty = true;
418: }
419:
420: public void onGcPerformed(char classId, long objectId, int objEpoch) {
421: if (currentEpoch < objEpoch) {
422: currentEpoch = objEpoch;
423: }
424:
425: RuntimeObjLivenessTermCCTNode termNode = objMap
426: .getNode(objectId);
427: long objSize = objMap.getLastRemovedObjSize();
428:
429: if (termNode == null) {
430: return; // Can happen if "reset collectors" previously performed
431: }
432:
433: termNode.updateForRemovedObject(objSize);
434: termNode.removeLiveObjectForEpoch(objEpoch);
435: nTrackedLiveObjects[classId]--;
436: objectsSizePerClass[classId] -= objSize;
437:
438: batchNotEmpty = true;
439: }
440:
441: public void onLivenessStackTrace(char classId, long objectId,
442: int objEpoch, long objSize, int[] methodIds) {
443: if (getClient().getCurrentInstrType() != CommonConstants.INSTR_OBJECT_LIVENESS) {
444: return; // ignore liveness events when not in appropriate mode
445: }
446:
447: if (currentEpoch < objEpoch) {
448: currentEpoch = objEpoch;
449: }
450:
451: try {
452: RuntimeObjLivenessTermCCTNode termNode = (RuntimeObjLivenessTermCCTNode) processStackTrace(
453: classId, methodIds, true);
454:
455: if (termNode != null) {
456: termNode.updateForNewObject(objSize);
457: termNode.addLiveObjectForEpoch(objEpoch);
458: objMap.put(objectId, termNode, objSize);
459:
460: nTrackedAllocObjects[classId]++;
461: nTrackedLiveObjects[classId]++;
462: objectsSizePerClass[classId] += objSize;
463: }
464: } catch (OutOfMemoryError e) {
465: ProfilerLogger.warning("OOME, resetting collectors!!!"); // NOI18N // TODO
466: reset();
467: }
468:
469: batchNotEmpty = true;
470: }
471:
472: public void updateInternals() {
473: loadNamesForJMethodIds();
474: }
475:
476: protected RuntimeCCTNode getAppRootNode() {
477: return new RuntimeMemoryCCTNode();
478: }
479:
480: protected void doBatchStart() {
481: transaction.beginTrans(true);
482: updateNumberOfClasses();
483: }
484:
485: protected void doBatchStop() {
486: transaction.endTrans();
487: }
488:
489: protected void doReset() {
490: transaction.beginTrans(true);
491:
492: try {
493: if (stacksForClasses != null) {
494: for (int i = 0; i < stacksForClasses.length; i++) {
495: stacksForClasses[i] = null;
496: objectsSizePerClass[i] = 0;
497: }
498: }
499:
500: if (objMap != null) {
501: objMap.clear();
502: }
503:
504: if (nTrackedAllocObjects != null) {
505: for (int i = 0; i < nTrackedAllocObjects.length; i++) {
506: nTrackedAllocObjects[i] = 0;
507: objectsSizePerClass[i] = 0;
508: }
509: }
510:
511: if (nTrackedLiveObjects != null) {
512: for (int i = 0; i < nTrackedLiveObjects.length; i++) {
513: nTrackedLiveObjects[i] = 0;
514: }
515: }
516:
517: if (objectsSizePerClass != null) {
518: for (int i = 0; i < objectsSizePerClass.length; i++) {
519: objectsSizePerClass[i] = 0;
520: }
521: }
522: } finally {
523: transaction.endTrans();
524: }
525: }
526:
527: protected void doShutdown() {
528: loadNamesForJMethodIds();
529: }
530:
531: protected void doStartup(ProfilerClient profilerClient) {
532: objMap = new ObjIdToCCTNodeMap();
533: currentEpoch = 0;
534:
535: profilerClient.registerMemoryCCTProvider(this );
536:
537: nProfiledClasses = 0;
538: stacksForClasses = null;
539: objectsSizePerClass = null;
540: nTrackedAllocObjects = null;
541: nTrackedLiveObjects = null;
542: maxSurvGen = null;
543: avgObjectAge = null;
544: unprofiledClass = null;
545: currentEpoch = -1;
546: }
547:
548: private String getClassName(int classId) {
549: status.beginTrans(false);
550:
551: try {
552: return status.getClassNames()[classId];
553: } finally {
554: status.endTrans();
555: }
556: }
557:
558: private RuntimeMemoryCCTNode getClassNode(int classId) {
559: return stacksForClasses[classId];
560: }
561:
562: private boolean isInitialized() {
563: return (unprofiledClass != null) && (stacksForClasses != null);
564: }
565:
566: private RuntimeMemoryCCTNode getNewTerminalNode(int methodId,
567: boolean live) {
568: return live ? new RuntimeObjLivenessTermCCTNode(methodId)
569: : new RuntimeObjAllocTermCCTNode(methodId);
570: }
571:
572: private void calculateAverageObjectAges() {
573: if (!isInitialized()) {
574: return; // in the middle of initialization; don't recalculate
575: }
576:
577: int nClasses = nProfiledClasses;
578:
579: avgObjectAge = new float[nClasses];
580:
581: for (int i = 0; i < nClasses; i++) {
582: if (unprofiledClass[i]) {
583: continue;
584: }
585:
586: RuntimeMemoryCCTNode rootNode = stacksForClasses[i];
587:
588: if (rootNode == null) {
589: continue;
590: }
591:
592: float age = RuntimeObjLivenessTermCCTNode
593: .calculateAvgObjectAgeForAllPaths(rootNode,
594: currentEpoch);
595:
596: if (age < 0.0f) {
597: age = 0.0f; // May happen after "Reset collectors"
598: }
599:
600: avgObjectAge[i] = age;
601: }
602: }
603:
604: private void calculateTotalNumberOfSurvGens() {
605: if (!isInitialized()) {
606: return;
607: }
608:
609: maxSurvGen = new int[nProfiledClasses];
610:
611: for (int i = 0; i < maxSurvGen.length; i++) {
612: if (unprofiledClass[i]) {
613: continue;
614: }
615:
616: RuntimeMemoryCCTNode rootNode = stacksForClasses[i];
617:
618: if (rootNode == null) {
619: continue;
620: }
621:
622: maxSurvGen[i] = RuntimeObjLivenessTermCCTNode
623: .calculateTotalNumberOfSurvGensForAllPaths(rootNode);
624: }
625: }
626:
627: //************************************************************
628:
629: /**********************************************************************************************
630: * Private implementation
631: **********************************************************************************************/
632: private void loadNamesForJMethodIds() {
633: // problem: unloaded classes in the meantime
634: // unloaded classes need to be handled specifically - see ClassLoaderManager, JMethodIdTable
635: transaction.beginTrans(false);
636:
637: try {
638: PresoObjAllocCCTNode.getNamesForMethodIdsFromVM(
639: getClient(), stacksForClasses);
640: } catch (ClientUtils.TargetAppOrVMTerminated ex) { /* Ignore silently */
641: } finally {
642: transaction.endTrans();
643: }
644: }
645:
646: /**
647: * Given the classId anmd the array of methodIds of the stack trace for
648: * the newly allocated object, update the reverse Calling Context Tree for this
649: * class. Adds new nodes and/or increases allocated object counters/size in nodes. Returns the terminating
650: * node in the resulting CCT branch.
651: */
652: private RuntimeMemoryCCTNode processStackTrace(char classId,
653: int[] methodIds, boolean live) {
654: if (classId >= stacksForClasses.length) {
655: ProfilerLogger
656: .severe("Received stack for non existent class Id: "
657: + (int) classId
658: + ", current length: "
659: + stacksForClasses.length // NOI18N
660: );
661: updateNumberOfClasses();
662: ProfilerLogger
663: .severe("Received stack for non existent class Id: "
664: + (int) classId
665: + ", current length after updateNumberOfClasses: " // NOI18N
666: + stacksForClasses.length);
667:
668: if (classId >= stacksForClasses.length) {
669: return null;
670: }
671: }
672:
673: RuntimeMemoryCCTNode curNode = stacksForClasses[classId];
674: RuntimeMemoryCCTNode parentNode = null;
675:
676: if (curNode == null) {
677: curNode = new RuntimeMemoryCCTNode(0);
678: stacksForClasses[classId] = curNode;
679: }
680:
681: int depth = methodIds.length;
682: int depthMinusOne = depth - 1;
683:
684: for (int i = 0; i < depth; i++) {
685: int methodId = methodIds[i];
686: parentNode = curNode;
687:
688: Object children = curNode.children;
689:
690: boolean found = false;
691:
692: if (children != null) {
693: if (children instanceof RuntimeMemoryCCTNode) {
694: if (((RuntimeMemoryCCTNode) children).methodId == methodId) {
695: curNode = (RuntimeMemoryCCTNode) children;
696: found = true;
697: }
698: } else {
699: RuntimeMemoryCCTNode[] ar = (RuntimeMemoryCCTNode[]) children;
700:
701: for (int j = 0; j < ar.length; j++) {
702: if (ar[j].methodId == methodId) {
703: curNode = ar[j];
704: found = true;
705:
706: break;
707: }
708: }
709: }
710: }
711:
712: if (!found) {
713: // Appropriate subnode not found or there are no subnodes yet - create one.
714: if (i < depthMinusOne) {
715: curNode = curNode.addNewChild(methodId); // Non-terminal node
716: } else { // Terminal node - need to create a specialized one depending on the profiling type (obj alloc or obj liveness)
717:
718: RuntimeMemoryCCTNode newNode = getNewTerminalNode(
719: methodId, live);
720: curNode.attachNodeAsChild(newNode);
721: curNode = newNode;
722: }
723: }
724: }
725:
726: // Now check if the curNode that we are going to return is actually not an instance of one of classes representing
727: // "terminal nodes", like RuntimeObjAllocTermCCTNode or RuntimeObjLivenessTermCCTNode. Such nodes contain information
728: // that is normally the same for the whole call chain - such as total number/size of objects of the given type allocated
729: // by this call chain. However, it looks like in some cases (different threads?) it may happen that one complete call
730: // chain may become a fragment of another, longer call chain. In that case we will neeed to have a "terminal" node in the middle
731: // of the chain. Here we are checking for the case when first a longer chain is created, and then a shorter one that
732: // matches a part of the longer one is found, and taking measures.
733: if (curNode.getClass() == RuntimeMemoryCCTNode.class) {
734: RuntimeMemoryCCTNode newNode = getNewTerminalNode(
735: curNode.methodId, live);
736: newNode.children = curNode.children;
737:
738: if (parentNode != null) {
739: Object parChildren = parentNode.children;
740: assert (parChildren != null); // parent will always have chilren
741:
742: if (parChildren instanceof RuntimeMemoryCCTNode) {
743: if (parChildren == curNode) {
744: parentNode.children = newNode;
745: }
746: } else {
747: RuntimeMemoryCCTNode[] ar = (RuntimeMemoryCCTNode[]) parChildren;
748:
749: for (int i = 0; i < ar.length; i++) {
750: if (ar[i] == curNode) {
751: ar[i] = newNode;
752:
753: break;
754: }
755: }
756: }
757: } else {
758: stacksForClasses[classId] = newNode;
759: }
760:
761: curNode = newNode;
762: }
763:
764: return curNode;
765: }
766:
767: private void updateNumberOfClasses() {
768: status.beginTrans(false);
769:
770: try {
771: nProfiledClasses = status.getNInstrClasses();
772: } finally {
773: status.endTrans();
774: }
775:
776: if ((stacksForClasses == null)
777: || (stacksForClasses.length < nProfiledClasses)) {
778: int newSize = (nProfiledClasses * 3) / 2;
779: RuntimeMemoryCCTNode[] newStacks = new RuntimeMemoryCCTNode[newSize];
780:
781: if (stacksForClasses != null) {
782: System.arraycopy(stacksForClasses, 0, newStacks, 0,
783: stacksForClasses.length);
784: }
785:
786: stacksForClasses = newStacks;
787:
788: long[] newObjSize = new long[newSize];
789:
790: if (objectsSizePerClass != null) {
791: System.arraycopy(objectsSizePerClass, 0, newObjSize, 0,
792: objectsSizePerClass.length);
793: }
794:
795: objectsSizePerClass = newObjSize;
796: }
797:
798: if (getClient().getCurrentInstrType() == CommonConstants.INSTR_OBJECT_LIVENESS) {
799: if ((nTrackedLiveObjects == null)
800: || (nTrackedLiveObjects.length < nProfiledClasses)) {
801: int newSize = (nProfiledClasses * 3) / 2;
802: int[] tmpIOldData = nTrackedLiveObjects;
803: int[] tmpINewData = null;
804: long[] tmpLOldData = nTrackedAllocObjects;
805: long[] tmpLNewData = null;
806: boolean[] tmpBOldData = unprofiledClass;
807: boolean[] tmpBNewData = null;
808: tmpINewData = new int[newSize];
809:
810: if (tmpIOldData != null) {
811: System.arraycopy(tmpIOldData, 0, tmpINewData, 0,
812: tmpIOldData.length);
813: }
814:
815: tmpLNewData = new long[newSize];
816:
817: if (tmpLOldData != null) {
818: System.arraycopy(tmpLOldData, 0, tmpLNewData, 0,
819: tmpLOldData.length);
820: }
821:
822: tmpBNewData = new boolean[newSize];
823:
824: if (tmpBOldData != null) {
825: System.arraycopy(tmpBOldData, 0, tmpBNewData, 0,
826: tmpBOldData.length);
827: }
828:
829: nTrackedLiveObjects = tmpINewData;
830: nTrackedAllocObjects = tmpLNewData;
831: unprofiledClass = tmpBNewData;
832:
833: /* Currently unused - avgObjectAge is created on demand only
834: float[][] tmpFOldData = new float[][]{avgObjectAge};
835: float[][] tmpFNewData = new float[1][];
836: for (int i = 0; i < 1; i++) {
837: tmpFNewData[i] = new float[newSize];
838: if (tmpFOldData[i] != null) System.arraycopy(tmpFOldData[i], 0, tmpFNewData[i], 0, tmpFOldData[i].length);
839: }
840: avgObjectAge = tmpFNewData[0]; */
841: }
842: }
843: }
844: }
|