View Javadoc

1   /*
2    *  Licensed to the Apache Software Foundation (ASF) under one
3    *  or more contributor license agreements.  See the NOTICE file
4    *  distributed with this work for additional information
5    *  regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    *  "License"); you may not use this file except in compliance
8    *  with the License.  You may obtain a copy of the License at
9    *
10   *    http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing,
13   *  software distributed under the License is distributed on an
14   *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   *  KIND, either express or implied.  See the License for the
16   *  specific language governing permissions and limitations
17   *  under the License.
18   *
19   */
20  package org.apache.mina.filter.executor;
21  
22  import java.lang.reflect.Field;
23  import java.lang.reflect.Modifier;
24  import java.util.HashSet;
25  import java.util.Set;
26  import java.util.concurrent.ConcurrentHashMap;
27  import java.util.concurrent.ConcurrentMap;
28  
29  import org.apache.mina.core.buffer.IoBuffer;
30  import org.apache.mina.core.session.IoEvent;
31  import org.apache.mina.core.write.WriteRequest;
32  
33  /**
34   * A default {@link IoEventSizeEstimator} implementation.
35   * <p>
36   * <a href="http://martin.nobilitas.com/java/sizeof.html">Martin's Java Notes</a>
37   * was used for estimation.  For unknown types, it inspects declaring fields of the
38   * class of the specified event and the parameter of the event.  The size of unknown
39   * declaring fields are approximated to the specified <tt>averageSizePerField</tt>
40   * (default: 64).
41   * <p>
42   * All the estimated sizes of classes are cached for performance improvement.
43   *
44   * @author <a href="http://mina.apache.org">Apache MINA Project</a>
45   */
46  public class DefaultIoEventSizeEstimator implements IoEventSizeEstimator {
47      /** A map containing the estimated size of each Java objects we know for */
48      private final ConcurrentMap<Class<?>, Integer> class2size = new ConcurrentHashMap<Class<?>, Integer>();
49  
50      /**
51       * Create a new instance of this class, injecting the known size of
52       * basic java types.
53       */
54      public DefaultIoEventSizeEstimator() {
55          class2size.put(boolean.class, 4); // Probably an integer.
56          class2size.put(byte.class, 1);
57          class2size.put(char.class, 2);
58          class2size.put(int.class, 4);
59          class2size.put(short.class, 2);
60          class2size.put(long.class, 8);
61          class2size.put(float.class, 4);
62          class2size.put(double.class, 8);
63          class2size.put(void.class, 0);
64      }
65  
66      /**
67       * {@inheritDoc}
68       */
69      public int estimateSize(IoEvent event) {
70          return estimateSize((Object) event) + estimateSize(event.getParameter());
71      }
72  
73      /**
74       * Estimate the size of an Object in number of bytes
75       * @param message The object to estimate
76       * @return The estimated size of the object
77       */
78      public int estimateSize(Object message) {
79          if (message == null) {
80              return 8;
81          }
82  
83          int answer = 8 + estimateSize(message.getClass(), null);
84  
85          if (message instanceof IoBuffer) {
86              answer += ((IoBuffer) message).remaining();
87          } else if (message instanceof WriteRequest) {
88              answer += estimateSize(((WriteRequest) message).getMessage());
89          } else if (message instanceof CharSequence) {
90              answer += ((CharSequence) message).length() << 1;
91          } else if (message instanceof Iterable) {
92              for (Object m : (Iterable<?>) message) {
93                  answer += estimateSize(m);
94              }
95          }
96  
97          return align(answer);
98      }
99  
100     private int estimateSize(Class<?> clazz, Set<Class<?>> visitedClasses) {
101         Integer objectSize = class2size.get(clazz);
102         
103         if (objectSize != null) {
104             return objectSize;
105         }
106 
107         if (visitedClasses != null) {
108             if (visitedClasses.contains(clazz)) {
109                 return 0;
110             }
111         } else {
112             visitedClasses = new HashSet<Class<?>>();
113         }
114 
115         visitedClasses.add(clazz);
116 
117         int answer = 8; // Basic overhead.
118         
119         for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
120             Field[] fields = c.getDeclaredFields();
121             
122             for (Field f : fields) {
123                 if ((f.getModifiers() & Modifier.STATIC) != 0) {
124                     // Ignore static fields.
125                     continue;
126                 }
127 
128                 answer += estimateSize(f.getType(), visitedClasses);
129             }
130         }
131 
132         visitedClasses.remove(clazz);
133 
134         // Some alignment.
135         answer = align(answer);
136 
137         // Put the final answer.
138         Integer tmpAnswer = class2size.putIfAbsent(clazz, answer);
139 
140         if (tmpAnswer != null) {
141             answer = tmpAnswer;
142         }
143 
144         return answer;
145     }
146 
147     private static int align(int size) {
148         if (size % 8 != 0) {
149             size /= 8;
150             size++;
151             size *= 8;
152         }
153         
154         return size;
155     }
156 }