001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 * 019 */ 020package org.apache.mina.filter.executor; 021 022import java.lang.reflect.Field; 023import java.lang.reflect.Modifier; 024import java.util.HashSet; 025import java.util.Set; 026import java.util.concurrent.ConcurrentHashMap; 027import java.util.concurrent.ConcurrentMap; 028 029import org.apache.mina.core.buffer.IoBuffer; 030import org.apache.mina.core.session.IoEvent; 031import org.apache.mina.core.write.WriteRequest; 032 033/** 034 * A default {@link IoEventSizeEstimator} implementation. 035 * <p> 036 * <a href="http://martin.nobilitas.com/java/sizeof.html">Martin's Java Notes</a> 037 * was used for estimation. For unknown types, it inspects declaring fields of the 038 * class of the specified event and the parameter of the event. The size of unknown 039 * declaring fields are approximated to the specified <tt>averageSizePerField</tt> 040 * (default: 64). 041 * <p> 042 * All the estimated sizes of classes are cached for performance improvement. 043 * 044 * @author <a href="http://mina.apache.org">Apache MINA Project</a> 045 */ 046public class DefaultIoEventSizeEstimator implements IoEventSizeEstimator { 047 /** A map containing the estimated size of each Java objects we know for */ 048 private final ConcurrentMap<Class<?>, Integer> class2size = new ConcurrentHashMap<Class<?>, Integer>(); 049 050 /** 051 * Create a new instance of this class, injecting the known size of 052 * basic java types. 053 */ 054 public DefaultIoEventSizeEstimator() { 055 class2size.put(boolean.class, 4); // Probably an integer. 056 class2size.put(byte.class, 1); 057 class2size.put(char.class, 2); 058 class2size.put(int.class, 4); 059 class2size.put(short.class, 2); 060 class2size.put(long.class, 8); 061 class2size.put(float.class, 4); 062 class2size.put(double.class, 8); 063 class2size.put(void.class, 0); 064 } 065 066 /** 067 * {@inheritDoc} 068 */ 069 public int estimateSize(IoEvent event) { 070 return estimateSize((Object) event) + estimateSize(event.getParameter()); 071 } 072 073 /** 074 * Estimate the size of an Object in number of bytes 075 * @param message The object to estimate 076 * @return The estimated size of the object 077 */ 078 public int estimateSize(Object message) { 079 if (message == null) { 080 return 8; 081 } 082 083 int answer = 8 + estimateSize(message.getClass(), null); 084 085 if (message instanceof IoBuffer) { 086 answer += ((IoBuffer) message).remaining(); 087 } else if (message instanceof WriteRequest) { 088 answer += estimateSize(((WriteRequest) message).getMessage()); 089 } else if (message instanceof CharSequence) { 090 answer += ((CharSequence) message).length() << 1; 091 } else if (message instanceof Iterable) { 092 for (Object m : (Iterable<?>) message) { 093 answer += estimateSize(m); 094 } 095 } 096 097 return align(answer); 098 } 099 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}