|
|
Rafael Steil
Calcule a memória RAM que seus objetos utilizam.
The Java Specialists' Newsletter [Issue 078] - 2003-09-29
Author: Dr. Heinz M. Kabutz
Contador de Memória para Java 1.4
Antes de iniciar, preciso avisar que estou usando classes que funcionam apenas a partir do Java 1.4. Além disso, os tamanhos de memória foram alterados no JDK 1.4, portanto existem diferenças entre o 1.4 e o 1.3. Esta classe é apenas para dar uma idéia do tamanho de um objeto.
Depois da minha newsletter #29, recebi alguns e-mails dizendo que seria muito mais fácil serializar o objeto e então contar os bytes. Se eu quisesse saber o quanto de banda um objeto iria usar, esta abordagem faria sentido. Porém, minha abordagem é usada para medir memória RAM. Outra vantagem da minha abordagem é que o objeto não precisa ser serializável.
A primeira classe que temos é chamada MemorySizes e irá dizer à classe contadora de memória o quanto de memória cada elemento utiliza. Determinei isso empiricamente.
01 import java.util.*;
02
03 public class MemorySizes {
04 private final Map primitiveSizes = new IdentityHashMap() {
05 {
06 put(boolean.class, new Integer(1));
07 put(byte.class, new Integer(1));
08 put(char.class, new Integer(2));
09 put(short.class, new Integer(2));
10 put(int.class, new Integer(4));
11 put(float.class, new Integer(4));
12 put(double.class, new Integer(8));
13 put(long.class, new Integer(8));
14 }
15 };
16
17 public int getPrimitiveFieldSize(Class clazz) {
18 return ((Integer) primitiveSizes.get(clazz)).intValue();
19 }
20
21 public int getPrimitiveArrayElementSize(Class clazz) {
22 return getPrimitiveFieldSize(clazz);
23 }
24
25 public int getPointerSize() {
26 return 4;
27 }
28
29 public int getClassSize() {
30 return 8;
31 }
32 }
|
Em seguida, temos a classe que conta o tamanho da memória. Para uma explicação deste código, sugiro que você consulte a newsletter original.
001 import java.lang.reflect.*;
002 import java.util.*;
003
004 /**
005 * Esta classe pode estimar o quanto de memória um objeto utiliza. Ela é
006 * bastante precisa para o JDK 1.4.2. É baseada na newsletter #29.
007 */
008 public final class MemoryCounter {
009 private static final MemorySizes sizes = new MemorySizes();
010 private final Map visited = new IdentityHashMap();
011 private final Stack stack = new Stack();
012
013 public synchronized long estimate(Object obj) {
014 assert visited.isEmpty();
015 assert stack.isEmpty();
016 long result = _estimate(obj);
017
018 while (!stack.isEmpty()) {
019 result += _estimate(stack.pop());
020 }
021
022 visited.clear();
023 return result;
024 }
025
026 private boolean skipObject(Object obj) {
027 if (obj instanceof String) {
028 // isto não causará vazamento de memória já que
029 // Strings internos não usados serão jogados fora
030 if (obj == ((String) obj).intern()) {
031 return true;
032 }
033 }
034
035 return (obj == null)|| visited.containsKey(obj);
036 }
037
038 private long _estimate(Object obj) {
039 if (skipObject(obj))
040 return 0;
041
042 visited.put(obj, null);
043 long result = 0;
044 Class clazz = obj.getClass();
045
046 if (clazz.isArray()) {
047 return _estimateArray(obj);
048 }
049
050 while (clazz != null) {
051 Field[] fields = clazz.getDeclaredFields();
052
053 for (int i = 0; i < fields.length; i++) {
054 if (!Modifier.isStatic(fields[i].getModifiers())) {
055 if (fields[i].getType().isPrimitive()) {
056 result += sizes.getPrimitiveFieldSize(fields[i].getType());
057 }
058 else {
059 result += sizes.getPointerSize();
060 fields[i].setAccessible(true);
061
062 try {
063 Object toBeDone = fields[i].get(obj);
064
065 if (toBeDone != null) {
066 stack.add(toBeDone);
067 }
068 }
069 catch (IllegalAccessException ex) {
070 assert false;
071 }
072 }
073 }
074 }
075
076 clazz = clazz.getSuperclass();
077 }
078
079 result += sizes.getClassSize();
080 return roundUpToNearestEightBytes(result);
081 }
082
083 private long roundUpToNearestEightBytes(long result) {
084 if ((result % 8) != 0) {
085 result += 8 - (result % 8);
086 }
087
088 return result;
089 }
090
091 protected long _estimateArray(Object obj) {
092 long result = 16;
093 int length = Array.getLength(obj);
094
095 if (length != 0) {
096 ClassarrayElementClazz = obj.getClass().getComponentType();
097
098 if (arrayElementClazz.isPrimitive()) {
099 result += length * sizes.getPrimitiveArrayElementSize(arrayElementClazz);
100 }
101 else {
102 for (int i = 0; i < length; i++) {
103 result += sizes.getPointerSize() + _estimate(Array.get(obj, i));
104 }
105 }
106 }
107
108 return result;
109 }
110 }
|
Recentemente alterei este código significativamente para incluir o novo IdentityHashMap e adicionar suporte a Strings internas. Isto não teria sido possível sem um bom conjunto para testes unitários. Normalmente eu não incluo meus testes unitários nas newsletters, já que ocupam muito espaço. Entretanto, eu gostaria de saber se você tiver um JDK 1.4.x em que os resultados são diferentes. O teste unitário deu certo na minha máquina, usando JDK 1.4.2, build 1.4.2-b28.
001 import java.util.*;
002 import junit.framework.TestCase;
003 import junit.swingui.TestRunner;
004
005 public class MemoryCounterTest extends TestCase {
006 public static void main(String[] args) {
007 TestRunner.run(MemoryCounterTest.class);
008 }
009
010 private static final MemoryCounter mc = new MemoryCounter();
011
012 public MemoryCounterTest(String name) {
013 super(name);
014 }
015
016 public void testString() {
017 assertEquals(64, mc.estimate(new String("Hello World!")));
018 }
019
020 public void testIntegerToString() {
021 for (int i=0; i<1; i++) {
022 assertEquals(72, mc.estimate("" + i));
023 }
024 }
025
026 static class Entry implements Map.Entry {
027 final Object key;
028 Object value;
029 final int hash;
030 Entry next;
031
032 Entry(int h, Object k, Object v, Entry n) {
033 value = v;
034 next = n;
035 key = k;
036 hash = h;
037 }
038
039 public Object getKey() {
040 return key;
041 }
042
043 public Object getValue() {
044 return value;
045 }
046
047 public Object setValue(Object value) {
048 return value;
049 }
050 }
051
052 public void testHashMap() {
053 assertEquals(120, mc.estimate(new HashMap()));
054
055 Byte[] all = new Byte[256];
056 for (int i = -128; i < 128; i++) {
057 all[i + 128] = new Byte((byte) i);
058 }
059 assertEquals(5136, mc.estimate(all));
060
061 HashMap hm = new HashMap();
062 for (int i = -128; i < 128; i++) {
063 hm.put("" + i, new Byte((byte) i));
064 }
065 assertEquals(30776, mc.estimate(hm));
066 }
067
068 public void testVector() {
069 assertEquals(80, mc.estimate(new Vector(10)));
070 }
071
072 public void testObject() {
073 assertEquals(8, mc.estimate(new Object()));
074 }
075
076 public void testInteger() {
077 assertEquals(16, mc.estimate(new Integer(1)));
078 }
079
080 public void testCharArray() {
081 assertEquals(40, mc.estimate("Hello World!".toCharArray()));
082 }
083
084 public void testByte() {
085 assertEquals(16, mc.estimate(new Byte((byte) 10)));
086 }
087
088 public void testThreeBytes() {
089 assertEquals(16, mc.estimate(new ThreeBytes()));
090 }
091
092 public void testSixtyFourBooleans() {
093 assertEquals(72, mc.estimate(new SixtyFourBooleans()));
094 }
095
096 public void testThousandBooleansObjects() {
097 Boolean[] booleans = new Boolean[1000];
098
099 for (int i = 0; i < booleans.length; i++)
100 booleans[i] = new Boolean(true);
101
102 assertEquals(20016, mc.estimate(booleans));
103 }
104
105 public void testThousandBytes() {
106 assertEquals(1016, mc.estimate(new byte[1000]));
107 }
108
109 public void testEmptyArrayList() {
110 assertEquals(80, mc.estimate(new ArrayList()));
111 }
112
113 public void testFullArrayList() {
114 ArrayList arrayList = new ArrayList(10000);
115
116 for (int i = 0; i < 10000; i++) {
117 arrayList.add(new Object());
118 }
119
120 assertEquals(120040, mc.estimate(arrayList));
121 }
122
123 public void testFullLinkedList() {
124 LinkedList linkedList = new LinkedList();
125
126 for (int i = 0; i < 10000; i++) {
127 linkedList.add(new Object());
128 }
129
130 assertEquals(320048, mc.estimate(linkedList));
131 }
132
133 public void testComplexClass() {
134 assertEquals(48, mc.estimate(new ComplexClass()));
135 }
136
137 public void testBooleanArray() {
138 assertEquals(27, mc.estimate(new boolean[11]));
139 }
140
141 public void testShortArray() {
142 assertEquals(38, mc.estimate(new short[11]));
143 }
144
145 public void testIntArray() {
146 assertEquals(60, mc.estimate(new int[11]));
147 }
148
149 public void testFloatArray() {
150 assertEquals(60, mc.estimate(new float[11]));
151 }
152
153 public void testLongArray() {
154 assertEquals(104, mc.estimate(new long[11]));
155 }
156
157 public void testDoubleArray() {
158 assertEquals(104, mc.estimate(new double[11]));
159 }
160
161 static class ThreeBytes {
162 byte b0;
163 byte b1;
164 byte b2;
165 }
166
167 private static class ComplexClass {
168 ComplexClass cc = this;
169 boolean z;
170 byte b;
171 char c;
172 double d;
173 float f;
174 int i;
175 long l;
176 short s;
177 }
178
179 private static class SixtyFourBooleans {
180 boolean a0;
181 boolean a1;
182 boolean a2;
183 boolean a3;
184 boolean a4;
185 boolean a5;
186 boolean a6;
187 boolean a7;
188 boolean b0;
189 boolean b1;
190 boolean b2;
191 boolean b3;
192 boolean b4;
193 boolean b5;
194 boolean b6;
195 boolean b7;
196 boolean c0;
197 boolean c1;
198 boolean c2;
199 boolean c3;
200 boolean c4;
201 boolean c5;
202 boolean c6;
203 boolean c7;
204 boolean d0;
205 boolean d1;
206 boolean d2;
207 boolean d3;
208 boolean d4;
209 boolean d5;
210 boolean d6;
211 boolean d7;
212 boolean e0;
213 boolean e1;
214 boolean e2;
215 boolean e3;
216 boolean e4;
217 boolean e5;
218 boolean e6;
219 boolean e7;
220 boolean f0;
221 boolean f1;
222 boolean f2;
223 boolean f3;
224 boolean f4;
225 boolean f5;
226 boolean f6;
227 boolean f7;
228 boolean g0;
229 boolean g1;
230 boolean g2;
231 boolean g3;
232 boolean g4;
233 boolean g5;
234 boolean g6;
235 boolean g7;
236 boolean h0;
237 boolean h1;
238 boolean h2;
239 boolean h3;
240 boolean h4;
241 boolean h5;
242 boolean h6;
243 boolean h7;
244 }
245 }
|
Eu ficaria feliz de saber se você achou essa newsletter útil. Simplesmente me envie um email contando como isso pode lhe ajudar.
Abraços,
Heinz
P.S. O código original tinha vários comentários. Eu removi eles para não poluir a newsletter.
Sobre a The Java Specialists Newsletter
Este artigo é fruto de uma parceria entre o GUJ e o autor da The Java Specialists Newsletter, Dr. Heinz M. Kabutz. Originalmente publicadas em inglês e enviadas para milhares de leitores ao redor do planeta, o GUJ propô-se a realizar a tradução para o Português do conteúdo, disponibilizando assim um ótimo material para um número ainda maior de leitores.
Você encontra o documento original, assim como newsletters anteriores, no site oficial, em http://www.javaspecialists.co.za.
--------------------------------------------------------------------------------
Copyright 2000-2003 Maximum Solutions, South Africa
Reprint Rights. Copyright subsists in all the material included in this email, but you may freely share the entire newsletter with anyone you feel may be interested, and you may reprint excerpts both online and offline provided that you acknowledge the source as follows: This material from The Java(tm) Specialists' Newsletter by Maximum Solutions (South Africa). Please contact Maximum Solutions for more information.
Java and Sun are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States and other countries. Maximum Solutions is independent of Sun Microsystems, Inc.
|
|
|