View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  
21  package org.apache.hadoop.hbase.util;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  
26  import java.lang.reflect.Field;
27  import java.lang.reflect.Modifier;
28  import java.util.Properties;
29  
30  /**
31   * Class for determining the "size" of a class, an attempt to calculate the
32   * actual bytes that an object of this class will occupy in memory
33   *
34   * The core of this class is taken from the Derby project
35   */
36  public class ClassSize {
37    static final Log LOG = LogFactory.getLog(ClassSize.class);
38  
39    private static int nrOfRefsPerObj = 2;
40  
41    /** Array overhead */
42    public static final int ARRAY;
43  
44    /** Overhead for ArrayList(0) */
45    public static final int ARRAYLIST;
46  
47    /** Overhead for ByteBuffer */
48    public static final int BYTE_BUFFER;
49  
50    /** Overhead for an Integer */
51    public static final int INTEGER;
52  
53    /** Overhead for entry in map */
54    public static final int MAP_ENTRY;
55  
56    /** Object overhead is minimum 2 * reference size (8 bytes on 64-bit) */
57    public static final int OBJECT;
58  
59    /** Reference size is 8 bytes on 64-bit, 4 bytes on 32-bit */
60    public static final int REFERENCE;
61  
62    /** String overhead */
63    public static final int STRING;
64  
65    /** Overhead for TreeMap */
66    public static final int TREEMAP;
67  
68    /** Overhead for ConcurrentHashMap */
69    public static final int CONCURRENT_HASHMAP;
70  
71    /** Overhead for ConcurrentHashMap.Entry */
72    public static final int CONCURRENT_HASHMAP_ENTRY;
73  
74    /** Overhead for ConcurrentHashMap.Segment */
75    public static final int CONCURRENT_HASHMAP_SEGMENT;
76  
77    /** Overhead for ConcurrentSkipListMap */
78    public static final int CONCURRENT_SKIPLISTMAP;
79  
80    /** Overhead for ConcurrentSkipListMap Entry */
81    public static final int CONCURRENT_SKIPLISTMAP_ENTRY;
82  
83    /** Overhead for ReentrantReadWriteLock */
84    public static final int REENTRANT_LOCK;
85  
86    /** Overhead for AtomicLong */
87    public static final int ATOMIC_LONG;
88  
89    /** Overhead for AtomicInteger */
90    public static final int ATOMIC_INTEGER;
91  
92    /** Overhead for AtomicBoolean */
93    public static final int ATOMIC_BOOLEAN;
94  
95    /** Overhead for CopyOnWriteArraySet */
96    public static final int COPYONWRITE_ARRAYSET;
97  
98    /** Overhead for CopyOnWriteArrayList */
99    public static final int COPYONWRITE_ARRAYLIST;
100 
101   private static final String THIRTY_TWO = "32";
102 
103   /**
104    * Method for reading the arc settings and setting overheads according
105    * to 32-bit or 64-bit architecture.
106    */
107   static {
108     // Figure out whether this is a 32 or 64 bit machine.
109     Properties sysProps = System.getProperties();
110     String arcModel = sysProps.getProperty("sun.arch.data.model");
111 
112     //Default value is set to 8, covering the case when arcModel is unknown
113     if (arcModel.equals(THIRTY_TWO)) {
114       REFERENCE = 4;
115     } else {
116       REFERENCE = 8;
117     }
118 
119     OBJECT = 2 * REFERENCE;
120 
121     ARRAY = align(3 * REFERENCE);
122 
123     ARRAYLIST = align(OBJECT + align(REFERENCE) + align(ARRAY) +
124         (2 * Bytes.SIZEOF_INT));
125 
126     //noinspection PointlessArithmeticExpression
127     BYTE_BUFFER = align(OBJECT + align(REFERENCE) + align(ARRAY) +
128         (5 * Bytes.SIZEOF_INT) +
129         (3 * Bytes.SIZEOF_BOOLEAN) + Bytes.SIZEOF_LONG);
130 
131     INTEGER = align(OBJECT + Bytes.SIZEOF_INT);
132 
133     MAP_ENTRY = align(OBJECT + 5 * REFERENCE + Bytes.SIZEOF_BOOLEAN);
134 
135     TREEMAP = align(OBJECT + (2 * Bytes.SIZEOF_INT) + align(7 * REFERENCE));
136 
137     STRING = align(OBJECT + ARRAY + REFERENCE + 3 * Bytes.SIZEOF_INT);
138 
139     CONCURRENT_HASHMAP = align((2 * Bytes.SIZEOF_INT) + ARRAY +
140         (6 * REFERENCE) + OBJECT);
141 
142     CONCURRENT_HASHMAP_ENTRY = align(REFERENCE + OBJECT + (3 * REFERENCE) +
143         (2 * Bytes.SIZEOF_INT));
144 
145     CONCURRENT_HASHMAP_SEGMENT = align(REFERENCE + OBJECT +
146         (3 * Bytes.SIZEOF_INT) + Bytes.SIZEOF_FLOAT + ARRAY);
147 
148     CONCURRENT_SKIPLISTMAP = align(Bytes.SIZEOF_INT + OBJECT + (8 * REFERENCE));
149 
150     CONCURRENT_SKIPLISTMAP_ENTRY = align(
151         align(OBJECT + (3 * REFERENCE)) + /* one node per entry */
152         align((OBJECT + (3 * REFERENCE))/2)); /* one index per two entries */
153 
154     REENTRANT_LOCK = align(OBJECT + (3 * REFERENCE));
155 
156     ATOMIC_LONG = align(OBJECT + Bytes.SIZEOF_LONG);
157 
158     ATOMIC_INTEGER = align(OBJECT + Bytes.SIZEOF_INT);
159 
160     ATOMIC_BOOLEAN = align(OBJECT + Bytes.SIZEOF_BOOLEAN);
161 
162     COPYONWRITE_ARRAYSET = align(OBJECT + REFERENCE);
163 
164     COPYONWRITE_ARRAYLIST = align(OBJECT + (2 * REFERENCE) + ARRAY);
165   }
166 
167   /**
168    * The estimate of the size of a class instance depends on whether the JVM
169    * uses 32 or 64 bit addresses, that is it depends on the size of an object
170    * reference. It is a linear function of the size of a reference, e.g.
171    * 24 + 5*r where r is the size of a reference (usually 4 or 8 bytes).
172    *
173    * This method returns the coefficients of the linear function, e.g. {24, 5}
174    * in the above example.
175    *
176    * @param cl A class whose instance size is to be estimated
177    * @param debug debug flag
178    * @return an array of 3 integers. The first integer is the size of the
179    * primitives, the second the number of arrays and the third the number of
180    * references.
181    */
182   @SuppressWarnings("unchecked")
183   private static int [] getSizeCoefficients(Class cl, boolean debug) {
184     int primitives = 0;
185     int arrays = 0;
186     //The number of references that a new object takes
187     int references = nrOfRefsPerObj;
188     int index = 0;
189 
190     for ( ; null != cl; cl = cl.getSuperclass()) {
191       Field[] field = cl.getDeclaredFields();
192       if (null != field) {
193         for (Field aField : field) {
194           if (Modifier.isStatic(aField.getModifiers())) continue;
195           Class fieldClass = aField.getType();
196           if (fieldClass.isArray()) {
197             arrays++;
198             references++;
199           } else if (!fieldClass.isPrimitive()) {
200             references++;
201           } else {// Is simple primitive
202             String name = fieldClass.getName();
203 
204             if (name.equals("int") || name.equals("I"))
205               primitives += Bytes.SIZEOF_INT;
206             else if (name.equals("long") || name.equals("J"))
207               primitives += Bytes.SIZEOF_LONG;
208             else if (name.equals("boolean") || name.equals("Z"))
209               primitives += Bytes.SIZEOF_BOOLEAN;
210             else if (name.equals("short") || name.equals("S"))
211               primitives += Bytes.SIZEOF_SHORT;
212             else if (name.equals("byte") || name.equals("B"))
213               primitives += Bytes.SIZEOF_BYTE;
214             else if (name.equals("char") || name.equals("C"))
215               primitives += Bytes.SIZEOF_CHAR;
216             else if (name.equals("float") || name.equals("F"))
217               primitives += Bytes.SIZEOF_FLOAT;
218             else if (name.equals("double") || name.equals("D"))
219               primitives += Bytes.SIZEOF_DOUBLE;
220           }
221           if (debug) {
222             if (LOG.isDebugEnabled()) {
223               // Write out region name as string and its encoded name.
224               LOG.debug("" + index + " " + aField.getName() + " " + aField.getType());
225             }
226           }
227           index++;
228         }
229       }
230     }
231     return new int [] {primitives, arrays, references};
232   }
233 
234   /**
235    * Estimate the static space taken up by a class instance given the
236    * coefficients returned by getSizeCoefficients.
237    *
238    * @param coeff the coefficients
239    *
240    * @param debug debug flag
241    * @return the size estimate, in bytes
242    */
243   private static long estimateBaseFromCoefficients(int [] coeff, boolean debug) {
244     long prealign_size = coeff[0] + align(coeff[1] * ARRAY) + coeff[2] * REFERENCE;
245 
246     // Round up to a multiple of 8
247     long size = align(prealign_size);
248     if(debug) {
249       if (LOG.isDebugEnabled()) {
250         // Write out region name as string and its encoded name.
251         LOG.debug("Primitives=" + coeff[0] + ", arrays=" + coeff[1] +
252             ", references(includes " + nrOfRefsPerObj +
253             " for object overhead)=" + coeff[2] + ", refSize " + REFERENCE +
254             ", size=" + size + ", prealign_size=" + prealign_size);
255       }
256     }
257     return size;
258   }
259 
260   /**
261    * Estimate the static space taken up by the fields of a class. This includes
262    * the space taken up by by references (the pointer) but not by the referenced
263    * object. So the estimated size of an array field does not depend on the size
264    * of the array. Similarly the size of an object (reference) field does not
265    * depend on the object.
266    *
267    * @param cl class
268    * @param debug debug flag
269    * @return the size estimate in bytes.
270    */
271   @SuppressWarnings("unchecked")
272   public static long estimateBase(Class cl, boolean debug) {
273     return estimateBaseFromCoefficients( getSizeCoefficients(cl, debug), debug);
274   }
275 
276   /**
277    * Aligns a number to 8.
278    * @param num number to align to 8
279    * @return smallest number >= input that is a multiple of 8
280    */
281   public static int align(int num) {
282     return (int)(align((long)num));
283   }
284 
285   /**
286    * Aligns a number to 8.
287    * @param num number to align to 8
288    * @return smallest number >= input that is a multiple of 8
289    */
290   public static long align(long num) {
291     //The 7 comes from that the alignSize is 8 which is the number of bytes
292     //stored and sent together
293     return  ((num + 7) >> 3) << 3;
294   }
295 }
296