一个String对象到底多大?
JAVA对象的组成部分
对象头 + 实例数据 + 对齐填充
HotSpot的对齐方式为8字节对齐:
(对象头 + 实例数据 + padding) % 8等于0且0 <= padding < 8
每个组成部分的大小
32位和64位系统上对象头的大小是不同的,开启指针压缩后,各部分大小也会有所不同。
单位:byte
64位 | 指针压缩(-XX:+UseCompressedOops) | |
---|---|---|
对象头 | 16 | 12 |
实例数据为引用类型 | 8 | 4 |
数组对象头 | 24 | 16 |
计算对象大小
对象本身大小 = 当前类及超类的基本类型实例字段大小 + 引用类型实例字段引用大小 + 实例基本类型数组总占用空间 + 实例引用类型数组引用本身占用空间大小;
手动计算后,校验是否正确
- 使用JOL工具
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.9</version>
</dependency>
public class SizeOfObjectDemo2 {
/**
* 64位虚拟机上开启指针压缩后,String对象占用内存总大小为24 + 16 + 2 * 6 + 4 =24 + 32 =56
* jdk 8
* @param args
*/
public static void main(String[] args) {
String s = "123678";
// 启用压缩后: 16(对象头) + 2 * 3 + 2 = 24
char[] c = new char[]{'1','2','3'};
// 对象本身大小
print(ClassLayout.parseInstance(s).toPrintable());
// 获取对象总大小
print("size : " + GraphLayout.parseInstance(s).totalSize());
}
static void print(String message) {
System.out.println(message);
System.out.println("-------------------------");
}
}
string 对象本身大小
java.lang.String object internals:
OFFSET | SIZE | TYPE DESCRIPTION | VALUE |
---|---|---|---|
0 | 4 | (object header) | 01 00 00 00 (00000001 00000000 00000000 00000000) (1) |
4 | 4 | (object header) | 00 00 00 00 (00000000 00000000 00000000 00000000) (0) |
8 | 4 | (object header) | da 02 00 f8 (11011010 00000010 00000000 11111000) (-134216998) |
12 | 4 | char[] String.value | [1, 2, 3, 6, 7, 8] |
16 | 4 | int String.hash | 0 |
20 | 4 | (loss due to the next object alignment) | |
Instance size: 24 bytes |
而一个对象的总大小需要递归计算包含的对象的大小。比如String对象计算它的总大小的时候就需要计算char数组的大小,这里计算了char[] c = new char[]{‘1’,‘2’,‘3’,‘4’,‘5’,‘6’};
[C object internals:
OFFSET | SIZE | TYPE DESCRIPTION | VALUE |
---|---|---|---|
0 | 4 | (object header) | 01 00 00 00 (00000001 00000000 00000000 00000000) (1) |
4 | 4 | (object header) | 00 00 00 00 (00000000 00000000 00000000 00000000) (0) |
8 | 4 | (object header) | 41 00 00 f8 (01000001 00000000 00000000 11111000) (-134217663) |
12 | 4 | (object header) | 06 00 00 00 (00000110 00000000 00000000 00000000) (6) |
16 | 12 | char [C. | N/A |
28 | 4 | (loss due to the next object alignment) | |
Instance size: 32 bytes |
- 使用Instrumentation
public class SizeOfObject {
static Instrumentation inst;
public static void premain(String args, Instrumentation instP) {
inst = instP;
}
/**
* 直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、<br></br>
* 引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小;<br></br>
* 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小 <br></br>
*
* @param obj
* @return
*/
public static long sizeOf(Object obj) {
return inst.getObjectSize(obj);
}
/**
* 递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小
*
* @param objP
* @return
* @throws IllegalAccessException
*/
public static long fullSizeOf(Object objP) throws IllegalAccessException {
Set<Object> visited = new HashSet<Object>();
Deque<Object> toBeQueue = new ArrayDeque<>();
toBeQueue.add(objP);
long size = 0L;
while (toBeQueue.size() > 0) {
Object obj = toBeQueue.poll();
// sizeOf的时候已经计基本类型和引用的长度,包括数组
size += skipObject(visited, obj) ? 0L : sizeOf(obj);
Class<?> tmpObjClass = obj.getClass();
if (tmpObjClass.isArray()) {
//[I , [F 基本类型名字长度是2
if (tmpObjClass.getName().length() > 2) {
for (int i = 0, len = Array.getLength(obj); i < len; i++) {
Object tmp = Array.get(obj, i);
if (tmp != null) {
//非基本类型需要深度遍历其对象
toBeQueue.add(Array.get(obj, i));
}
}
}
} else {
while (tmpObjClass != null) {
Field[] fields = tmpObjClass.getDeclaredFields();
for (Field field : fields) {
//静态不计 基本类型不重复计
if (Modifier.isStatic(field.getModifiers())
|| field.getType().isPrimitive()) {
continue;
}
field.setAccessible(true);
Object fieldValue = field.get(obj);
if (fieldValue == null) {
continue;
}
toBeQueue.add(fieldValue);
}
tmpObjClass = tmpObjClass.getSuperclass();
}
}
}
return size;
}
/**
* String.intern的对象不计;计算过的不计,也避免死循环
*
* @param visited
* @param obj
* @return
*/
static boolean skipObject(Set<Object> visited, Object obj) {
if (obj instanceof String && obj == ((String) obj).intern()) {
return true;
}
return visited.contains(obj);
}
}
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
<archive>
<manifestEntries>
<Premain-Class>com.example.instrumentation.SizeOfObject</Premain-Class>
<!--<Agent-Class>com.example.instrumentation.MyJavaAgent</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>-->
</manifestEntries>
</archive>
</configuration>
<executions>
<execution>
<goals>
<goal>attached</goal>
</goals>
<phase>package</phase>
</execution>
</executions>
</plugin>
</plugins>
</build>