赞
踩
参考连接:https://www.iteye.com/blog/yueyemaitian-2033046
1、编写Instrumentation类
创建一个普通类,内含静态方法premain(),这个方法名是java agent内定的方法名,总会在main函数之前执行。
当存在多个premain()重载函数时,参数少的方法会被自动忽略掉。
package com.hpp;
import java.lang.instrument.Instrumentation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashSet;
import java.util.Set;
/**
* 对象占用字节大小工具类
*/
public class SizeOfObject {
static Instrumentation inst;
public static void premain(String args, Instrumentation instP) {
inst = instP;
}
/**
* 直接计算当前对象占用空间大小,包括当前类及超类的基本类型实例字段大小、<br></br>
* 引用类型实例字段引用大小、实例基本类型数组总占用空间、实例引用类型数组引用本身占用空间大小;<br></br>
* 但是不包括超类继承下来的和当前类声明的实例引用字段的对象本身的大小、实例引用数组引用的对象本身的大小 <br></br>
*
*/
public static long sizeOf(Object obj) {
return inst.getObjectSize(obj);
}
/**
* 递归计算当前对象占用空间总大小,包括当前类和超类的实例字段大小以及实例字段引用对象大小
*
*/
public static long fullSizeOf(Object objP) throws IllegalAccessException {
Set<Object> visited = new HashSet<Object>();
Deque<Object> toBeQueue = new ArrayDeque<Object>();
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);
}
public static void main(String[] args) {
sizeOf(new Object());
}
}
2、编写达成jar包需要的MANIFEST.MF文件
针对普通的java工程,在和src同级的目录中创建META-INF/MANIFEST.MF文件,并输入如下内容:
Premain-Class: com.hpp.SizeOfObject
Can-Redefine-Classes: true
Can-Retransform-Classes: true
针对maven工程,在resource目录下新建META-INF/MANIFEST.MF文件,配合maven-assembly-plugin插件使用。。。还可以通过maven-jar-plugin插件实现key/value值写入,具体如下
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<finalName>test</finalName>
<archive>
<manifestEntries>
<Premain-class>com.tmall.buy.SizeOfObject</Premain-class>
<Can-Retransform-Classes >true</Can-Retransform-Classes >
<Can-Redefine-Classes>false</Can-Redefine-Classes>
</manifestEntries>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
</plugin>
3、将Instrumentation类打成相应的jar包
方式1:使用maven_jar_plugin插件
方式2:使用idea打出jar包
4、编写测试类
package com.hpp.test;
import java.io.File;
import static com.hpp.SizeOfObject.fullSizeOf;
import static com.hpp.SizeOfObject.sizeOf;
public class TestObject {
/**
* -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 = 16
* -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + padding/4 = 24
*/
static class A {
int a;
}
/**
* -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24
* -XX:-UseCompressedOops: mark/8 + metedata/8 + 4 + 4 = 24
*/
static class B {
int a;
int b;
}
/**
* -XX:+UseCompressedOops: mark/4 + metedata/8 + 4 + 4 + padding/4 = 24
* -XX:-UseCompressedOops: mark/8 + metedata/8 + 8 + 4 + padding/4 = 32
*/
static class B2 {
int b2a;
Integer b2b;
}
/**
* 不考虑对象头:
* 4 + 4 + 4 * 3 + 3 * sizeOf(B)
*/
static class C extends A {
int ba;
B[] as = new B[3];
C() {
for (int i = 0; i < as.length; i++) {
as[i] = new B();
}
}
}
static class D extends B {
int da;
Integer[] di = new Integer[3];
}
/**
* 会算上A的实例字段
*/
static class E extends A {
int ea;
int eb;
}
public static void main(String[] args) throws IllegalAccessException {
System.out.println(new File("./target/classes").getAbsolutePath());
System.out.println("sizeOf(new Object())=" + sizeOf(new Object()));
System.out.println("sizeOf(new A())=" + sizeOf(new A()));
System.out.println("sizeOf(new B())=" + sizeOf(new B()));
System.out.println("sizeOf(new B2())=" + sizeOf(new B2()));
System.out.println("sizeOf(new B[3])=" + sizeOf(new B[3]));
System.out.println("sizeOf(new C())=" + sizeOf(new C()));
System.out.println("fullSizeOf(new C())=" + fullSizeOf(new C()));
System.out.println("sizeOf(new D())=" + sizeOf(new D()));
System.out.println("fullSizeOf(new D())=" + fullSizeOf(new D()));
System.out.println("sizeOf(new int[3])=" + sizeOf(new int[3]));
System.out.println("sizeOf(new Integer(1)=" + sizeOf(new Integer(1)));
System.out.println("sizeOf(new Integer[0])=" + sizeOf(new Integer[0]));
System.out.println("sizeOf(new Integer[1])=" + sizeOf(new Integer[1]));
System.out.println("sizeOf(new Integer[2])=" + sizeOf(new Integer[2]));
System.out.println("sizeOf(new Integer[3])=" + sizeOf(new Integer[3]));
System.out.println("sizeOf(new Integer[4])=" + sizeOf(new Integer[4]));
System.out.println("sizeOf(new A[3])=" + sizeOf(new A[3]));
System.out.println("sizeOf(new E())=" + sizeOf(new E()));
}
}
上述需在测试工程中,引入插桩的实现类
5、测试结果
通过java –jar方式执行,java -javaagent:test.jar -cp test2.jar com.hpp.test.TestObject,其中test.jar和test2.jar,在同一目录中。
通过idea方式执行,引入插桩类后,在启动的vm参数中,增加-javaagent:lib/test.jar配置。
执行结果如下:
在64位机器上,运行结果如下:
sizeOf(new Object())=16
sizeOf(new A())=16
sizeOf(new B())=24
sizeOf(new B2())=24
sizeOf(new B[3])=32
sizeOf(new C())=24
fullSizeOf(new C())=128
sizeOf(new D())=32
fullSizeOf(new D())=64
sizeOf(new int[3])=32
sizeOf(new Integer(1)=16
sizeOf(new Integer[0])=16
sizeOf(new Integer[1])=24
sizeOf(new Integer[2])=24
sizeOf(new Integer[3])=32
sizeOf(new Integer[4])=32
sizeOf(new A[3])=32
sizeOf(new E())=24
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。