View Javadoc

1   /***
2    *  Copyright 2003-2010 Terracotta, Inc.
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  
17  package net.sf.ehcache.pool.sizeof;
18  
19  import java.lang.reflect.Array;
20  import java.lang.reflect.Field;
21  import java.lang.reflect.Modifier;
22  import java.util.Stack;
23  
24  import net.sf.ehcache.pool.sizeof.filter.PassThroughFilter;
25  import net.sf.ehcache.pool.sizeof.filter.SizeOfFilter;
26  
27  import static net.sf.ehcache.pool.sizeof.JvmInformation.MINIMUM_OBJECT_SIZE;
28  import static net.sf.ehcache.pool.sizeof.JvmInformation.OBJECT_ALIGNMENT;
29  import static net.sf.ehcache.pool.sizeof.JvmInformation.POINTER_SIZE;
30  
31  /***
32   * SizeOf that uses reflection to measure on heap size of object graphs
33   *
34   * @author Alex Snaps
35   * @author Chris Dennis
36   */
37  public class ReflectionSizeOf extends SizeOf {
38  
39      /***
40       * Builds a new SizeOf that will not filter fields and will cache reflected fields
41       * @see #ReflectionSizeOf(net.sf.ehcache.pool.sizeof.filter.SizeOfFilter, boolean)
42       */
43      public ReflectionSizeOf() {
44          this(new PassThroughFilter());
45      }
46  
47      /***
48       * Builds a new SizeOf that will filter fields and will cache reflected fields
49       * @param fieldFilter The filter to apply
50       * @see #ReflectionSizeOf(net.sf.ehcache.pool.sizeof.filter.SizeOfFilter, boolean)
51       * @see SizeOfFilter
52       */
53      public ReflectionSizeOf(SizeOfFilter fieldFilter) {
54          this(fieldFilter, true);
55      }
56  
57      /***
58       * Builds a new SizeOf that will filter fields
59       * @param fieldFilter The filter to apply
60       * @param caching Whether to cache reflected fields
61       * @see SizeOfFilter
62       */
63      public ReflectionSizeOf(SizeOfFilter fieldFilter, boolean caching) {
64          super(fieldFilter, caching);
65      }
66  
67      /***
68       * {@inheritDoc}
69       */
70      @Override
71      protected long measureSizeOf(Object obj) {
72          if (obj == null) {
73              return 0;
74          }
75  
76          Class<?> aClass = obj.getClass();
77          if (aClass.isArray()) {
78              return guessArraySize(obj);
79          } else {
80              long size = PrimitiveType.CLASS.getSize();
81  
82              Stack<Class<?>> classStack = new Stack<Class<?>>();
83              for (Class<?> klazz = aClass; klazz != null; klazz = klazz.getSuperclass()) {
84                  classStack.push(klazz);
85              }
86  
87              while (!classStack.isEmpty()) {
88                  Class<?> klazz = classStack.pop();
89  
90                  //assuming default class layout
91                  int oops = 0;
92                  int doubles = 0;
93                  int words = 0;
94                  int shorts = 0;
95                  int bytes = 0;
96                  for (Field f : klazz.getDeclaredFields()) {
97                      if (Modifier.isStatic(f.getModifiers())) {
98                          continue;
99                      }
100                     if (f.getType().isPrimitive()) {
101                         switch (PrimitiveType.forType(f.getType())) {
102                             case BOOLEAN:
103                             case BYTE:
104                                 bytes++;
105                                 break;
106                             case SHORT:
107                             case CHAR:
108                                 shorts++;
109                                 break;
110                             case INT:
111                             case FLOAT:
112                                 words++;
113                                 break;
114                             case DOUBLE:
115                             case LONG:
116                                 doubles++;
117                                 break;
118                             default:
119                                 throw new AssertionError();
120                         }
121                     } else {
122                         oops++;
123                     }
124                 }
125                 if (doubles > 0 && (size % PrimitiveType.LONG.getSize()) != 0) {
126                     long length = PrimitiveType.LONG.getSize() - (size % PrimitiveType.LONG.getSize());
127                     size += PrimitiveType.LONG.getSize() - (size % PrimitiveType.LONG.getSize());
128 
129                     while (length >= PrimitiveType.INT.getSize() && words > 0) {
130                         length -= PrimitiveType.INT.getSize();
131                         words--;
132                     }
133                     while (length >= PrimitiveType.SHORT.getSize() && shorts > 0) {
134                         length -= PrimitiveType.SHORT.getSize();
135                         shorts--;
136                     }
137                     while (length >= PrimitiveType.BYTE.getSize() && bytes > 0) {
138                         length -= PrimitiveType.BYTE.getSize();
139                         bytes--;
140                     }
141                     while (length >= PrimitiveType.getReferenceSize() && oops > 0) {
142                         length -= PrimitiveType.getReferenceSize();
143                         oops--;
144                     }
145                 }
146                 size += PrimitiveType.DOUBLE.getSize() * doubles;
147                 size += PrimitiveType.INT.getSize() * words;
148                 size += PrimitiveType.SHORT.getSize() * shorts;
149                 size += PrimitiveType.BYTE.getSize() * bytes;
150 
151                 if (oops > 0) {
152                     if ((size % PrimitiveType.getReferenceSize()) != 0) {
153                         size += PrimitiveType.getReferenceSize() - (size % PrimitiveType.getReferenceSize());
154                     }
155                     size += oops * PrimitiveType.getReferenceSize();
156                 }
157 
158                 if ((doubles + words + shorts + bytes + oops) > 0 && (size % POINTER_SIZE) != 0) {
159                     size += JvmInformation.POINTER_SIZE - (size % POINTER_SIZE);
160                 }
161             }
162             if ((size % OBJECT_ALIGNMENT) != 0) {
163                 size += OBJECT_ALIGNMENT - (size % OBJECT_ALIGNMENT);
164             }
165             return Math.max(size, MINIMUM_OBJECT_SIZE);
166         }
167     }
168 
169     private long guessArraySize(Object obj) {
170         long size = PrimitiveType.getArraySize();
171         int length = Array.getLength(obj);
172         if (length != 0) {
173             Class<?> arrayElementClazz = obj.getClass().getComponentType();
174             if (arrayElementClazz.isPrimitive()) {
175                 size += length * PrimitiveType.forType(arrayElementClazz).getSize();
176             } else {
177                 size += length * PrimitiveType.getReferenceSize();
178             }
179         }
180         if ((size % OBJECT_ALIGNMENT) != 0) {
181             size += OBJECT_ALIGNMENT - (size % OBJECT_ALIGNMENT);
182         }
183         return Math.max(size, MINIMUM_OBJECT_SIZE);
184     }
185 }