实现一个sizeof获取Java对象大小.docx
《实现一个sizeof获取Java对象大小.docx》由会员分享,可在线阅读,更多相关《实现一个sizeof获取Java对象大小.docx(18页珍藏版)》请在冰豆网上搜索。
实现一个sizeof获取Java对象大小
实现一个sizeof获取Java对象大小
在学习编程的过程中,我觉得不止要获得课本的知识,更多的是通过学习技术知识提高解决问题的能力,这样我们才能走在最前方,更多Java学习,请搜索疯狂Java;
由于Java的设计者不想让程序员管理和了解内存的使用,我们想要知道一个对象在内存中的大小变得比较困难了。
本文提供了可以获取对象的大小的方法,但是由于各个虚拟机在内存使用上可能存在不同,因此该方法不能在各虚拟机上都适用,而是仅在hotspot32位虚拟机上,或者其它内存管理方式与hotspot32位虚拟机相同的虚拟机上适用。
本方法使用了Unsafe类来访问对象的私有属性,因此有些特殊的设置和做法,要留意类定义前面的文字说明。
要想计算对象大小,我们必须熟悉hotspot32上不同类型所占的空间:
一,原始类型primitives:
boolean:
1byte,尽管Java语言规范里面boolean是一个bit;
byte:
1byte;
char:
2bytes;
short:
2bytes;
int:
4bytes;
float:
4bytes;
long:
8bytes;
double:
8bytes。
二,引用类型:
4bytes,即使是null值也是如此。
三,空的普通对象(无任何属性,如newObject(),不是null对象):
8bytes。
存放对象头的各种信息。
四,空的数组(即长度为0的数组,而不是null数组):
12bytes,其中比普通对象多出来的4bytes是用来放数组长度的。
五,hotspot32分配内存是以8bytes的整数倍来计算的,因此不足8个字节的对象要补足剩余的
字节数以对齐。
Java代码
/**
*这个例子在eclipse里不能直接编译,要到项目的属性,
*JavaCompiler,Errors/Warnings中DeprecatedandrestrictedAPI
*中Forbiddenreference(accessrules)中设置为warning。
*
*获取一个Java对象在内存所占的空间,不同的虚拟机内存管理方式可能不同,
*本例是针对32位的hotspot虚拟机的。
*
*由于虚拟机对字符串做了特殊处理,比如将其放入常量池,因此sizeof得到的字符串
*包含了常量池里面占用的空间。
基本类型的包装类也会重复利用对象。
*
*设计作者:
teasp
*信息描述:
*/
@SuppressWarnings("restriction")
publicclassHotspotSizeof
{
publicstaticfinalintOBJ_BASIC_LEN=8*8;
publicstaticfinalintARRAY_BASIC_LEN=12*8;
publicstaticfinalintOBJ_REF_LEN=4*8;
publicstaticfinalintALIGN=8*8;
privatestaticUnsafeUNSAFE;
static{
try
{
FieldtheUnsafe=Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE=(Unsafe)theUnsafe.get(null);
}
catch(Exceptione)
{
e.printStackTrace();
}
}
/**
*原始类型的种类,以及每个类型所占空间,单位为bit
*@authorAdministrator
*
*/
privateenumPType
{
布尔(8)/*Java语言规定是1个bit*/,字节(8),字符(16),短整(16),
整形(32),浮点(32),长整(64),双精(64);
privateintbits;
privatePType(intbits)
{
this.bits=bits;
}
publicintgetBits(){
returnbits;
}
}
/**
*计算obj对象在虚拟机中所占的内存,单位为bit。
*如果isPapa为true,则表明计算的是obj对象父类定义的属性。
*
*@paramobj
*@paramclazz
*@paramisPapa
*@return
*/
privatestaticintgetObjBits(Objectobj,Classclazz,booleanisPapa)
{
intbits=0;
if(obj==null)
{
returnbits;
}
bits+=OBJ_BASIC_LEN;
if(isPapa)
{
bits=0;
}
Field[]fields=clazz.getDeclaredFields();
if(fields!
=null)
{
for(Fieldfield:
fields)
{
//静态属性不参与计算
if(Modifier.isStatic(field.getModifiers()))
{
//System.out.println("static"+field.getName());
continue;
}
Classc=field.getType();
if(c==boolean.class)
{
bits+=PType.布尔.getBits();
}
elseif(c==byte.class)
{
bits+=PType.字节.getBits();
}
elseif(c==char.class)
{
bits+=PType.字符.getBits();
}
elseif(c==short.class)
{
bits+=PType.短整.getBits();
}
elseif(c==int.class)
{
bits+=PType.整形.getBits();
System.out.println(field.getName()+"="+UNSAFE.getInt(obj,
UNSAFE.objectFieldOffset(field)));
}
elseif(c==float.class)
{
bits+=PType.浮点.getBits();
}
elseif(c==long.class)
{
bits+=PType.长整.getBits();
}
elseif(c==double.class)
{
bits+=PType.双精.getBits();
}
elseif(c==void.class)
{
//nothing
}elseif(c.isArray())
{//是数组
Objecto=UNSAFE.getObject(obj,UNSAFE.objectFieldOffset
(field));
bits+=OBJ_REF_LEN;
if(o!
=null)
{
try
{
bits+=bitsofArray(o);
}catch(Exceptione)
{
thrownewRuntimeException(e);
}
}
}else
{//普通对象
Objecto=UNSAFE.getObject(obj,UNSAFE.objectFieldOffset
(field));
bits+=OBJ_REF_LEN;
if(o!
=null)
{
try
{
bits+=bitsof(o);
}catch(Exceptione)
{
thrownewRuntimeException(e);
}
}
}
}
}
Classpapa=clazz.getSuperclass();
if(papa!
=null)
{
bits+=getObjBits(obj,papa,true);
}
//补齐,当计算父类属性时,因为是对同一个对象在进行统计,所以不要补齐。
//一个对象只做一次补齐动作。
if(false==isPapa)
{
if(bits%ALIGN>0)
{
bits+=(ALIGN-bits%ALIGN);
}
}
returnbits;
}
/**
*计算arr数组在虚拟机中所占的内存,单位为bit
*@paramarr
*@return
*/
privatestaticintbitsofArray(Objectarr)
{
intbits=0;
if(arr==null)
{
returnbits;
}
bits+=ARRAY_BASIC_LEN;
Classc=arr.getClass();
if(c.isArray()==false)
{
thrownewRuntimeException("Mustbearray!
");
}
if(c==boolean[].class)
{
bits+=PType.布尔.getBits()*((boolean[])arr).length;
}
elseif(c==byte[].class)
{
bits+=PType.字节.getBits()*((byte[])arr).length;
}
elseif(c==char[].class)
{
bits+=PType.字符.getBits()*((char[])arr).length;
}
elseif(c==short[].class)
{
bits+=PType.短整.getBits()*((short[])arr).length;
}
elseif(c==int[].class)
{
bits+=PType.整形.getBits()*((int[])arr).length;
}
elseif(c==float[].class)
{
bits+=PType.浮点.getBits()*((float[])arr).length;
}
elseif(c==long[].class)
{
bits+=PType.长整.getBits()*((long[])arr).length;
}
elseif(c==double[].class)
{
bits+=PType.双精.getBits()*((double[])arr).length;
}
else
{
Object[]os=(Object[])arr;
for(Objecto:
os)
{
bits+=OBJ_REF_LEN+bitsof(o);
}
}
//补齐
if(bits%ALIGN>0)
{
bits+=(ALIGN-bits%ALIGN);
}
returnbits;
}
/**
*计算obj对象在虚拟机中所占的内存,单位为bit
*@paramobj
*@return
*/
privatestaticintbitsof(Objectobj)
{
if(obj==null)
{
return0;
}
if(obj.getClass().isArray())
{
returnbitsofArray(obj);
}
returngetObjBits(obj,obj.getClass(),false);
}
/**
*计算obj对象在虚拟机中所占的内存,单位为byte
*@paramobj
*@return
*/
publicstaticintsizeof(Objectobj)
{
returnbitsof(obj)/8;
}
privatestaticvoidrunGC()throwsException
{
//IthelpstocallRuntime.gc()
//usingseveralmethodcalls:
for(intr=0;r<4;++r)_runGC();
}
privatestaticvoid_runGC()throwsException
{
longusedMem1=usedMemory(),usedMem2=Long.MAX_VALUE;
for(inti=0;(usedMem1
{
Runtime.getRuntime().runFinalization();
Runtime.getRuntime().gc();
Thread.yield();
usedMem2=usedMem1;
usedMem1=usedMemory();
}
}
privatestaticlongusedMemory()
{
returnRuntime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory
();
}
/**
*本方法在计算String以及原始类型的包装类的时候可能不准。
*Strings="abc";这种方式产生的String对象会被放入常量池。
*Integer.valueOf
(1)会返回缓存的对象而不是new一个。
*@paramcls
*@return
*@throwsException
*/
privatestaticlongdeterminObjSize(Classcls)throwsException
{
//Warmupallclasses/methodswewilluse
runGC();
usedMemory();
//Arraytokeepstrongreferencestoallocatedobjects
finalintcount=100000;
Object[]objects=newObject[count];
longheap1=0;
//Allocatecount+1objects,discardthefirstone
for(inti=-1;i {
Objectobject=null;
//Instantiateyourdatahereandassignittoobject
//object=newObject();
//object=newInteger(i);
//object=newLong(i);
//object=newString();
//object=newbyte[128][1]
object=cls.newInstance();
if(i>=0)
objects[i]=object;
else
{
object=null;//Discardthewarmupobject
runGC();
heap1=usedMemory();//Takeabeforeheapsnapshot
}
}
runGC();
longheap2=usedMemory();//Takeanafterheapsnapshot:
finalintsize=Math.round(((float)(heap2-heap1))/count);
System.out.println("'before'heap:
"+heap1+
",'after'heap:
"+heap2);
System.out.println("heapdelta:
"+(heap2-heap1)+
",{"+objects[0].getClass()+"}size="+size+"bytes");
for(inti=0;i objects=null;
returnsize;
}
publicstaticvoidmain(String[]args)
{
HotspotSizeofhs=newHotspotSizeof();
hs.test();
}
@Test
publicvoidtest()
{
try
{
Assert.assertEquals(determinObjSize(Obj4SizeofTest.class),sizeof(new
Obj4SizeofTest()));
}catch(Exceptione)
{
thrownewRuntimeException(e);
}
}
}
测试用的两个类:
Java代码
/**
*设计作者:
teasp
*信息描述:
*/
publicclassPapa
{
@SuppressWarnings("unused")
privateintaint=4;
publicstaticintbint;
@SuppressWarnings("unused")
//privateStringstr="123";
//privateStringstr=newString("123");
//privateStringstr=newString(newbyte[]{49,50,51});
privateStringstr=newString(newchar[]{49,50,51});
@SuppressWarnings("unused")
privateint[]ints={};
@SuppressWarnings("unused")
privateint[][]intss={{}};
//privateint[][]intss={{1},{1,2}};
}
/**
*设计作者:
teasp
*信息描述:
*/
publicclassObj4SizeofTestextendsPapa
{
@SuppressWarnings("unused")
privateintaint=3;
publicintbint=4;
@SuppressWarnings("unused")
privatebooleanb1=true;
@SuppressWarnings("unused")
privatebooleanb2=true;
@SuppressWarnings("unused")
privatebooleanb3=true;
@SuppressWarnings("unused")
privatebooleanb4=true;
@SuppressWarnings("unused")
privatebooleanb5=true;
@SuppressWarnings("unused")
privatebooleanb6=true;
@SuppressWarnings("unused")
privatebooleanb7=true;
@SuppressWarnings("unused")
privatebooleanb8=true;
@SuppressWarnings("unused")
privateStringstr1;
@SuppressWarnings("unused")
privateObjectobj=newPapa();
publicstaticfinalbyte[]bytes={97};
@SuppressWarnings("unused")
privateStringstr2=newString(bytes);
@SuppressWarnings("unused")
privateIntegeri=newInteger
(1);
@SuppressWarnings("unused")
privateint[]is={1,2,3};
@SuppressWarnings("unused")
privateObject[][]o