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.traffic;
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.common.IoBuffer;
30  
31  /**
32   * A default {@link MessageSizeEstimator} implementation.
33   * <p>
34   * <a href="http://martin.nobilitas.com/java/sizeof.html">Martin's Java Notes</a>
35   * was used for estimation of the size of non-{@link IoBuffer}s.  For unknown
36   * types, it inspects declaring fields of the class of the specified message.
37   * The size of unknown declaring fields are approximated to the specified
38   * <tt>averageSizePerField</tt> (default: 64).
39   * <p>
40   * All the estimated sizes of classes are cached for performance improvement.
41   * 
42   * @author The Apache MINA Project (dev@mina.apache.org)
43   * @version $Rev: 595517 $, $Date: 2007-11-15 18:31:56 -0700 (Thu, 15 Nov 2007) $
44   */
45  public class DefaultMessageSizeEstimator implements MessageSizeEstimator {
46  
47      private final ConcurrentMap<Class<?>, Integer> class2size = new ConcurrentHashMap<Class<?>, Integer>();
48      
49      public DefaultMessageSizeEstimator() {
50          class2size.put(boolean.class, 4); // Probably an integer.
51          class2size.put(byte.class, 1);
52          class2size.put(char.class, 2);
53          class2size.put(short.class, 2);
54          class2size.put(int.class, 4);
55          class2size.put(long.class, 8);
56          class2size.put(float.class, 4);
57          class2size.put(double.class, 8);
58          class2size.put(void.class, 0);
59      }
60      
61      public int estimateSize(Object message) {
62          if (message == null) {
63              return 8;
64          }
65  
66          int answer = 8 + estimateSize(message.getClass(), null);
67          
68          if (message instanceof IoBuffer) {
69              answer += ((IoBuffer) message).remaining();
70          } else if (message instanceof CharSequence) {
71              answer += ((CharSequence) message).length() << 1;
72          } else if (message instanceof Iterable) {
73              for (Object m: (Iterable<?>) message) {
74                  answer += estimateSize(m);
75              }
76          }
77          
78          return align(answer);
79      }
80      
81      private int estimateSize(Class<?> clazz, Set<Class<?>> visitedClasses) {
82          Integer objectSize = class2size.get(clazz);
83          if (objectSize != null) {
84              return objectSize;
85          }
86          
87          if (visitedClasses != null) {
88              if (visitedClasses.contains(clazz)) {
89                  return 0;
90              }
91          } else {
92              visitedClasses = new HashSet<Class<?>>();
93          }
94          
95          visitedClasses.add(clazz);
96          
97          int answer = 8; // Basic overhead.
98          for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
99              Field[] fields = c.getDeclaredFields();
100             for (Field f: fields) {
101                 if ((f.getModifiers() & Modifier.STATIC) != 0) {
102                     // Ignore static fields.
103                     continue;
104                 }
105                 
106                 answer += estimateSize(f.getType(), visitedClasses);
107             }
108         }
109             
110         visitedClasses.remove(clazz);
111         
112         // Some alignment.
113         answer = align(answer);
114         
115         // Put the final answer.
116         class2size.putIfAbsent(clazz, answer);
117         return answer;
118     }
119     
120     private static int align(int size) {
121         if (size % 8 != 0) {
122             size /= 8;
123             size ++;
124             size *= 8;
125         }
126         
127         return size;
128     }
129 }