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