pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / util / RamUsageEstimator.java
1 package org.apache.lucene.util;
2
3 /**
4  * Licensed to the Apache Software Foundation (ASF) under one or more
5  * contributor license agreements.  See the NOTICE file distributed with
6  * this work for additional information regarding copyright ownership.
7  * The ASF licenses this file to You under the Apache License, Version 2.0
8  * (the "License"); you may not use this file except in compliance with
9  * the License.  You may obtain a copy of the License at
10  *
11  *     http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19
20 import java.lang.reflect.*;
21 import java.text.DecimalFormat;
22 import java.util.*;
23
24 /**
25  * Estimates the size of a given Object using a given MemoryModel for primitive
26  * size information.
27  * 
28  * Resource Usage: 
29  * 
30  * Internally uses a Map to temporally hold a reference to every
31  * object seen. 
32  * 
33  * If checkInterned, all Strings checked will be interned, but those
34  * that were not already interned will be released for GC when the
35  * estimate is complete.
36  * 
37  * @lucene.internal
38  */
39 public final class RamUsageEstimator {
40
41   public final static int NUM_BYTES_SHORT = 2;
42   public final static int NUM_BYTES_INT = 4;
43   public final static int NUM_BYTES_LONG = 8;
44   public final static int NUM_BYTES_FLOAT = 4;
45   public final static int NUM_BYTES_DOUBLE = 8;
46   public final static int NUM_BYTES_CHAR = 2;
47   public final static int NUM_BYTES_OBJECT_HEADER = 8;
48   public final static int NUM_BYTES_OBJECT_REF = Constants.JRE_IS_64BIT ? 8 : 4;
49   public final static int NUM_BYTES_ARRAY_HEADER = NUM_BYTES_OBJECT_HEADER + NUM_BYTES_INT + NUM_BYTES_OBJECT_REF;
50
51   private MemoryModel memoryModel;
52
53   private final Map<Object,Object> seen;
54
55   private int refSize;
56   private int arraySize;
57   private int classSize;
58
59   private boolean checkInterned;
60
61   /**
62    * Constructs this object with an AverageGuessMemoryModel and
63    * checkInterned = true.
64    */
65   public RamUsageEstimator() {
66     this(new AverageGuessMemoryModel());
67   }
68
69   /**
70    * @param checkInterned check if Strings are interned and don't add to size
71    * if they are. Defaults to true but if you know the objects you are checking
72    * won't likely contain many interned Strings, it will be faster to turn off
73    * intern checking.
74    */
75   public RamUsageEstimator(boolean checkInterned) {
76     this(new AverageGuessMemoryModel(), checkInterned);
77   }
78
79   /**
80    * @param memoryModel MemoryModel to use for primitive object sizes.
81    */
82   public RamUsageEstimator(MemoryModel memoryModel) {
83     this(memoryModel, true);
84   }
85
86   /**
87    * @param memoryModel MemoryModel to use for primitive object sizes.
88    * @param checkInterned check if Strings are interned and don't add to size
89    * if they are. Defaults to true but if you know the objects you are checking
90    * won't likely contain many interned Strings, it will be faster to turn off
91    * intern checking.
92    */
93   public RamUsageEstimator(MemoryModel memoryModel, boolean checkInterned) {
94     this.memoryModel = memoryModel;
95     this.checkInterned = checkInterned;
96     // Use Map rather than Set so that we can use an IdentityHashMap - not
97     // seeing an IdentityHashSet
98     seen = new IdentityHashMap<Object,Object>(64);
99     this.refSize = memoryModel.getReferenceSize();
100     this.arraySize = memoryModel.getArraySize();
101     this.classSize = memoryModel.getClassSize();
102   }
103
104   public long estimateRamUsage(Object obj) {
105     long size = size(obj);
106     seen.clear();
107     return size;
108   }
109
110   private long size(Object obj) {
111     if (obj == null) {
112       return 0;
113     }
114     // interned not part of this object
115     if (checkInterned && obj instanceof String
116         && obj == ((String) obj).intern()) { // interned string will be eligible
117                                              // for GC on
118                                              // estimateRamUsage(Object) return
119       return 0;
120     }
121
122     // skip if we have seen before
123     if (seen.containsKey(obj)) {
124       return 0;
125     }
126
127     // add to seen
128     seen.put(obj, null);
129
130     Class<?> clazz = obj.getClass();
131     if (clazz.isArray()) {
132       return sizeOfArray(obj);
133     }
134
135     long size = 0;
136
137     // walk type hierarchy
138     while (clazz != null) {
139       Field[] fields = clazz.getDeclaredFields();
140       for (int i = 0; i < fields.length; i++) {
141         if (Modifier.isStatic(fields[i].getModifiers())) {
142           continue;
143         }
144
145         if (fields[i].getType().isPrimitive()) {
146           size += memoryModel.getPrimitiveSize(fields[i].getType());
147         } else {
148           size += refSize;
149           fields[i].setAccessible(true);
150           try {
151             Object value = fields[i].get(obj);
152             if (value != null) {
153               size += size(value);
154             }
155           } catch (IllegalAccessException ex) {
156             // ignore for now?
157           }
158         }
159
160       }
161       clazz = clazz.getSuperclass();
162     }
163     size += classSize;
164     return size;
165   }
166
167   private long sizeOfArray(Object obj) {
168     int len = Array.getLength(obj);
169     if (len == 0) {
170       return 0;
171     }
172     long size = arraySize;
173     Class<?> arrayElementClazz = obj.getClass().getComponentType();
174     if (arrayElementClazz.isPrimitive()) {
175       size += len * memoryModel.getPrimitiveSize(arrayElementClazz);
176     } else {
177       for (int i = 0; i < len; i++) {
178         size += refSize + size(Array.get(obj, i));
179       }
180     }
181
182     return size;
183   }
184
185   private static final long ONE_KB = 1024;
186   private static final long ONE_MB = ONE_KB * ONE_KB;
187   private static final long ONE_GB = ONE_KB * ONE_MB;
188
189   /**
190    * Return good default units based on byte size.
191    */
192   public static String humanReadableUnits(long bytes, DecimalFormat df) {
193     String newSizeAndUnits;
194
195     if (bytes / ONE_GB > 0) {
196       newSizeAndUnits = String.valueOf(df.format((float) bytes / ONE_GB))
197           + " GB";
198     } else if (bytes / ONE_MB > 0) {
199       newSizeAndUnits = String.valueOf(df.format((float) bytes / ONE_MB))
200           + " MB";
201     } else if (bytes / ONE_KB > 0) {
202       newSizeAndUnits = String.valueOf(df.format((float) bytes / ONE_KB))
203           + " KB";
204     } else {
205       newSizeAndUnits = String.valueOf(bytes) + " bytes";
206     }
207
208     return newSizeAndUnits;
209   }
210 }