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