1 | /* |
2 | * Copyright 2008 Eric Caspole |
3 | * |
4 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this |
5 | * file except in compliance with the License. You may obtain a copy of the License at |
6 | * |
7 | * http://www.apache.org/licenses/LICENSE-2.0 |
8 | * |
9 | * Unless required by applicable law or agreed to in writing, software distributed under |
10 | * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
11 | * KIND, either express or implied. See the License for the specific language governing |
12 | * permissions and limitations under the License. |
13 | */ |
14 | package net.sf.madmap; |
15 | |
16 | import java.util.*; |
17 | import java.util.concurrent.*; |
18 | import java.io.*; |
19 | |
20 | import javax.swing.JProgressBar; |
21 | import javax.swing.JOptionPane; |
22 | import java.lang.management.*; |
23 | import javax.management.*; |
24 | |
25 | /** |
26 | * Madmap objects hold the file being read and run the analysis after being |
27 | * read in by the HprofReaders, which get started by the Madmap for the input |
28 | * file. |
29 | * |
30 | * @author ecaspole |
31 | * |
32 | */ |
33 | public class Madmap implements MadmapMBean, Runnable { |
34 | static File _hprofFile = null; |
35 | String _fileName; |
36 | boolean _isBinary = false; |
37 | long _startTime = 0; |
38 | ILogger _logger = (ILogger)(MadmapMain.appContext()).getBean("logger"); |
39 | IResources _resources = (IResources)(MadmapMain.appContext()).getBean("resources"); |
40 | IHprofReader _reader = (IHprofReader)(MadmapMain.appContext()).getBean("reader"); |
41 | IHprofReader _binReader = (IHprofReader)(MadmapMain.appContext()).getBean("binReader"); |
42 | |
43 | ConcurrentHashMap<HprofHeapAllocation,HprofHeapAllocation> _heap_objects_table = new ConcurrentHashMap<HprofHeapAllocation,HprofHeapAllocation>(); |
44 | ConcurrentHashMap<HprofClassElement,HprofClassElement> _class_list = new ConcurrentHashMap<HprofClassElement,HprofClassElement>(); |
45 | |
46 | ConcurrentHashMap _leak_guess_table = null; // new ConcurrentHashMap(); |
47 | ConcurrentHashMap _stack_trace_table = new ConcurrentHashMap(); |
48 | ConcurrentHashMap _threads_list = new ConcurrentHashMap(); |
49 | Vector _roots_list = new Vector( 1024 ); |
50 | ConcurrentHashMap _finalizable_class_list = new ConcurrentHashMap(); |
51 | |
52 | MainWindow _window; |
53 | |
54 | // MBean methods |
55 | public String getMsg() { return "Thanks for using the Madmap MBean."; } |
56 | |
57 | public boolean getVerbose() { return MadmapMain.verbose(); } |
58 | public void setVerbose(boolean x) { MadmapMain.setVerbose(x); } |
59 | |
60 | public String getFilename() { return _fileName; } |
61 | public boolean isBinary() { return _isBinary; } |
62 | |
63 | // Basic type arrays do not know their class addr when they are seen, |
64 | // so they are reconciled during analysis to build |
65 | // the type/count histogram. |
66 | HprofClassElement boolArrayKlass = null; |
67 | HprofClassElement byteArrayKlass = null; |
68 | HprofClassElement charArrayKlass = null; |
69 | HprofClassElement shortArrayKlass = null; |
70 | HprofClassElement intArrayKlass = null; |
71 | HprofClassElement longArrayKlass = null; |
72 | HprofClassElement floatArrayKlass = null; |
73 | HprofClassElement doubleArrayKlass = null; |
74 | |
75 | // fakeKlassAddrs are used to keep track of basic type array |
76 | // classes until all the classes are loaded, then arrays holding the |
77 | // fake type are patched with the real type addr |
78 | |
79 | static final int fakeBoolArrayKlassAddr = -1; |
80 | static final int fakeByteArrayKlassAddr = -2; |
81 | static final int fakeCharArrayKlassAddr = -3; |
82 | static final int fakeShortArrayKlassAddr = -4; |
83 | static final int fakeIntArrayKlassAddr = -5; |
84 | static final int fakeLongArrayKlassAddr = -6; |
85 | static final int fakeFloatArrayKlassAddr = -7; |
86 | static final int fakeDoubleArrayKlassAddr = -8; |
87 | |
88 | static final HprofClassElement fakeBoolArrayKlass = new HprofClassElement(fakeBoolArrayKlassAddr, "bool_fake_array_class"); |
89 | static final HprofClassElement fakeByteArrayKlass = new HprofClassElement(fakeByteArrayKlassAddr, "byte_fake_array_class"); |
90 | static final HprofClassElement fakeCharArrayKlass = new HprofClassElement(fakeCharArrayKlassAddr, "char_fake_array_class"); |
91 | static final HprofClassElement fakeShortArrayKlass = new HprofClassElement(fakeShortArrayKlassAddr, "short_fake_array_class"); |
92 | static final HprofClassElement fakeIntArrayKlass = new HprofClassElement(fakeIntArrayKlassAddr, "int_fake_array_class"); |
93 | static final HprofClassElement fakeLongArrayKlass = new HprofClassElement(fakeLongArrayKlassAddr, "long_fake_array_class"); |
94 | static final HprofClassElement fakeFloatArrayKlass = new HprofClassElement(fakeFloatArrayKlassAddr, "float_fake_array_class"); |
95 | static final HprofClassElement fakeDoubleArrayKlass = new HprofClassElement(fakeDoubleArrayKlassAddr, "double_fake_array_class"); |
96 | |
97 | public static HprofClassElement getFakeArrayClass( String nm ) { |
98 | HprofClassElement cls_addr = null; |
99 | if ((nm == "boolean[]") || (nm == "boolean") || (nm == "[Z")) { |
100 | cls_addr = fakeBoolArrayKlass; |
101 | } else if ((nm == "byte[]") || (nm == "byte") || (nm == "[B")) { |
102 | cls_addr = fakeByteArrayKlass; |
103 | } else if ((nm == "char[]") || (nm == "char") || (nm == "[C")) { |
104 | cls_addr = fakeCharArrayKlass; |
105 | } else if (( nm == "short[]") || (nm == "short") || (nm == "[S")) { |
106 | cls_addr = fakeShortArrayKlass; |
107 | } else if ((nm == "int[]") || (nm == "int") || (nm == "[I")) { |
108 | cls_addr = fakeIntArrayKlass; |
109 | } else if ((nm == "long[]") || (nm == "long") || (nm == "[J")) { |
110 | cls_addr = fakeLongArrayKlass; |
111 | } else if ((nm == "float[]") || (nm == "float") || (nm == "[F")) { |
112 | cls_addr = fakeFloatArrayKlass; |
113 | } else if ((nm == "double[]") || (nm == "double") || (nm == "[D")) { |
114 | cls_addr = fakeDoubleArrayKlass; |
115 | } else { |
116 | System.out.println( "### No match for basic type array: " + nm ); |
117 | assert true == false : "should not reach here!"; |
118 | } |
119 | |
120 | return cls_addr; |
121 | } |
122 | |
123 | |
124 | public static void setFile( File f ) { |
125 | _hprofFile = f; |
126 | } |
127 | |
128 | public ConcurrentHashMap<HprofHeapAllocation,HprofHeapAllocation> getObjects() { return _heap_objects_table; } |
129 | public ConcurrentHashMap getGuesses() { return _leak_guess_table; } |
130 | |
131 | public ConcurrentHashMap getStackTraces() { return _stack_trace_table; } |
132 | public ConcurrentHashMap getThreads() { return _threads_list; } |
133 | public Vector getRoots() { return _roots_list; } |
134 | public ConcurrentHashMap<HprofClassElement,HprofClassElement> getClasses() { return _class_list; } |
135 | |
136 | public float elapsedTime() { |
137 | return (float)(System.currentTimeMillis() - _startTime)/(float)1000.0; |
138 | } |
139 | |
140 | public void run() { |
141 | _logger.myLog( getWindow(), " " ); |
142 | _logger.myLog( getWindow(), "=================================================================================" ); |
143 | _logger.myLog( getWindow(), "Execution environment for this run: " ); |
144 | _logger.myLog( getWindow(), "madmap version\t= " + _resources.getString("version") ); |
145 | _logger.myLog( getWindow(), "java version\t= " + System.getProperty ( "java.version" ) ); |
146 | _logger.myLog( getWindow(), "os name\t= " + System. getProperty ( "os.name" ) ); |
147 | _logger.myLog( getWindow(), "os version\t= " + System. getProperty ( "os.version" ) ); |
148 | _logger.myLog( getWindow(), "default locale\t= " + java.util.Locale.getDefault() ); |
149 | _logger.myLog( getWindow(), "cwd\t= " + System.getProperty("user.dir") ); |
150 | _logger.myLog( getWindow(), "Total memory\t= " + Runtime.getRuntime().totalMemory() ); |
151 | _logger.myLog( getWindow(), "Max memory\t= " + Runtime.getRuntime().maxMemory() ); |
152 | _logger.myLog( getWindow(), "Free memory\t= " + Runtime.getRuntime().freeMemory() ); |
153 | _logger.myLog( getWindow(), "Processors\t= " + Runtime.getRuntime().availableProcessors() ); |
154 | _logger.myLog( getWindow(), "=================================================================================" ); |
155 | _logger.myLog( getWindow(), " " ); |
156 | init(); |
157 | } |
158 | |
159 | public void init() |
160 | { |
161 | _startTime = System.currentTimeMillis(); |
162 | if (isBinary()) { |
163 | _binReader.parse( this, _fileName ); |
164 | } else{ |
165 | _reader.parse( this, _fileName ); |
166 | } |
167 | if ( MadmapMain.runDump() ) { |
168 | dumpCollectedInfo( true, true, true, true, false); |
169 | } |
170 | analyze(); |
171 | } |
172 | |
173 | public void dumpCollectedInfo( boolean thds, boolean stacks, boolean roots, boolean classes, boolean objects ) { |
174 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
175 | _logger.myLog( getWindow(), "Dumping tables... " ); |
176 | |
177 | if ( thds == true ) { |
178 | // Threads |
179 | _logger.myLog( getWindow(), "Dumping Threads... " ); |
180 | Collection<HprofThreadData> htc = this.getThreads().values(); |
181 | Iterator<HprofThreadData> ihtc = htc.iterator(); |
182 | while( ihtc.hasNext() ) { |
183 | HprofThreadData ho = ihtc.next(); |
184 | _logger.myLog( getWindow(), ho.toString() ); |
185 | } |
186 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
187 | } |
188 | |
189 | if ( stacks == true ) { |
190 | // Stack traces |
191 | _logger.myLog( getWindow(), "Dumping Stack traces... " ); |
192 | Collection<HprofStackTraceData> htc = this.getStackTraces().values(); |
193 | Iterator<HprofStackTraceData> ihtc = htc.iterator(); |
194 | while( ihtc.hasNext() ) { |
195 | HprofStackTraceData sd = ihtc.next(); |
196 | _logger.myLog( getWindow(), sd.toString() ); |
197 | |
198 | } |
199 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
200 | } |
201 | |
202 | if ( roots == true ) { |
203 | // Roots |
204 | _logger.myLog( getWindow(), "Dumping Roots... " ); |
205 | for( int i = 0; i < this.getRoots().size(); i++ ) { |
206 | HprofRoot rt = (HprofRoot) this.getRoots().get(i); |
207 | _logger.myLog( getWindow(), rt.toString() ); |
208 | |
209 | } |
210 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
211 | } |
212 | |
213 | if ( classes == true ) { |
214 | // Classes |
215 | _logger.myLog( getWindow(), "Dumping Classes... " ); |
216 | Collection<HprofClassElement> cc = this.getClasses().values(); |
217 | Iterator<HprofClassElement> icc = cc.iterator(); |
218 | while( icc.hasNext() ) { |
219 | HprofClassElement ho = icc.next(); |
220 | _logger.myLog( getWindow(), ho.toString() ); |
221 | } |
222 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
223 | } |
224 | |
225 | try { |
226 | |
227 | if ( objects == true ) { |
228 | // Objects |
229 | _logger.myLog( getWindow(), "Dumping Objects... " ); |
230 | Collection<HprofHeapAllocation> hoc = this.getObjects().values(); |
231 | Iterator<HprofHeapAllocation> ihoc = hoc.iterator(); |
232 | |
233 | while( ihoc.hasNext() ) { |
234 | HprofHeapAllocation ho = ihoc.next(); |
235 | assert ho != null : "ho was null!"; |
236 | _logger.myLog( getWindow(), ho.toString() ); |
237 | } |
238 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
239 | } |
240 | |
241 | } catch (Exception e) { |
242 | e.printStackTrace(); |
243 | } |
244 | } |
245 | |
246 | |
247 | long _live_objects = 0; |
248 | long _liveObjectsSize = 0; |
249 | long _totalObjectsSize = 0; |
250 | long _total_finalizers = 0; |
251 | |
252 | public void printFinalizers() { |
253 | Collection<HprofHeapAllocation> hoc = this.getObjects().values(); |
254 | Iterator<HprofHeapAllocation> ihoc = hoc.iterator(); |
255 | HprofHeapCollectable ho = null; |
256 | |
257 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
258 | |
259 | while( ihoc.hasNext() ) { |
260 | ho = (HprofHeapCollectable)ihoc.next(); |
261 | if ( ho instanceof HprofClassElement ) { |
262 | continue; |
263 | } |
264 | |
265 | if ( (((HprofHeapAllocation)ho).className()).equals("java.lang.ref.Finalizer") || |
266 | (((HprofHeapAllocation)ho).className()).equals("java/lang/ref/Finalizer")) { |
267 | long referent; |
268 | HprofHeapAllocation horef = null; |
269 | |
270 | _total_finalizers++; |
271 | |
272 | // In 1.4 referent is the third field, in 1.5 it is first |
273 | referent = (ho.children())[ 2 ]; |
274 | horef = ( HprofObject ) _heap_objects_table.get( new HprofHeapElement(referent) ); |
275 | if ( ( horef.className().equals("java.lang.ref.Finalizer")) ) { |
276 | // It must be 1.5, get child 0 instead |
277 | referent = (ho.children())[ 0 ]; |
278 | horef = (HprofObject) _heap_objects_table.get( new HprofHeapElement(referent) ); |
279 | } |
280 | |
281 | assert referent != 0 : "finalizer referent is null!" ; |
282 | assert horef != null : "finalizer referent not found in heap!" ; |
283 | if ( MadmapMain.runDump() ) { |
284 | System.out.println( "## Finalizer referent: " + referent + " = " + horef.toString()); |
285 | } |
286 | |
287 | // Build the sorted TreeSet for the GUI table |
288 | HprofClassElement currClass = horef.getActualClass(); |
289 | HprofClassElement c2 = (HprofClassElement)_finalizable_class_list.get((int)currClass.addr()); |
290 | if (c2 == null) { |
291 | _finalizable_class_list.put( (int)currClass.addr(), currClass ); |
292 | } else { |
293 | assert c2 == currClass : "referent classes should match?"; |
294 | } |
295 | |
296 | currClass.setFinalizableCount( currClass.getFinalizableCount() + 1 ); |
297 | currClass.setFinalizableSize( currClass.getFinalizableSize() + horef.size() ); |
298 | } |
299 | } |
300 | |
301 | java.util.ArrayList<HprofClassElement> fList = new java.util.ArrayList<HprofClassElement>(); |
302 | //fList.addAll(this.getClasses().values()); |
303 | fList.addAll(_finalizable_class_list.values()); |
304 | Collections.sort(fList, new FinalizeComparator()); |
305 | |
306 | if ( ! MadmapMain.noGUI() ) { |
307 | getWindow().addFinalizerHeapTab( fList ); |
308 | } |
309 | |
310 | _logger.myLog( getWindow(), "Summary of finalizable objects sorted by cumulative size: " ); |
311 | _logger.myLog( getWindow(), " " ); |
312 | _logger.myLog( getWindow(), "Size\t\tCount\t\tClass" ); |
313 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
314 | |
315 | Iterator<HprofClassElement> ihoc3 = fList.iterator(); |
316 | while( ihoc3.hasNext() ) { |
317 | HprofClassElement hk = ihoc3.next(); |
318 | int ic = hk.getFinalizableCount(); |
319 | long is = hk.getFinalizableSize(); |
320 | |
321 | if ( ic > 0 ) { |
322 | _logger.myLog( getWindow(), is + "\t\t" + ic + "\t\t" + hk.className() + "@" + Long.toHexString( hk.addr() ) ); |
323 | } |
324 | } |
325 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
326 | _logger.myLog( getWindow(), " " ); |
327 | _logger.myLog( getWindow(), elapsedTime() + ": Total count of finalizers: " + _total_finalizers ); |
328 | } |
329 | |
330 | |
331 | public void buildLiveObjectsTable() { |
332 | long totalSize = this.getObjects().size(); |
333 | long size = 0; |
334 | long lowestHeapAddr = (long) Long.MAX_VALUE; // HACK this is 64GB or so |
335 | long highestHeapAddr = 0; |
336 | HprofHeapAllocation biggestObj = null; |
337 | long biggestObjSize = 0; |
338 | HprofHeapAllocation biggestArr = null; |
339 | long biggestArrSize = 0; |
340 | long deadObjects = 0; |
341 | int progressCounter = 0; |
342 | JProgressBar progress_bar = null; |
343 | |
344 | if ( ! MadmapMain.noGUI() ) { |
345 | progress_bar = getWindow().getProgressBar(); |
346 | getWindow().getLabelForProgressBar().setText("Removing Dead Objects"); |
347 | |
348 | progress_bar.setMinimum(0); |
349 | progress_bar.setMaximum(this.getObjects().size() - 1); |
350 | progress_bar.setValue(0); |
351 | |
352 | getWindow().updateMemoryProgressBar(); |
353 | } |
354 | |
355 | for (Enumeration e = this.getObjects().keys() ; e.hasMoreElements() ; ) { |
356 | Object theKey = e.nextElement(); |
357 | HprofHeapAllocation ho = (HprofHeapAllocation) this.getObjects().get( theKey ); |
358 | size = ho.size(); |
359 | long currObjAddr = ho.addr(); |
360 | |
361 | if ( currObjAddr == 0 ) { |
362 | System.out.println( "Found 0 addr object?! = " + ho ); |
363 | _logger.myLog( getWindow(), ho.toString() ); |
364 | } |
365 | |
366 | // record heap range info |
367 | if ( currObjAddr < lowestHeapAddr ) { |
368 | assert currObjAddr != 0 : "object addr is messed up." ; |
369 | lowestHeapAddr = currObjAddr; |
370 | } |
371 | if ( currObjAddr > highestHeapAddr ) { |
372 | highestHeapAddr = currObjAddr; |
373 | } |
374 | |
375 | _totalObjectsSize += size; |
376 | if ( ho.isAlive() ) { |
377 | |
378 | // fix the basic type arrays right away - it is necessary |
379 | // to print, etc. |
380 | // This doesnt seem like the best place to do this but it avoids |
381 | // having another complete pass over the heap table |
382 | HprofClassElement klass_addr = ho.class_addr(); |
383 | |
384 | if ( klass_addr == fakeBoolArrayKlass ) { |
385 | ho.set_class_addr( boolArrayKlass ); |
386 | assert ho instanceof HprofArrayObject : "Class is wrong for array type"; |
387 | } else if ( klass_addr == fakeByteArrayKlass ) { |
388 | ho.set_class_addr( byteArrayKlass ); |
389 | assert ho instanceof HprofArrayObject : "Class is wrong for array type"; |
390 | } else if ( klass_addr == fakeCharArrayKlass ) { |
391 | ho.set_class_addr( charArrayKlass ); |
392 | assert ho instanceof HprofArrayObject : "Class is wrong for array type"; |
393 | } else if ( klass_addr == fakeShortArrayKlass ) { |
394 | ho.set_class_addr( shortArrayKlass ); |
395 | assert ho instanceof HprofArrayObject : "Class is wrong for array type"; |
396 | } else if ( klass_addr == fakeIntArrayKlass ) { |
397 | ho.set_class_addr( intArrayKlass ); |
398 | assert ho instanceof HprofArrayObject : "Class is wrong for array type"; |
399 | } else if ( klass_addr == fakeLongArrayKlass ) { |
400 | ho.set_class_addr( longArrayKlass ); |
401 | assert ho instanceof HprofArrayObject : "Class is wrong for array type"; |
402 | } else if ( klass_addr == fakeFloatArrayKlass ) { |
403 | ho.set_class_addr( floatArrayKlass ); |
404 | assert ho instanceof HprofArrayObject : "Class is wrong for array type"; |
405 | } else if ( klass_addr == fakeDoubleArrayKlass ) { |
406 | ho.set_class_addr( doubleArrayKlass ); |
407 | assert ho instanceof HprofArrayObject : "Class is wrong for array type"; |
408 | } |
409 | |
410 | _live_objects++; |
411 | _liveObjectsSize += size; |
412 | |
413 | // record the largest live object and array while building live table |
414 | if ( ho instanceof HprofArrayObject ) { |
415 | if ( size > biggestArrSize ) { |
416 | biggestArr = (HprofHeapAllocation) ho; |
417 | biggestArrSize = size; |
418 | } |
419 | } else if ( (ho instanceof HprofObject) /* || (ho instanceof HprofClassElement) */ ) { |
420 | if ( size > biggestObjSize ) { |
421 | biggestObj = (HprofHeapAllocation) ho; |
422 | biggestObjSize = size; |
423 | } |
424 | } else { |
425 | _logger.myLog( getWindow(), "Unknown instance: " ); |
426 | _logger.myLog( getWindow(), ho.toString() ); |
427 | } |
428 | } else { |
429 | |
430 | if ( MadmapMain.runDump() ) { |
431 | System.out.println( "Removed: " + ho.className() + "@" + Long.toHexString( ho.addr() ) ); |
432 | } |
433 | |
434 | this.getObjects().remove( theKey ); |
435 | deadObjects++; |
436 | } |
437 | |
438 | if( ( ! MadmapMain.noGUI() ) && ((progressCounter++) % 100 == 0)) { |
439 | progress_bar.setValue(progressCounter); |
440 | getWindow().updateMemoryProgressBar(); |
441 | } |
442 | } |
443 | |
444 | if ( ! MadmapMain.noGUI() ) { |
445 | progress_bar.setValue(progress_bar.getMaximum()); |
446 | getWindow().updateMemoryProgressBar(); |
447 | } |
448 | |
449 | _logger.myLog( getWindow(), "\n"); |
450 | _logger.myLog( getWindow(), "Objects in Heap:" ); |
451 | _logger.myLog( getWindow(), " Total : " + totalSize ); |
452 | _logger.myLog( getWindow(), " Total size : " + _totalObjectsSize ); |
453 | _logger.myLog( getWindow(), " Live : " + _live_objects ); |
454 | _logger.myLog( getWindow(), " Live size : " + _liveObjectsSize ); |
455 | _logger.myLog( getWindow(), " Unreferenced: " + ( totalSize - _live_objects )); |
456 | _logger.myLog( getWindow(), " Removed : " + deadObjects ); |
457 | _logger.myLog( getWindow(), " Lowest object start address : " + Long.toHexString(lowestHeapAddr) ); |
458 | _logger.myLog( getWindow(), " Highest object start address: " + Long.toHexString(highestHeapAddr) ); |
459 | _logger.myLog( getWindow(), " Size of heap range : 0x" + Long.toHexString(highestHeapAddr - lowestHeapAddr) + " == " + (highestHeapAddr - lowestHeapAddr)); |
460 | |
461 | if ( biggestObj != null ) { |
462 | _logger.myLog( getWindow(), "Biggest Array:" ); |
463 | |
464 | _logger.myLog( getWindow(), new String(Long.toHexString(biggestArr.addr()) + " " + biggestArr.className() + " size=" + biggestArr.size())); |
465 | //if ( MadmapMain.verbose()) { |
466 | if ( false ) { |
467 | _logger.myLog( getWindow(), biggestArr.toString() ); |
468 | _logger.myLog( getWindow(), "\n"); |
469 | } |
470 | _logger.myLog( getWindow(), "Biggest Object:" ); |
471 | _logger.myLog( getWindow(), biggestObj.toString() ); |
472 | } |
473 | } |
474 | |
475 | |
476 | int _totalMarkedLive = 0; |
477 | java.util.Stack<HprofHeapElement> _marking_stack = new java.util.Stack<HprofHeapElement>(); |
478 | |
479 | |
480 | // This will be called with roots with iterative marking |
481 | public void markChildren( HprofHeapCollectable ho ) { |
482 | int markedInThisPass = 0; |
483 | |
484 | assert _marking_stack.empty() : "starting markChildren but _marking_stack is not empty! " ; |
485 | |
486 | if ( ! ho.isAlive() ) { |
487 | ho.setLiveness( true ); |
488 | _totalMarkedLive++; |
489 | markedInThisPass++; |
490 | |
491 | //if ( MadmapMain.runDump() ) { |
492 | // System.out.println( "markChildren: root: " + ho.className() + "@" + Long.toHexString( ho.addr() ) ); |
493 | //} |
494 | |
495 | // Add the children of this root |
496 | long numChildren = ho.children_size(); |
497 | for ( int i = 0; i < numChildren; i++ ) { |
498 | _marking_stack.push( new HprofHeapElement( (long)ho.children()[i] )); |
499 | } |
500 | |
501 | // In a loop, keep marking and adding children going down from this root |
502 | while ( ! _marking_stack.empty() ) { |
503 | |
504 | // Get the long that is a heap ref |
505 | HprofHeapElement addr = (HprofHeapElement)_marking_stack.pop(); |
506 | |
507 | // See if it is in the heap table |
508 | HprofHeapCollectable child = (HprofHeapCollectable) this.getObjects().get( addr ); |
509 | |
510 | // It could be a class ref |
511 | if ( child == null ) { |
512 | //if ( MadmapMain.verbose() ) { |
513 | // System.out.println( "Object " + ho.addr() + " " + " child: " + j + " is null!" ); |
514 | //} |
515 | child = (HprofHeapCollectable) this.getClasses().get( addr ); |
516 | } |
517 | |
518 | if ( child == null ) { |
519 | //if ( MadmapMain.verbose() ) { |
520 | // _logger.myLog( getWindow(), "markChildren: Cannot find child " + Long.toHexString( addr.addr() ) + " descending from root " + Long.toHexString(ho.addr()) + " in heap table!!" ); |
521 | // _logger.myLog( getWindow(), ho.toString() ); |
522 | //} |
523 | } else { |
524 | // Add and mark objects only once |
525 | if ( ! child.isAlive() ) { |
526 | child.setLiveness( true ); |
527 | _totalMarkedLive++; |
528 | markedInThisPass++; |
529 | |
530 | // Add the children of this object |
531 | numChildren = child.children_size(); |
532 | for ( int i = 0; i < numChildren; i++ ) { |
533 | _marking_stack.push( new HprofHeapElement(child.children()[i]) ); |
534 | } |
535 | } |
536 | } |
537 | } |
538 | } else { |
539 | //if ( verbose() ) { |
540 | // System.out.println( "markChildren: root was already marked: " + ho.className() + "@" + Long.toHexString( ho.addr() ) ); |
541 | // ho.print(); |
542 | //} |
543 | |
544 | } |
545 | //if ( markedInThisPass > 1 ) { |
546 | // System.out.println( "markedInThisPass: " + markedInThisPass ); |
547 | //} |
548 | |
549 | assert _marking_stack.empty() : "exiting markChildren but _marking_stack is not empty! " ; |
550 | } |
551 | |
552 | |
553 | private void iterativeMarking() { |
554 | JProgressBar progress_bar = null; |
555 | |
556 | try { |
557 | |
558 | _logger.myLog( getWindow(), elapsedTime() + ": analyze: iterative marking" ); |
559 | |
560 | if ( ! MadmapMain.noGUI() ) { |
561 | progress_bar = getWindow().getProgressBar(); |
562 | getWindow().getLabelForProgressBar().setText("Marking Live Objects"); |
563 | progress_bar.setMinimum(0); |
564 | progress_bar.setMaximum(this.getRoots().size() - 1); |
565 | progress_bar.setValue(0); |
566 | } |
567 | |
568 | // Do marking and analysis by looping-marking method |
569 | { |
570 | for ( int i = 0; i < this.getRoots().size(); i++ ) { |
571 | HprofHeapCollectable ho; |
572 | |
573 | // Get root from class list |
574 | ho = (HprofHeapCollectable) this.getClasses().get( new HprofHeapElement(((HprofRoot)this.getRoots().get(i)).addr()) ); |
575 | |
576 | // otherwise get root from objects table |
577 | if ( ho == null ) { |
578 | ho = (HprofHeapCollectable) this.getObjects().get( new HprofHeapElement( ((HprofRoot)this.getRoots().get(i)).addr() )); |
579 | } |
580 | |
581 | if ( ho != null ) { |
582 | markChildren( ho ); |
583 | } else { |
584 | // The 0 root is the primordial root |
585 | if (i != 0 && MadmapMain.verbose() && (((HprofRoot)this.getRoots().get(i)).rootType().equals("<system class>"))) { |
586 | _logger.myLog( getWindow(), "# Cannot find system class root[" + i + "]: " + this.getRoots().get(i) + " in heap table or in class list!!" ); |
587 | _logger.myLog( getWindow(), ((HprofRoot)this.getRoots().get(i)).toString() ); |
588 | } |
589 | } |
590 | |
591 | if( ( ! MadmapMain.noGUI() ) && (i % 100 == 0)) { |
592 | progress_bar.setValue(i); |
593 | getWindow().updateMemoryProgressBar(); |
594 | } |
595 | } |
596 | |
597 | if( ! MadmapMain.noGUI() ) { |
598 | progress_bar.setValue(progress_bar.getMaximum()); |
599 | } |
600 | _logger.myLog( getWindow(), elapsedTime() + ": analyze: iterative marking complete" ); |
601 | |
602 | // Done marking, dump the _marking_stack |
603 | _marking_stack = null; |
604 | } |
605 | |
606 | } catch( Throwable e ) { |
607 | _logger.myLog( getWindow(), elapsedTime() + ": Exception in iterativeMarking(): " + e ); |
608 | e.printStackTrace(); |
609 | } |
610 | } |
611 | |
612 | |
613 | int _maxRecursion = 0; |
614 | int _currRecursion = 0; |
615 | |
616 | public long recursiveCollectChildrenSize( HprofHeapCollectable ho ) { |
617 | ho.setLiveness( true ); |
618 | ho.setRetainedSize( ho.size() ); |
619 | _totalMarkedLive++; |
620 | long numChildren = ho.children_size(); |
621 | if ( numChildren > 0 ) { |
622 | long[] curr_children = ho.children(); |
623 | |
624 | //if ( MadmapMain.verbose() ) { |
625 | // System.out.println( "Object " + ho.addr() + " " + " children: " + numChildren ); |
626 | //} |
627 | for ( int j = 0; j < numChildren; j++ ) { |
628 | try { |
629 | long addr = curr_children[ j ]; |
630 | HprofHeapCollectable child = (HprofHeapAllocation) this.getObjects().get( new HprofHeapElement(addr) ); |
631 | |
632 | // It could be a class ref |
633 | if ( child == null ) { |
634 | //if ( MadmapMain.verbose() ) { |
635 | // System.out.println( "Object " + ho.addr() + " " + " child: " + j + " is null!" ); |
636 | //} |
637 | child = (HprofHeapCollectable) this.getClasses().get( new HprofHeapElement(addr) ); |
638 | } |
639 | |
640 | if ( child == null ) { |
641 | if ( MadmapMain.verbose() ) { |
642 | _logger.myLog( getWindow(), "Cannot find child ho.children[" + j + "]: " + Long.toHexString( addr ) + " of parent " + Long.toHexString(ho.addr() ) + " in heap table!!" ); |
643 | _logger.myLog( getWindow(), ho.toString() ); |
644 | } |
645 | } else { |
646 | if ( ! child.isAlive() ) { |
647 | long retSize = recursiveCollectChildrenSize( child ); |
648 | ho.setRetainedSize(ho.getRetainedSize() + retSize); |
649 | } |
650 | } |
651 | } catch ( ClassCastException cce ) { |
652 | cce.printStackTrace(); |
653 | } |
654 | } |
655 | } |
656 | _currRecursion++; |
657 | return ho.getRetainedSize(); |
658 | } |
659 | |
660 | /** |
661 | * Build a histogram of types to make a table in the gui |
662 | */ |
663 | private void buildLiveObjectsHistogram() { |
664 | Collection<HprofHeapAllocation> hoc = _heap_objects_table.values(); |
665 | Iterator<HprofHeapAllocation> ihoc = hoc.iterator(); |
666 | while( ihoc.hasNext() ) { |
667 | HprofHeapAllocation ho = ihoc.next(); |
668 | HprofClassElement klassRef = ho.class_addr(); |
669 | assert klassRef != null : "should not be null"; |
670 | try { |
671 | //System.out.println( "... " + ho.className() + "... " + Long.toHexString(ho.class_addr().addr()) + "... " + |
672 | // Long.toHexString(currObjAddr) + " sz:" + size ); |
673 | if ( klassRef != null ) { |
674 | klassRef.setInstanceCount( klassRef.getInstanceCount() + 1 ); |
675 | klassRef.setInstanceSize( klassRef.getInstanceSize() + ho.size() ); |
676 | //System.out.println( "Updating instance count for " + k.className() + "@" + Long.toHexString( k.addr() ) + " sz:" + ho.size() + " total sz:" + k.getInstanceSize()); |
677 | } else { |
678 | _logger.myLog( getWindow(), "no klass for " + Long.toHexString( klassRef.addr() ) + ho ); |
679 | } |
680 | } catch ( Exception e ) { |
681 | _logger.myLog( getWindow(), "### Exception during basic type reconciliation " ); |
682 | if ( ho != null ) { |
683 | _logger.myLog( getWindow(), ho.toString() ); |
684 | } |
685 | _logger.myLog( getWindow(), "### klassRef = " + klassRef ); |
686 | e.printStackTrace(); |
687 | } |
688 | } |
689 | |
690 | java.util.ArrayList<HprofClassElement> classList = new java.util.ArrayList<HprofClassElement>(); |
691 | classList.addAll(this.getClasses().values()); |
692 | Collections.sort(classList, new SizeComparator()); |
693 | |
694 | // remove those with 0 instances |
695 | Iterator<HprofClassElement> icl = classList.iterator(); |
696 | while( icl.hasNext() ) { |
697 | HprofClassElement hk = icl.next(); |
698 | int ic = hk.getInstanceCount(); |
699 | if (ic == 0) { |
700 | icl.remove(); |
701 | if ( MadmapMain.runDump() ) { |
702 | System.out.println( "Removed class with 0 instances: " + hk.className() + "@" + Long.toHexString(hk.addr())); |
703 | } |
704 | } |
705 | } |
706 | |
707 | TreeSet threadSet = new TreeSet( new ThreadComparator() ); |
708 | threadSet.addAll( (Collection<HprofThreadData>)_threads_list.values() ); |
709 | |
710 | if ( ! MadmapMain.noGUI() ) { |
711 | getWindow().addLiveHeapTab( classList, _liveObjectsSize ); |
712 | getWindow().addThreadsTab( threadSet ); |
713 | } |
714 | |
715 | //_logger.myLog( getWindow(), " " ); |
716 | _logger.myLog( getWindow(), "Summary of live objects sorted by cumulative size: " ); |
717 | _logger.myLog( getWindow(), " " ); |
718 | _logger.myLog( getWindow(), "Size\t\tPct of Live Size\t\tCount\t\tClass@ClassAddr" ); |
719 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
720 | |
721 | Iterator<HprofClassElement> ihoc3 = classList.iterator(); |
722 | while( ihoc3.hasNext() ) { |
723 | HprofClassElement hk = ihoc3.next(); |
724 | int ic = hk.getInstanceCount(); |
725 | long is = hk.getInstanceSize(); |
726 | |
727 | float pctLiveSize = (float) is / (float) _liveObjectsSize * (float)100.0; |
728 | |
729 | if ( ic > 0 ) { |
730 | _logger.myLog( getWindow(), is + "\t\t" + pctLiveSize + "%\t\t" + ic + "\t\t" + hk.className() + "@" + Long.toHexString( hk.addr() ) ); |
731 | } |
732 | } |
733 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
734 | } |
735 | |
736 | /** |
737 | * Recursive version of retained size collection |
738 | * Only used with -r |
739 | * @param rootSet |
740 | * @param description |
741 | */ |
742 | void sortOnRetainedSize( Vector rootSet, String description) { |
743 | JProgressBar progress_bar = null; |
744 | int i = 0; |
745 | try { |
746 | Iterator<HprofRoot> irs = rootSet.iterator(); |
747 | |
748 | if ( ! MadmapMain.noGUI() ) { |
749 | progress_bar = getWindow().getProgressBar(); |
750 | getWindow().getLabelForProgressBar().setText("Retained Size: " + description); |
751 | progress_bar.setMinimum(0); |
752 | progress_bar.setMaximum(rootSet.size() - 1); |
753 | progress_bar.setValue(0); |
754 | } |
755 | |
756 | _logger.myLog( getWindow(), elapsedTime() + ": analyze: recursive retained size" ); |
757 | |
758 | while( irs.hasNext() ) { |
759 | HprofRoot hr = irs.next(); |
760 | HprofHeapCollectable ho; |
761 | |
762 | ho = (HprofHeapCollectable) this.getClasses().get( new HprofHeapElement(hr.addr()) ); |
763 | if ( ho == null ) { |
764 | ho = (HprofHeapCollectable) this.getObjects().get( new HprofHeapElement(hr.addr()) ); |
765 | } |
766 | |
767 | if ( ho != null ) { |
768 | recursiveCollectChildrenSize( ho ); |
769 | if ( _currRecursion > _maxRecursion ) { |
770 | _maxRecursion = _currRecursion; |
771 | } |
772 | _currRecursion = 0; |
773 | } else { |
774 | // The 0 root is the primordial root |
775 | if (hr.addr() != 0 && MadmapMain.verbose() && hr.rootType().equals("<system class>")) { |
776 | _logger.myLog( getWindow(), "Cannot find system class root[" + Long.toHexString(hr.addr()) + "] in heap table or in class list!!" ); |
777 | _logger.myLog( getWindow(), hr.toString() ); |
778 | } |
779 | } |
780 | |
781 | if (( ! MadmapMain.noGUI() ) && (i++ % 100 == 0)) { |
782 | progress_bar.setValue(i); |
783 | getWindow().updateMemoryProgressBar(); |
784 | } |
785 | } |
786 | |
787 | if( ! MadmapMain.noGUI() ) { |
788 | progress_bar.setValue(progress_bar.getMaximum()); |
789 | } |
790 | } catch ( Exception e ) { |
791 | System.out.println( "Exception while collect children size = " + e ); |
792 | e.printStackTrace(); |
793 | } |
794 | } |
795 | |
796 | /** |
797 | * Reset object fields used repeatedly during retained size |
798 | * collection passes |
799 | */ |
800 | void resetForScanning() { |
801 | // Erase the liveness field, we use use it during size collection |
802 | for (Enumeration e = this.getObjects().keys() ; e.hasMoreElements() ; ) { |
803 | Object k = e.nextElement(); |
804 | HprofHeapCollectable ho = (HprofHeapCollectable) this.getObjects().get( k ); |
805 | ho.setLiveness(false); |
806 | ho.setRetainedSize(0); |
807 | ho.resetVisited(); |
808 | ho.setClaimed(false); |
809 | } |
810 | for (Enumeration e = this.getClasses().keys() ; e.hasMoreElements() ; ) { |
811 | Object k = e.nextElement(); |
812 | HprofClassElement ho = (HprofClassElement) this.getClasses().get( k ); |
813 | ho.setLiveness(false); |
814 | ho.setRetainedSize(0); |
815 | ho.setTotalRetainedSize(0); |
816 | ho.resetVisited(); |
817 | ho.setClaimed(false); |
818 | } |
819 | _leak_guess_table = new ConcurrentHashMap(); |
820 | } |
821 | |
822 | /** |
823 | * Returns the object for the given address in the target dump |
824 | * @param ref |
825 | * @return |
826 | */ |
827 | HprofHeapCollectable getRefFromAddr( long ref ) { |
828 | // See if it is in the heap table |
829 | HprofHeapCollectable child = (HprofHeapAllocation) this.getObjects().get( new HprofHeapElement( ref )); |
830 | // It could be a class ref |
831 | if ( child == null ) { |
832 | child = (HprofHeapCollectable) this.getClasses().get( new HprofHeapElement(ref) ); |
833 | } |
834 | if ( MadmapMain.runDump() && child == null ) { |
835 | System.out.println( "### getRefFromAddr didnt find " + Long.toHexString( ref )); |
836 | } |
837 | return child; |
838 | } |
839 | |
840 | void accumulateChildrenSize( HprofHeapCollectable ref ) { |
841 | // Collect the visited children sizes into ref |
842 | Stack visited = ref.getVisitedStack(); |
843 | if ( visited != null ) { |
844 | while ( ! visited.empty() ) { |
845 | HprofHeapCollectable childRef = (HprofHeapCollectable) visited.pop(); |
846 | |
847 | if ( ! childRef.isClaimed() ) { |
848 | childRef.claim(); |
849 | long childSize = childRef.getRetainedSize(); |
850 | ref.setRetainedSize( ref.getRetainedSize() + childSize); |
851 | if ( ! (ref.getRetainedSize() < _liveObjectsSize)) { |
852 | _logger.myLog( getWindow(), "ret size > total live size of " + _liveObjectsSize ); |
853 | _logger.myLog( getWindow(), ref.toString() ); |
854 | } |
855 | if ( MadmapMain.runDump() ) { |
856 | System.out.println( "added " + childRef.className() + "@" + Long.toHexString( childRef.addr() ) + "=" + childSize + |
857 | " to:" + ref.className() + "@" + Long.toHexString( ref.addr() ) + " total size:" + ref.getRetainedSize() ); |
858 | } |
859 | } |
860 | } |
861 | } |
862 | |
863 | if ( MadmapMain.runDump() ) { |
864 | System.out.println( "Completed retained size collection " + ref.className() + "@" + |
865 | Long.toHexString( ref.addr() ) + " total size:" + ref.getRetainedSize() ); |
866 | } |
867 | assert ref.getRetainedSize() < _liveObjectsSize : "ret size > total live size!"; |
868 | } |
869 | |
870 | public void iterativeCollectChildrenSize( HprofHeapCollectable ho ) { |
871 | int markedInThisPass = 0; |
872 | java.util.Stack _addr_stack = new java.util.Stack(); |
873 | |
874 | _addr_stack.push( ho ); |
875 | |
876 | // In a loop, keep marking and adding children going down from this root |
877 | while ( ! _addr_stack.empty() ) { |
878 | // Get the long that is a heap ref |
879 | HprofHeapCollectable ref = (HprofHeapCollectable)_addr_stack.pop(); |
880 | //if ( MadmapMain.runDump() ) { |
881 | // System.out.println( "popped ref: " + ref.className() + "@" +Long.toHexString( ref.addr() )); |
882 | //} |
883 | |
884 | if ( ref != null ) { |
885 | ref.setLiveness( true ); |
886 | |
887 | // Add the children of this object |
888 | long numChildren = ref.children_size(); |
889 | |
890 | if (( numChildren > 0 ) && (ref.getRetainedSize() == 0)) { |
891 | int start = ref.getVisitedCount(); |
892 | int i = 0; |
893 | long allChildren[] = ref.children(); |
894 | |
895 | if ( MadmapMain.runDump() ) { |
896 | System.out.println( "scanned " + (markedInThisPass++) + " curr:" + ref.className() + "@" + |
897 | Long.toHexString( ref.addr() ) + " already visited " + start + " children of " + numChildren); |
898 | } |
899 | |
900 | if ( start < numChildren ) { |
901 | boolean pushedRef = false; |
902 | for ( i = ((int)numChildren - start); i > 0; i-- ) { |
903 | // If the child is not visited yet, push it |
904 | // in case of a cycle, the parent will already be visited |
905 | HprofHeapCollectable childRef = getRefFromAddr( allChildren[i - 1] ); |
906 | |
907 | // 080828 - handle null child ref appearing in input file (should be rare) |
908 | if ( (childRef != null) && (! childRef.isAlive())) { |
909 | if ( pushedRef == false) { |
910 | _addr_stack.push( ref ); |
911 | pushedRef = true; |
912 | if ( MadmapMain.runDump() ) { |
913 | System.out.println( " ref: " + ref.className() + "@" + Long.toHexString( ref.addr() )); |
914 | } |
915 | } |
916 | |
917 | _addr_stack.push( childRef ); |
918 | ref.visited( childRef ); |
919 | |
920 | if ( MadmapMain.runDump() ) { |
921 | System.out.println( "pushing ref: " + ref.className() + "@" + Long.toHexString( ref.addr() ) + "->" + |
922 | Long.toHexString( childRef.addr() )); |
923 | } |
924 | } else { |
925 | if ( MadmapMain.runDump() ) { |
926 | System.out.println( "child already live: ref: " + ref.className() + "@" + Long.toHexString( ref.addr() ) + "->" + |
927 | Long.toHexString( childRef.addr() )); |
928 | } |
929 | } |
930 | } |
931 | |
932 | // When we get here all children are alive, they are all visited, but not necessarily visited by this parent |
933 | // add the children we actually visited |
934 | if ( pushedRef == false ) { |
935 | |
936 | if ( MadmapMain.runDump() ) { |
937 | System.out.println( "ref: " + ref.className() + "@" + Long.toHexString( ref.addr() ) + ": all children are visited" ); |
938 | } |
939 | |
940 | if (ref.getRetainedSize() == 0 ) { |
941 | // if we get here all the children are visited from ref, it is a very simple graph with no cycles |
942 | ref.setRetainedSize( ref.size() ); |
943 | accumulateChildrenSize( ref ); |
944 | } |
945 | } |
946 | } else { |
947 | assert (ref.getRetainedSize() == 0) : "resetting retained size?"; |
948 | |
949 | if ( (start == numChildren) && ( ref.getRetainedSize() == 0 ) ) { |
950 | // if we get here all the children are visited from ref, it is a very simple graph with no cycles |
951 | ref.setRetainedSize( ref.size() ); |
952 | accumulateChildrenSize( ref ); |
953 | } else { |
954 | //if ( verbose() ) { |
955 | // System.out.println( "visited stack is empty: " + ref.className() + "@" + Long.toHexString( ref.addr() ) ); |
956 | //} |
957 | } |
958 | } |
959 | } else { |
960 | if ( ref.getRetainedSize() == 0 ) { |
961 | // This object has no children |
962 | ref.setRetainedSize( ref.size() ); |
963 | } else { |
964 | // If we get here, this object has already been fully accounted for by earlier work, nothing to do |
965 | } |
966 | //assert ref.getRetainedSize() < _liveObjectsSize : "ret size > total live size!"; |
967 | } |
968 | } else { |
969 | _logger.myLog( getWindow(), "markChildren: ====================================================================================" ); |
970 | _logger.myLog( getWindow(), "markChildren: pushed null descending from root " + Long.toHexString(ho.addr()) + " in heap table!!" ); |
971 | _logger.myLog( getWindow(), ho.toString() ); |
972 | _logger.myLog( getWindow(), "markChildren: ====================================================================================" ); |
973 | } |
974 | } |
975 | |
976 | assert _addr_stack.empty() : "exiting markChildren but _marking_stack is not empty! " ; |
977 | } |
978 | |
979 | public void iterativeCollectChildrenSizeOld( HprofHeapCollectable ho ) { |
980 | int markedInThisPass = 0; |
981 | java.util.Stack _addr_stack = new java.util.Stack(); |
982 | |
983 | if ( MadmapMain.runDump() ) { |
984 | System.out.println( "Marking root: " + ho.className() + "@" + Long.toHexString( ho.addr() ) ); |
985 | } |
986 | |
987 | _addr_stack.push( ho ); |
988 | |
989 | // In a loop, keep marking and adding children going down from this root |
990 | while ( ! _addr_stack.empty() ) { |
991 | |
992 | // Get the long that is a heap ref |
993 | HprofHeapCollectable ref = (HprofHeapCollectable)_addr_stack.pop(); |
994 | |
995 | if ( ref != null ) { |
996 | ref.setLiveness( true ); |
997 | |
998 | // Add the children of this object |
999 | long numChildren = ref.children_size(); |
1000 | int start = ref.getVisitedCount(); |
1001 | |
1002 | if ( MadmapMain.runDump() ) { |
1003 | System.out.println( "scanned " + (markedInThisPass++) + " curr:" + ref.className() + "@" + |
1004 | Long.toHexString( ref.addr() ) + " already visited children " + start + " of " + numChildren); |
1005 | } |
1006 | |
1007 | if ( numChildren > 0 ) { |
1008 | int i = 0; |
1009 | for ( i = start; i < numChildren; i++ ) { |
1010 | // If the child is not visited yet, push it |
1011 | // in case of a cycle, the parent will already be visited |
1012 | HprofHeapCollectable childRef = getRefFromAddr( ref.children()[i] ); |
1013 | if ( ! childRef.isAlive() ) { |
1014 | // add the parent so we can continue to visit the other children |
1015 | _addr_stack.push( ref ); |
1016 | _addr_stack.push( childRef ); |
1017 | ref.visited( childRef ); |
1018 | |
1019 | if ( MadmapMain.runDump() ) { |
1020 | System.out.println( " ref: " + ref.className() + "@" + Long.toHexString( ref.addr() ) + "->" + |
1021 | Long.toHexString( childRef.addr() )); |
1022 | } |
1023 | break; |
1024 | } else { |
1025 | if ( MadmapMain.runDump() ) { |
1026 | System.out.println( "child already live: ref: " + ref.className() + "@" + Long.toHexString( ref.addr() ) + "->" + |
1027 | Long.toHexString( childRef.addr() )); |
1028 | } |
1029 | } |
1030 | } |
1031 | |
1032 | // if we get here all the children are visited |
1033 | if ( i == numChildren ) { |
1034 | if ( ref.getRetainedSize() == 0 ) { |
1035 | ref.setRetainedSize( ref.size() ); |
1036 | } |
1037 | // Collect the visited children sizes into ref |
1038 | Stack visited = ref.getVisitedStack(); |
1039 | if ( visited != null ) { |
1040 | while ( ! visited.empty() ) { |
1041 | HprofHeapCollectable childRef = (HprofHeapCollectable) visited.pop(); |
1042 | long childSize = childRef.getRetainedSize(); |
1043 | ref.setRetainedSize( ref.getRetainedSize() + childSize); |
1044 | if ( MadmapMain.runDump() ) { |
1045 | System.out.println( "added " + childRef.className() + "@" + Long.toHexString( childRef.addr() ) + "=" + childSize + |
1046 | " to:" + ref.className() + "@" + Long.toHexString( ref.addr() ) + " total size:" + ref.getRetainedSize() ); |
1047 | } |
1048 | } |
1049 | |
1050 | if ( MadmapMain.runDump() ) { |
1051 | System.out.println( "Completed retained size collection " + ref.className() + "@" + |
1052 | Long.toHexString( ref.addr() ) + " total size:" + ref.getRetainedSize() ); |
1053 | } |
1054 | |
1055 | } else { |
1056 | if ( MadmapMain.runDump() ) { |
1057 | System.out.println( "visited stack is empty: " + ref.className() + "@" + Long.toHexString( ref.addr() ) ); |
1058 | } |
1059 | } |
1060 | } |
1061 | } else { |
1062 | // This object has no children |
1063 | ref.setRetainedSize( ref.size() ); |
1064 | //if ( MadmapMain.runDump() ) { |
1065 | // System.out.println( "no children: " + ref.className() + "@" + Long.toHexString( ref.addr() ) ); |
1066 | //} |
1067 | } |
1068 | } else { |
1069 | _logger.myLog( getWindow(), "markChildren: ====================================================================================" ); |
1070 | _logger.myLog( getWindow(), "markChildren: pushed null descending from root " + Long.toHexString(ho.addr()) + " in heap table!!" ); |
1071 | _logger.myLog( getWindow(), ho.toString() ); |
1072 | _logger.myLog( getWindow(), "markChildren: ====================================================================================" ); |
1073 | } |
1074 | } |
1075 | |
1076 | assert _addr_stack.empty() : "exiting markChildren but _marking_stack is not empty! " ; |
1077 | } |
1078 | |
1079 | |
1080 | void iterativeSortOnRetainedSize( Vector rootSet, String description) { |
1081 | JProgressBar progress_bar = null; |
1082 | int i = 0; |
1083 | Iterator<HprofRoot> irs = rootSet.iterator(); |
1084 | |
1085 | _logger.myLog( getWindow(), elapsedTime() + ": analyze: iterative retained size by: " + description ); |
1086 | |
1087 | if ( ! MadmapMain.noGUI() ) { |
1088 | progress_bar = getWindow().getProgressBar(); |
1089 | getWindow().getLabelForProgressBar().setText("Retained Size: " + description); |
1090 | progress_bar.setMinimum(0); |
1091 | progress_bar.setMaximum(rootSet.size() - 1); |
1092 | progress_bar.setValue(0); |
1093 | } |
1094 | |
1095 | try { |
1096 | while( irs.hasNext() ) { |
1097 | HprofRoot hr = irs.next(); |
1098 | HprofHeapCollectable ho; |
1099 | |
1100 | assert hr != null : "root should not be null"; |
1101 | |
1102 | ho = (HprofHeapCollectable) this.getClasses().get( new HprofHeapElement(hr.addr()) ); |
1103 | if ( ho == null ) { |
1104 | ho = (HprofHeapCollectable) this.getObjects().get( new HprofHeapElement( hr.addr()) ); |
1105 | } |
1106 | |
1107 | if ( ho != null ) { |
1108 | if ( MadmapMain.testFeature() == true ) { |
1109 | iterativeCollectChildrenSizeOld( ho ); |
1110 | } else { |
1111 | iterativeCollectChildrenSize( ho ); |
1112 | } |
1113 | } else { |
1114 | // The 0 root is the primordial root |
1115 | if (hr.addr() != 0 && MadmapMain.verbose() && hr.rootType().equals("<system class>")) { |
1116 | _logger.myLog( getWindow(), "Cannot find system class root[" + Long.toHexString(hr.addr()) + "] in heap table or in class list!!" ); |
1117 | _logger.myLog( getWindow(), hr.toString() ); |
1118 | } |
1119 | } |
1120 | |
1121 | if( ( ! MadmapMain.noGUI() ) && (i++ % 100 == 0)) { |
1122 | progress_bar.setValue(i); |
1123 | getWindow().updateMemoryProgressBar(); |
1124 | } |
1125 | } |
1126 | |
1127 | if( ! MadmapMain.noGUI() ) { |
1128 | progress_bar.setValue(progress_bar.getMaximum()); |
1129 | } |
1130 | _logger.myLog( getWindow(), elapsedTime() + ": analyze: iterative retained size complete" ); |
1131 | } catch( Throwable e ) { |
1132 | _logger.myLog( getWindow(), elapsedTime() + ": iterative retained size: " + e ); |
1133 | e.printStackTrace(); |
1134 | } |
1135 | } |
1136 | |
1137 | public void displayRetainedSortResults(String description) { |
1138 | // Object retained size tab first |
1139 | { |
1140 | ArrayList<HprofHeapAllocation> objList = new java.util.ArrayList<HprofHeapAllocation>(); |
1141 | // make a list for the table model |
1142 | ArrayList<HprofHeapAllocation> retModel = new ArrayList<HprofHeapAllocation>(); |
1143 | |
1144 | // Add objects which retain > 10% of live size |
1145 | for (Enumeration e = this.getObjects().keys() ; e.hasMoreElements() ; ) { |
1146 | Object k = e.nextElement(); |
1147 | HprofHeapAllocation ho = this.getObjects().get( k ); |
1148 | long rs = ho.getRetainedSize(); |
1149 | float pctLiveSize = ((float) rs / (float) _liveObjectsSize) * (float)100.0; |
1150 | |
1151 | if ( pctLiveSize > 10.0 ) { |
1152 | objList.add( ho ); |
1153 | } |
1154 | } |
1155 | Collections.sort(objList, new RetainedSizeComparator()); |
1156 | |
1157 | _logger.myLog( getWindow(), "Live objects sorted by cumulative retained size by: " + description ); |
1158 | _logger.myLog( getWindow(), " " ); |
1159 | _logger.myLog( getWindow(), "Retained Size\tPct of Live Size\tClass@ObjAddr" ); |
1160 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
1161 | |
1162 | Iterator<HprofHeapAllocation> ihoc3 = objList.iterator(); |
1163 | while( ihoc3.hasNext() ) { |
1164 | HprofHeapAllocation hk = ihoc3.next(); |
1165 | long rs = hk.getRetainedSize(); |
1166 | float pctLiveSize = ((float) rs / (float) _liveObjectsSize) * (float)100.0; |
1167 | boolean haveThread = false; |
1168 | HprofThreadData thd = null; |
1169 | |
1170 | // Print the thread name as a convenience to the user, it is in the thread record |
1171 | if ( hk.className().equals("java.lang.Thread") ) { |
1172 | thd = (HprofThreadData) getThreads().get( (int) hk.addr() ); |
1173 | if ( thd != null ) { |
1174 | haveThread = true; |
1175 | } |
1176 | } |
1177 | |
1178 | { |
1179 | // Add this object's ret size to its class' ret size |
1180 | HprofClassElement thisClass = (HprofClassElement) hk.getActualClass(); |
1181 | thisClass.setTotalRetainedSize( thisClass.getTotalRetainedSize() + hk.getRetainedSize() ); |
1182 | } |
1183 | |
1184 | //if ( pctLiveSize > 10.0 ) { |
1185 | { |
1186 | int stackId = hk.getStackTrace(); |
1187 | HprofStackTraceData std = null; |
1188 | if ( stackId != 0 ) { |
1189 | std = (HprofStackTraceData)getStackTraces().get(stackId); |
1190 | } |
1191 | List stkTrc = null; |
1192 | if ( std != null ) { |
1193 | stkTrc = std.getStackVector(); |
1194 | } |
1195 | |
1196 | // Things with no stack trace are unlikely to be a leak |
1197 | if ( (stkTrc != null) && (stkTrc.size() == 1) && ((String)stkTrc.get(0)).equals("<empty>") ) { |
1198 | // really need to do this? |
1199 | //ihoc3.remove(); |
1200 | } else { |
1201 | // now examine children and get their retained size |
1202 | // Leak guesses will be based on the idea of "if obj x retains n%, x is a |
1203 | // leak guess if no child of x retains more than (n - 5)% |
1204 | long[] child_refs = hk.children(); |
1205 | boolean notLeak = false; |
1206 | if ( child_refs != null ) { |
1207 | for ( int i = 0; i < child_refs.length; i++ ) { |
1208 | HprofHeapCollectable child = getRefFromAddr( child_refs[i] ); |
1209 | if (child != null) { |
1210 | long rsc = child.getRetainedSize(); |
1211 | float childPctLiveSize = ((float) rsc / (float) _liveObjectsSize) * (float)100.0; |
1212 | if ( childPctLiveSize > (pctLiveSize - (float)5.0) ) { |
1213 | notLeak = true; |
1214 | } |
1215 | } else { |
1216 | notLeak = true; |
1217 | } |
1218 | } |
1219 | if (! notLeak ) { |
1220 | getGuesses().put( hk.addr(), (new Boolean(true)) ); |
1221 | } |
1222 | } |
1223 | StringBuffer objDetail = new StringBuffer(rs + "\t" + pctLiveSize + "%\t" + "\t" + hk.className() + "@" + Long.toHexString( hk.addr())); |
1224 | if ( haveThread == true ) { |
1225 | objDetail.append(" [ " + thd.name() + " ]"); |
1226 | } |
1227 | if (! notLeak ) { |
1228 | objDetail.append("\t *** Leak Guess ***"); |
1229 | } |
1230 | _logger.myLog( getWindow(), objDetail.toString() ); |
1231 | |
1232 | if (stkTrc != null) { |
1233 | for (int j = 0; j < stkTrc.size(); j++ ) { |
1234 | String s = (String) stkTrc.get( j ); |
1235 | _logger.myLog( getWindow(), " " + s ); |
1236 | } |
1237 | } |
1238 | retModel.add( hk ); |
1239 | } |
1240 | } |
1241 | } |
1242 | |
1243 | if ( ! MadmapMain.noGUI() ) { |
1244 | getWindow().addRetainedHeapTab( this, retModel, _liveObjectsSize, description ); |
1245 | } |
1246 | } |
1247 | |
1248 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
1249 | } |
1250 | |
1251 | |
1252 | public void doRetainedByClass() { |
1253 | for (int i = 0; i < _rootDescNames.length; i++) { |
1254 | int verifyRoots = getRoots().size(); |
1255 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
1256 | Collections.sort(getRoots(), new RootComparator(_rootHprofNames[i])); |
1257 | assert verifyRoots == getRoots().size() : "roots sort is messed up"; |
1258 | |
1259 | String description = _rootHprofNames[i]; |
1260 | |
1261 | // Now build a list of retained-by-class to get ready to show in a tab |
1262 | { |
1263 | ArrayList<HprofHeapCollectable> classList = new java.util.ArrayList<HprofHeapCollectable>(); |
1264 | |
1265 | // Add classes which retain > 10% of live size |
1266 | for (Enumeration e = this.getClasses().keys() ; e.hasMoreElements() ; ) { |
1267 | Object k = e.nextElement(); |
1268 | HprofHeapCollectable ho = (HprofHeapCollectable) this.getClasses().get( k ); |
1269 | long rs = ho.getRetainedSize(); |
1270 | float pctLiveSize = ((float) rs / (float) _liveObjectsSize) * (float)100.0; |
1271 | |
1272 | if ( pctLiveSize > 10.0 ) { |
1273 | classList.add( ho ); |
1274 | } |
1275 | } |
1276 | |
1277 | Collections.sort(classList, new CumulativeRetainedSizeComparator()); |
1278 | // make a list for the table model |
1279 | ArrayList<HprofHeapCollectable> classRetModel = new ArrayList<HprofHeapCollectable>(); |
1280 | |
1281 | _logger.myLog( getWindow(), "Classes sorted by cumulative retained size by: " + description ); |
1282 | _logger.myLog( getWindow(), " " ); |
1283 | _logger.myLog( getWindow(), "Retained Size\tPct of Live Size\tInstance Count\tClassName@ClassAddr" ); |
1284 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
1285 | |
1286 | Iterator<HprofHeapCollectable> ihoc = classList.iterator(); |
1287 | while( ihoc.hasNext() ) { |
1288 | HprofHeapCollectable hk = ihoc.next(); |
1289 | classRetModel.add( hk ); |
1290 | } |
1291 | |
1292 | if ( ! MadmapMain.noGUI() ) { |
1293 | getWindow().addRetainedByClassTab( this, classRetModel, _liveObjectsSize, description ); |
1294 | } |
1295 | } |
1296 | } |
1297 | } |
1298 | |
1299 | |
1300 | // These two string arrays should be kept in sync |
1301 | String[] _rootDescNames = { |
1302 | "Classes", |
1303 | "Threads", |
1304 | "Java stack", |
1305 | "JNI global refs" |
1306 | }; |
1307 | |
1308 | String[] _rootHprofNames = { |
1309 | "<system class>", |
1310 | "<thread>", |
1311 | "<Java stack>", |
1312 | "<JNI global ref>" |
1313 | }; |
1314 | |
1315 | /** |
1316 | * Search the class list and record the basic type array |
1317 | * classes, they will be installed into basic type array |
1318 | * objects in a later step |
1319 | */ |
1320 | void reconcileBasicTypeArrays() { |
1321 | Collection<HprofClassElement> hoc = this.getClasses().values(); |
1322 | Iterator<HprofClassElement> ihoc = hoc.iterator(); |
1323 | |
1324 | while( ihoc.hasNext() ) { |
1325 | HprofClassElement hc = ihoc.next(); |
1326 | String nm = hc.className(); |
1327 | |
1328 | if (( nm == "boolean[]" ) || (nm == "[Z")) { |
1329 | boolArrayKlass = hc; |
1330 | } else if (( nm == "byte[]" ) || (nm == "[B")) { |
1331 | byteArrayKlass = hc; |
1332 | } else if (( nm == "char[]" ) || (nm == "[C")) { |
1333 | charArrayKlass = hc; |
1334 | } else if (( nm == "short[]" ) || (nm == "[S")) { |
1335 | shortArrayKlass = hc; |
1336 | } else if (( nm == "int[]" ) || (nm == "[I")) { |
1337 | intArrayKlass = hc; |
1338 | } else if (( nm == "long[]" ) || (nm == "[J")) { |
1339 | longArrayKlass = hc; |
1340 | } else if (( nm == "float[]" ) || (nm == "[F")) { |
1341 | floatArrayKlass = hc; |
1342 | } else if (( nm == "double[]" ) || (nm == "[D")) { |
1343 | doubleArrayKlass = hc; |
1344 | } |
1345 | } |
1346 | } |
1347 | |
1348 | |
1349 | public void analyze() { |
1350 | if ( MadmapMain.verbose() ) { |
1351 | _logger.myLog( getWindow(), elapsedTime() + ": analyze: _roots_list.size() = " + this.getRoots().size() ); |
1352 | } |
1353 | |
1354 | reconcileBasicTypeArrays(); |
1355 | |
1356 | if (isBinary() && (MadmapMain.printFinalizers() == true)) { |
1357 | printFinalizers(); |
1358 | } |
1359 | |
1360 | iterativeMarking(); |
1361 | |
1362 | if ( MadmapMain.verbose() ) { |
1363 | _logger.myLog( getWindow(), elapsedTime() + ": analyze: build live table " ); |
1364 | } |
1365 | |
1366 | long build_startTime = System.currentTimeMillis(); |
1367 | buildLiveObjectsTable(); |
1368 | long build_endTime = System.currentTimeMillis(); |
1369 | if ( MadmapMain.verbose() ) { |
1370 | System.out.println( "Built live graph in " + (build_endTime - build_startTime) + "ms"); |
1371 | } |
1372 | |
1373 | if ( MadmapMain.verbose() ) { |
1374 | _logger.myLog( getWindow(), " " ); |
1375 | _logger.myLog( getWindow(), elapsedTime() + ": analyze: build live table complete " ); |
1376 | _logger.myLog( getWindow(), " " ); |
1377 | } |
1378 | |
1379 | if ( MadmapMain.runSystemGC() ) { |
1380 | _logger.myLog( getWindow(), elapsedTime() + ": analyze: System.gc() for footprint analysis " ); |
1381 | System.gc(); |
1382 | _logger.myLog( getWindow(), elapsedTime() + ": analyze: System.gc() done " ); |
1383 | } |
1384 | |
1385 | _logger.myLog( getWindow(), " " ); |
1386 | _logger.myLog( getWindow(), "Total number of types: " + ( this.getClasses().size() )); |
1387 | |
1388 | |
1389 | buildLiveObjectsHistogram(); |
1390 | if ( !isBinary() && (MadmapMain.printFinalizers() == true)) { |
1391 | printFinalizers(); |
1392 | } |
1393 | |
1394 | { |
1395 | // retained size code starts here 080524 |
1396 | resetForScanning(); |
1397 | |
1398 | int verifyRoots = getRoots().size(); |
1399 | |
1400 | for (int i = 0; i < _rootDescNames.length; i++) { |
1401 | _logger.myLog( getWindow(), "==================================================================================================== " ); |
1402 | Collections.sort(getRoots(), new RootComparator(_rootHprofNames[i])); |
1403 | assert verifyRoots == getRoots().size() : "roots sort is messed up"; |
1404 | if ( MadmapMain.runRecursiveChildrenSize() == true ) { |
1405 | sortOnRetainedSize( getRoots(), _rootDescNames[i] ); |
1406 | } else { |
1407 | iterativeSortOnRetainedSize( getRoots(), _rootDescNames[i] ); |
1408 | } |
1409 | displayRetainedSortResults( _rootDescNames[i] ); |
1410 | resetForScanning(); |
1411 | } |
1412 | } |
1413 | _logger.myLog( getWindow(), elapsedTime() + ": Analysis complete." ); |
1414 | |
1415 | } |
1416 | |
1417 | public MainWindow getWindow() { return _window; } |
1418 | |
1419 | public Madmap( String name, MainWindow theWindow ) { |
1420 | _window = theWindow; |
1421 | preinit( name ); |
1422 | } |
1423 | |
1424 | public Madmap( File inputFile, MainWindow theWindow ) { |
1425 | String name; |
1426 | _window = theWindow; |
1427 | try { |
1428 | name = inputFile.getCanonicalPath(); |
1429 | } catch (IOException e) { |
1430 | System.out.println("Something wrong with file from gui"); |
1431 | e.printStackTrace(); |
1432 | return; |
1433 | } |
1434 | preinit( name ); |
1435 | } |
1436 | |
1437 | public Madmap( String name ) { |
1438 | preinit( name ); |
1439 | } |
1440 | |
1441 | public static boolean isBinaryDump(String name) { |
1442 | FileInputStream fis = null; |
1443 | DataInputStream dis; |
1444 | BufferedInputStream bis; |
1445 | byte[] rawBytes = new byte[ 18 ]; |
1446 | byte b = -1; |
1447 | |
1448 | try { |
1449 | fis = new FileInputStream( name ); |
1450 | bis = new BufferedInputStream( fis ); |
1451 | dis = new DataInputStream( bis ); |
1452 | |
1453 | // File starts with null-terminated "JAVA PROFILE 1.0.1" |
1454 | int bytesRead = dis.read( rawBytes ); |
1455 | b = dis.readByte(); |
1456 | dis.close(); |
1457 | } catch (IOException e) { |
1458 | System. out. println ( " Exception while examining file type: " + e ); |
1459 | e.printStackTrace(); |
1460 | System.exit(0); |
1461 | } |
1462 | |
1463 | String header = new String( rawBytes ); |
1464 | if ((header.equals ( "JAVA PROFILE 1.0.1" ) || |
1465 | header.equals ( "JAVA PROFILE 1.0.2" )) && (b == 0)) { |
1466 | return true; |
1467 | } |
1468 | return false; |
1469 | |
1470 | } |
1471 | |
1472 | private void preinit( String name ) { |
1473 | if (MadmapMain.verbose()) { |
1474 | System.out.println( "Opening file: " + name ); |
1475 | } |
1476 | _fileName = name; |
1477 | _isBinary = Madmap.isBinaryDump( _fileName ); |
1478 | |
1479 | // binary dumps dont need the workaround below |
1480 | if (_isBinary) { |
1481 | return; |
1482 | } |
1483 | |
1484 | // 070307 - JDK 1.4 does not emit class |
1485 | // entries for basic type arrays, so I create a phony placeholder entry |
1486 | // to use when building up the instance size/count info |
1487 | if ( MadmapMain.jdk14_compatible() ) { |
1488 | boolArrayKlass = new HprofClassElement( (long) 999, "bool[]" ); |
1489 | byteArrayKlass = new HprofClassElement( (long) 998, "byte[]" ); |
1490 | charArrayKlass = new HprofClassElement( (long) 997, "char[]" ); |
1491 | shortArrayKlass = new HprofClassElement( (long) 996, "short[]" ); |
1492 | intArrayKlass = new HprofClassElement( (long) 995, "int[]" ); |
1493 | longArrayKlass = new HprofClassElement( (long) 994, "long[]" ); |
1494 | floatArrayKlass = new HprofClassElement( (long) 993, "float[]" ); |
1495 | doubleArrayKlass = new HprofClassElement( (long) 992, "double[]" ); |
1496 | |
1497 | _class_list.put( boolArrayKlass, boolArrayKlass ); |
1498 | _class_list.put( byteArrayKlass, byteArrayKlass ); |
1499 | _class_list.put( charArrayKlass, charArrayKlass ); |
1500 | _class_list.put( shortArrayKlass, shortArrayKlass ); |
1501 | _class_list.put( intArrayKlass, intArrayKlass ); |
1502 | _class_list.put( longArrayKlass, longArrayKlass ); |
1503 | _class_list.put( floatArrayKlass, floatArrayKlass ); |
1504 | _class_list.put( doubleArrayKlass, doubleArrayKlass ); |
1505 | } |
1506 | } |
1507 | } |