View Javadoc

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