added summary for the object list
[jnode:svn-mirror.git] / core / src / core / org / jnode / vm / memmgr / def / DefHeapStatistics.java
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2003-2010 JNode.org
5  *
6  * This library is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published
8  * by the Free Software Foundation; either version 2.1 of the License, or
9  * (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful, but 
12  * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13  * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
14  * License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this library; If not, write to the Free Software Foundation, Inc., 
18  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19  */
20  
21 package org.jnode.vm.memmgr.def;
22
23 import java.io.IOException;
24 import java.util.TreeMap;
25
26 import org.jnode.util.NumberUtils;
27 import org.jnode.vm.facade.HeapStatistics;
28 import org.jnode.vm.facade.NoObjectFilter;
29 import org.jnode.vm.facade.ObjectFilter;
30 import org.jnode.vm.objects.VmSystemObject;
31
32 /**
33  * @author Martin Husted Hartvig (hagar@jnode.org)
34  */
35
36 final class DefHeapStatistics extends VmSystemObject implements HeapStatistics {
37
38     private int minInstanceCount = 0;
39     private long minTotalSize = 0;
40     private ObjectFilter objectFilter = NoObjectFilter.INSTANCE;
41     private final TreeMap<String, HeapCounter> countData = new TreeMap<String, HeapCounter>();
42
43     private static final char NEWLINE = '\n';
44     private static final String USAGE = " memory usage=";
45     private static final String NO_MATCHING_OBJECT = "No object is matching criteria";
46     private static final String SUMMARY = "Summary : ";
47     private static final String CLASSES = " classe(s) ";
48     private static final String INSTANCES = " instances(s) ";
49     
50     public boolean contains(String classname) {
51         // If we don't accept this class, we pretend to have it already to (maybe) avoid unnecessary work
52         // and memory allocation (we also hope to avoid a call to add(String, int)).
53         return !objectFilter.accept(classname) || countData.containsKey(classname);
54     }
55
56     public void add(String className, int size) {
57         if (objectFilter.accept(className)) {           
58                 HeapCounter count = (HeapCounter) countData.get(className);
59         
60                 if (count == null) {
61                     count = new HeapCounter(className, size);
62                     countData.put(className, count);
63                 }
64         
65                 count.inc();
66         }
67     }
68
69     /**
70      * Sets the minimum number of instances a class must have before
71      * it is listed in toString.
72      *
73      * @param count
74      */
75     public void setMinimumInstanceCount(int count) {
76         this.minInstanceCount = count;
77     }
78
79     /**
80      * Sets the minimum bytes of occupied memory by all instances of a class
81      * before it is listed in toString.
82      *
83      * @param bytes
84      */
85     public void setMinimumTotalSize(long bytes) {
86         this.minTotalSize = bytes;
87     }
88     
89     /**
90      * {@inheritDoc}
91      */
92     @Override
93     public void setObjectFilter(ObjectFilter objectFilter) {
94         this.objectFilter = (objectFilter == null) ? NoObjectFilter.INSTANCE : objectFilter;
95     }
96     
97     /**
98      * {@inheritDoc}
99      * @throws IOException 
100      */
101     public void writeTo(Appendable a) throws IOException {
102         boolean first = true;
103
104         if (countData.isEmpty()) {
105             a.append(NO_MATCHING_OBJECT);
106         } else {
107             int nbClasses = 0;
108             int nbInstances = 0;
109             int totalSize = 0;
110             
111             for (HeapCounter c : countData.values()) {
112                 if ((c.getInstanceCount() >= minInstanceCount) && (c.getTotalSize() >= minTotalSize)) {
113                     if (first) {
114                         first = false;
115                     } else {
116                         a.append(NEWLINE);
117                     }
118                     c.append(a);
119                     
120                     nbClasses++;
121                     nbInstances += c.getInstanceCount();
122                     totalSize += c.getTotalSize();
123                 }
124             }
125             
126             if (nbClasses == 0) {
127                 a.append(NO_MATCHING_OBJECT);                
128             } else {
129                 a.append(NEWLINE);
130                 a.append(SUMMARY).append(Integer.toString(nbClasses)).append(CLASSES);
131                 a.append(Integer.toString(nbInstances)).append(INSTANCES);
132                 appendUsage(a, totalSize);
133             }
134         }
135         a.append(NEWLINE);
136     }
137     
138     private static void appendUsage(Appendable a, long size) throws IOException {
139         a.append(USAGE);
140         if (size >= 1024) {
141             a.append(NumberUtils.toBinaryByte(size)).append(" (");
142             a.append(Long.toString(size)).append("b)");
143         } else {
144             a.append(Long.toString(size)).append('b');
145         }
146
147     }
148     
149     /**
150      * {@inheritDoc}
151      */
152     public String toString() {
153         final StringBuilder sb = new StringBuilder();
154         try {
155             writeTo(sb);
156         } catch (IOException e) {
157             // normally, it will never happen 
158             throw new RuntimeException(e);
159         }
160         return sb.toString();
161     }
162
163     static final class HeapCounter {
164
165         private final String name;
166         private int instanceCount;
167         private int objectSize = 0;
168
169         public HeapCounter(String objectName, int objectSize) {
170             this.name = objectName;
171             this.objectSize = objectSize;
172             this.instanceCount = 0;
173         }
174
175         public void inc() {
176             instanceCount++;
177         }
178
179         public int getInstanceCount() {
180             return instanceCount;
181         }
182
183         public int getObjectSize() {
184             return objectSize;
185         }
186
187         public long getTotalSize() {
188             return objectSize * (long) instanceCount;
189         }
190
191         public void append(Appendable a) throws IOException {
192             a.append(name);
193             a.append("  #");
194             a.append(Integer.toString(instanceCount));
195
196             if (objectSize != 0) {
197                 appendUsage(a, getTotalSize());
198             }
199         }
200     }
201 }