零、快速入门
1. 每个java对象在内存中映射一个C++对象,C++对象只存储java对象头信息(mark和klass),C++头对象之后紧跟着java成员变量
2. java对象内存布局,主要是针对C++头对象之后的内存区域做编排
3. 常见的内存布局方式,是按照成员变量定义顺序依次分配内存地址(例如C++),但因为存在内存对齐机制,顺序分配会引入不少空白区域
4. hotspot虚拟机不使用变量声明顺序确定内存地址顺序,而是将相同类型的变量连续分布,同时长类型(double/long)在前,短类型(byte)靠后,从而减少内存对齐造成的空间浪费
一、概述
基于hotspot源码解释java对象内存布局。
java语言开发者借助JVM管理对象,向下屏蔽了底层内存使用细节,还具有强大gc的能力。但作为使用者,闲暇之余不禁在想,java对象在内存中具体是怎样的。工作中也遇到需要预估队列的内存开销以决定队列长度的情况,此时需要对java对象的内存占用情况有所了解。为此写下这篇关于java对象内存布局的文章,文章结构如下:
二、对象内存布局
1、netty里计算对象内存开销的逻辑
下面的代码摘自netty,用于估算对象内存开销。大致思路没有问题,但和底层实际内存布局还是有区别。其实从java语言的角度很难快速准确的计算对象内存开销,因为对象的内存布局受jvm实现、jvm版本、还有jvm启动参数的影响。
static {
class2size.put(boolean.class, 4); // Probably an integer.
class2size.put(byte.class, 1);
class2size.put(char.class, 2);
class2size.put(int.class, 4);
class2size.put(short.class, 2);
class2size.put(long.class, 8);
class2size.put(float.class, 4);
class2size.put(double.class, 8);
class2size.put(void.class, 0);
}
public static int estimateSize(Object o) {
if (o == null) {
return 8;
}
int answer = 8 + estimateSize(o.getClass(), null);
if (o instanceof byte[]) {
answer += ((byte[]) o).length;
} else if (o instanceof ByteBuffer) {
answer += ((ByteBuffer) o).remaining();
} else if (o instanceof CharSequence) {
answer += ((CharSequence) o).length() << 1;
} else if (o instanceof Iterable<?>) {
for (Object m : (Iterable<?>) o) {
answer += estimateSize(m);
}
}
return align(answer);
}
private static int estimateSize(Class<?> clazz, Set<Class<?>> visitedClasses) {
Integer objectSize = class2size.get(clazz);
if (objectSize != null) {
return objectSize;
}
if (visitedClasses != null) {
if (visitedClasses.contains(clazz)) {
return 0;
}
} else {
visitedClasses = new HashSet<Class<?>>();
}
visitedClasses.add(clazz);
int answer = 8; // Basic overhead.
for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
Field[] fields = c.getDeclaredFields();
for (Field f : fields) {
if ((f.getModifiers() & Modifier.STATIC) != 0) {
// Ignore static fields.
continue;
}
answer += estimateSize(f.getType(), visitedClasses);
}
}
visitedClasses.remove(cla