View Javadoc

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 }