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 
189     for ( ; null != cl; cl = cl.getSuperclass()) {
190       Field[] field = cl.getDeclaredFields();
191       if (null != field) {
192         for (Field aField : field) {
193           if (!Modifier.isStatic(aField.getModifiers())) {
194             Class fieldClass = aField.getType();
195             if (fieldClass.isArray()) {
196               arrays++;
197               references++;
198             } else if (!fieldClass.isPrimitive()) {
199               references++;
200             } else {// Is simple primitive
201               String name = fieldClass.getName();
202 
203               if (name.equals("int") || name.equals("I"))
204                 primitives += Bytes.SIZEOF_INT;
205               else if (name.equals("long") || name.equals("J"))
206                 primitives += Bytes.SIZEOF_LONG;
207               else if (name.equals("boolean") || name.equals("Z"))
208                 primitives += Bytes.SIZEOF_BOOLEAN;
209               else if (name.equals("short") || name.equals("S"))
210                 primitives += Bytes.SIZEOF_SHORT;
211               else if (name.equals("byte") || name.equals("B"))
212                 primitives += Bytes.SIZEOF_BYTE;
213               else if (name.equals("char") || name.equals("C"))
214                 primitives += Bytes.SIZEOF_CHAR;
215               else if (name.equals("float") || name.equals("F"))
216                 primitives += Bytes.SIZEOF_FLOAT;
217               else if (name.equals("double") || name.equals("D"))
218                 primitives += Bytes.SIZEOF_DOUBLE;
219             }
220             if (debug) {
221               if (LOG.isDebugEnabled()) {
222                 // Write out region name as string and its encoded name.
223                 LOG.debug(aField.getName() + "\n\t" + aField.getType());
224               }
225             }
226           }
227         }
228       }
229     }
230     return new int [] {primitives, arrays, references};
231   }
232 
233   /**
234    * Estimate the static space taken up by a class instance given the
235    * coefficients returned by getSizeCoefficients.
236    *
237    * @param coeff the coefficients
238    *
239    * @param debug debug flag
240    * @return the size estimate, in bytes
241    */
242   private static long estimateBaseFromCoefficients(int [] coeff, boolean debug) {
243     long size = coeff[0] + align(coeff[1]*ARRAY) + coeff[2]*REFERENCE;
244 
245     // Round up to a multiple of 8
246     size = align(size);
247     if(debug) {
248       if (LOG.isDebugEnabled()) {
249         // Write out region name as string and its encoded name.
250         LOG.debug("Primitives " + coeff[0] + ", arrays " + coeff[1] +
251             ", references(includes " + nrOfRefsPerObj +
252             " for object overhead) " + coeff[2] + ", refSize " + REFERENCE +
253             ", size " + size);
254       }
255     }
256     return size;
257   }
258 
259   /**
260    * Estimate the static space taken up by the fields of a class. This includes
261    * the space taken up by by references (the pointer) but not by the referenced
262    * object. So the estimated size of an array field does not depend on the size
263    * of the array. Similarly the size of an object (reference) field does not
264    * depend on the object.
265    *
266    * @param cl class
267    * @param debug debug flag
268    * @return the size estimate in bytes.
269    */
270   @SuppressWarnings("unchecked")
271   public static long estimateBase(Class cl, boolean debug) {
272     return estimateBaseFromCoefficients( getSizeCoefficients(cl, debug), debug);
273   }
274 
275   /**
276    * Aligns a number to 8.
277    * @param num number to align to 8
278    * @return smallest number >= input that is a multiple of 8
279    */
280   public static int align(int num) {
281     return (int)(align((long)num));
282   }
283 
284   /**
285    * Aligns a number to 8.
286    * @param num number to align to 8
287    * @return smallest number >= input that is a multiple of 8
288    */
289   public static long align(long num) {
290     //The 7 comes from that the alignSize is 8 which is the number of bytes
291     //stored and sent together
292     return  ((num + 7) >> 3) << 3;
293   }
294 
295 }
296