日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

计算 java_两种计算Java对象大小的方法(转)

發布時間:2024/9/19 java 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 计算 java_两种计算Java对象大小的方法(转) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文:http://blog.csdn.net/iter_zc/article/details/41822719

另一篇類似文章:http://www.cnblogs.com/magialmoon/p/3757767.html?

這篇說說如何計算Java

對象大小的方法。之前在聊聊高并發(四)Java對象的表示模型和運行時內存表示?這篇中已經說了Java對象的內存表示模型是Oop-Klass模型。

普通對象的結構如下,按64位機器的長度計算

1. 對象頭(_mark), 8個字節

2. Oop指針,如果是32G內存以下的,默認開啟對象指針壓縮,4個字節

3. 數據區

4.Padding(內存對齊),按照8的倍數對齊

數組對象結構是

1. 對象頭(_mark), 8個字節

2. Oop指針,如果是32G內存以下的,默認開啟對象指針壓縮,4個字節

3. 數組長度,4個字節

4. 數據區

5. Padding(內存對齊),按照8的倍數對齊

清楚了對象在內存的基本布局后,咱們說兩種計算Java對象大小的方法

1. 通過直接獲取對象的大小

2.

通過sun.misc.Unsafe對象的objectFieldOffset(field)等方法結合反射來計算對象的大小

的方式

先講講的方式,這種方法得到的是Shallow

Size,即遇到引用時,只計算引用的長度,不計算所引用的對象的實際大小。如果要計算所引用對象的實際大小,可以通過遞歸的方式去計算。

java.lang.instrument.Instrumentation的實例必須通過指定javaagent的方式才能獲得,具體的步驟如下:

1. 定義一個類,提供一個premain方法: public static void premain(String

agentArgs, Instrumentation instP)

2. 創建META-INF/MANIFEST.MF文件,內容是指定PreMain的類是哪個: Premain-Class:

sizeof.ObjectShallowSize

3. 把這個類打成jar,然后用java -javaagent XXXX.jar XXX.main的方式執行

下面先定義一個類來獲得java.lang.instrument.Instrumentation的實例,并提供了一個static的sizeOf方法對外提供Instrumentation的能力

[java]?view plain

copy

packagesizeof;

import

java.lang.instrument.Instrumentation;

public

class

ObjectShallowSize?{

private

static

Instrumentation?inst;

public

static

void

premain(String?agentArgs,?Instrumentation?instP){

inst?=?instP;

}

public

static

long

sizeOf(Object?obj){

return

inst.getObjectSize(obj);

}

}

定義META-INF/MANIFEST.MF文件

[java]

view

plaincopy

Premain-Class:?sizeof.ObjectShallowSize

打成jar包

[html]

view

plaincopy

cd?編譯后的類和META-INF文件夾所在目錄

jar?cvfm?java-agent-sizeof.jar?META-INF/MANIFEST.MF??.

準備好了這個jar之后,我們可以寫測試類來測試Instrumentation的getObjectSize方法了。在這之前我們先來看對象在內存中是按照什么順序排列的

有如下這個類,字段的定義按如下順序

[java]

view

plaincopy

private

static

class

ObjectA?{

String?str;??//?4

int

i1;

//?4

byte

b1;

//?1

byte

b2;

//?1

int

i2;

//?4

ObjectB?obj;?//4

byte

b3;

//?1

}

按照我們之前說的方法來計算一下這個對象所占大小,注意按8對齊

8(_mark) + 4(oop指針) + 4(str) + 4(i1) + 1(b1) + 1(b2) +

2(padding) + 4(i2) + 4(obj) + 1(b3) + 7(padding) = 40 ?

但事實上是這樣的嗎? 我們來用Instrumentation的getObjectSize來計算一下先:

[java]

view

plaincopy

package

test;

import

sizeof.ObjectShallowSize;

public

class

SizeofWithInstrumetation?{

private

static

class

ObjectA?{

String?str;??//?4

int

i1;

//?4

byte

b1;

//?1

byte

b2;

//?1

int

i2;

//?4

ObjectB?obj;?//4

byte

b3;

//?1

}

private

static

class

ObjectB?{

}

public

static

void

main(String[]?args){

System.out.println(ObjectShallowSize.sizeOf(new

ObjectA()));

}

}

得到的結果是32!不是會按8對齊嗎,b3之前的數據加起來已經是32了,多了1個b3,為33,應該對齊到40才對啊。事實上,HotSpot創建的對象的字段會先按照給定順序排列一下,默認的順序如下,從長到短排列,引用排最后

:? long/double --> int/float

-->? short/char -->

byte/boolean --> Reference

這個順序可以使用JVM參數:

-XX:FieldsAllocationSylte=0(默認是1)來改變。

我們使用sun.misc.Unsafe對象的objectFieldOffset方法來驗證一下:

[java]

view

plaincopy

Field[]?fields?=?ObjectA.

class

.getDeclaredFields();

for

(Field?f:?fields){

System.out.println(f.getName()?+?"?offset:?"

+unsafe.objectFieldOffset(f));

}

可以看到確實是按照從長到短,引用排最后

的方式在內存中排列的。按照這種方法我們來重新計算下ObjectA創建的對象的長度:

8(_mark) + 4(oop指針) + 4(i1) + + 4(i2) + 1(b1) + 1(b2) + 1(b3) +

1(padding) +? 4(str) + 4(obj) = 32

得到的結果和的結果是一樣的,證明我們的計算方式是正確的。

sun.misc.Unsafe的方式

下面說一下通過sun.misc.Unsafe對象的objectFieldOffset(field)等方法結合反射來計算對象的大小。基本的思路如下:

1. 通過反射獲得一個類的Field

2. 通過Unsafe的objectFieldOffset()獲得每個Field的offSet

3. 對Field按照offset排序,取得最大的offset,然后加上這個field的長度,再加上Padding對齊

上面三步就可以獲得一個對象的Shallow

size。可以進一步通過遞歸去計算所引用對象的大小,從而可以計算出一個對象所占用的實際大小。

如何獲得Unsafe對象已經在這篇中聊聊序列化(二)使用sun.misc.Unsafe繞過new機制來創建Java對象

說過了,可以通過反射的機制來獲得.

Oop指針是4還是未壓縮的8也可以通過unsafe.arrayIndexScale(Object[].class)來獲得,這個方法返回一個引用所占用的長度

[java]

view

plaincopy

static

{

try

{

Field?field?=?Unsafe.class

.getDeclaredField(

"theUnsafe"

);

field.setAccessible(true

);

unsafe?=?(Unsafe)?field.get(null

);

objectRefSize?=?unsafe.arrayIndexScale(Object[].class

);

}?catch

(Exception?e)?{

throw

new

RuntimeException(e);

}

}

下面的源碼摘自

http://java-performance.info/memory-introspection-using-sun-misc-unsafe-and-reflection/,

原文中的代碼在計算對象大小的時候有問題,我做了微調,并加上了內存對齊的方法,這樣計算出的結果和Instrumentation的getObjectSize方法是一樣的。

[java]

view

plaincopy

package

test;

import

java.util.ArrayList;

import

java.util.Collections;

import

java.util.Comparator;

import

java.util.List;

public

class

ObjectInfo?{

public

final

String?name;

public

final

String?type;

public

final

String?contents;

public

final

int

offset;

public

final

int

length;

public

final

int

arrayBase;

public

final

int

arrayElementSize;

public

final

int

arraySize;

public

final

List?children;

public

ObjectInfo(String?name,?String?type,?String?contents,

int

offset,

int

length,

int

arraySize,

int

arrayBase,

int

arrayElementSize)

{

this

.name?=?name;

this

.type?=?type;

this

.contents?=?contents;

this

.offset?=?offset;

this

.length?=?length;

this

.arraySize?=?arraySize;

this

.arrayBase?=?arrayBase;

this

.arrayElementSize?=?arrayElementSize;

children?=?new

ArrayList(

1

);

}

public

void

addChild(

final

ObjectInfo?info?)

{

if

(?info?!=

null

)

children.add(?info?);

}

public

long

getDeepSize()

{

//return?length?+?arraySize?+?getUnderlyingSize(?arraySize?!=?0?);

return

addPaddingSize(arraySize?+?getUnderlyingSize(?arraySize?!=

0

));

}

long

size?=

0

;

private

long

getUnderlyingSize(

final

boolean

isArray?)

{

//long?size?=?0;

for

(

final

ObjectInfo?child?:?children?)

size?+=?child.arraySize?+?child.getUnderlyingSize(?child.arraySize?!=?0

);

if

(?!isArray?&&?!children.isEmpty()?){

int

tempSize?=?children.get(?children.size()?-

1

).offset?+?children.get(?children.size()?-

1

).length;

size?+=?addPaddingSize(tempSize);

}

return

size;

}

private

static

final

class

OffsetComparator

implements

Comparator

{

@Override

public

int

compare(

final

ObjectInfo?o1,

final

ObjectInfo?o2?)

{

return

o1.offset?-?o2.offset;

//safe?because?offsets?are?small?non-negative?numbers

}

}

//sort?all?children?by?their?offset

public

void

sort()

{

Collections.sort(?children,?new

OffsetComparator()?);

}

@Override

public

String?toString()?{

final

StringBuilder?sb?=

new

StringBuilder();

toStringHelper(?sb,?0

);

return

sb.toString();

}

private

void

toStringHelper(

final

StringBuilder?sb,

final

int

depth?)

{

depth(?sb,?depth?).append("name="

).append(?name?).append(

",?type="

).append(?type?)

.append(?",?contents="

).append(?contents?).append(

",?offset="

).append(?offset?)

.append(",?length="

).append(?length?);

if

(?arraySize?>

0

)

{

sb.append(",?arrayBase="

).append(?arrayBase?);

sb.append(",?arrayElemSize="

).append(?arrayElementSize?);

sb.append(?",?arraySize="

).append(?arraySize?);

}

for

(

final

ObjectInfo?child?:?children?)

{

sb.append(?'\n'

);

child.toStringHelper(sb,?depth?+?1

);

}

}

private

StringBuilder?depth(

final

StringBuilder?sb,

final

int

depth?)

{

for

(

int

i?=

0

;?i?

sb.append(?"\t"

);

return

sb;

}

private

long

addPaddingSize(

long

size){

if

(size?%

8

!=

0

){

return

(size?/

8

+

1

)?*

8

;

}

return

size;

}

}

package

test;

import

java.lang.reflect.Array;

import

java.lang.reflect.Field;

import

java.lang.reflect.Modifier;

import

java.util.ArrayList;

import

java.util.Arrays;

import

java.util.Collections;

import

java.util.HashMap;

import

java.util.IdentityHashMap;

import

java.util.List;

import

java.util.Map;

import

sun.misc.Unsafe;

public

class

ClassIntrospector?{

private

static

final

Unsafe?unsafe;

private

static

final

int

objectRefSize;

static

{

try

{

Field?field?=?Unsafe.class

.getDeclaredField(

"theUnsafe"

);

field.setAccessible(true

);

unsafe?=?(Unsafe)?field.get(null

);

objectRefSize?=?unsafe.arrayIndexScale(Object[].class

);

}?catch

(Exception?e)?{

throw

new

RuntimeException(e);

}

}

private

static

final

Map?primitiveSizes;

static

{

primitiveSizes?=?new

HashMap(

10

);

primitiveSizes.put(byte

.

class

,

1

);

primitiveSizes.put(char

.

class

,

2

);

primitiveSizes.put(int

.

class

,

4

);

primitiveSizes.put(long

.

class

,

8

);

primitiveSizes.put(float

.

class

,

4

);

primitiveSizes.put(double

.

class

,

8

);

primitiveSizes.put(boolean

.

class

,

1

);

}

public

ObjectInfo?introspect(

final

Object?obj)

throws

IllegalAccessException?{

try

{

return

introspect(obj,

null

);

}?finally

{

//?clean?visited?cache?before?returning?in?order?to?make

//?this?object?reusable

m_visited.clear();

}

}

//?we?need?to?keep?track?of?already?visited?objects?in?order?to?support

//?cycles?in?the?object?graphs

private

IdentityHashMapm_visited?=

new

IdentityHashMap(

100

);

private

ObjectInfo?introspect(

final

Object?obj,

final

Field?fld)

throws

IllegalAccessException?{

//?use?Field?type?only?if?the?field?contains?null.?In?this?case?we?will

//?at?least?know?what's?expected?to?be

//?stored?in?this?field.?Otherwise,?if?a?field?has?interface?type,?we

//?won't?see?what's?really?stored?in?it.

//?Besides,?we?should?be?careful?about?primitives,?because?they?are

//?passed?as?boxed?values?in?this?method

//?(first?arg?is?object)?-?for?them?we?should?still?rely?on?the?field

//?type.

boolean

isPrimitive?=?fld?!=

null

&&?fld.getType().isPrimitive();

boolean

isRecursive?=

false

;

//?will?be?set?to?true?if?we?have?already

//?seen?this?object

if

(!isPrimitive)?{

if

(m_visited.containsKey(obj))

isRecursive?=?true

;

m_visited.put(obj,?true

);

}

final

Class?type?=?(fld?==

null

||?(obj?!=

null

&&?!isPrimitive))???obj

.getClass()?:?fld.getType();

int

arraySize?=

0

;

int

baseOffset?=

0

;

int

indexScale?=

0

;

if

(type.isArray()?&&?obj?!=

null

)?{

baseOffset?=?unsafe.arrayBaseOffset(type);

indexScale?=?unsafe.arrayIndexScale(type);

arraySize?=?baseOffset?+?indexScale?*?Array.getLength(obj);

}

final

ObjectInfo?root;

if

(fld?==

null

)?{

root?=?new

ObjectInfo(

""

,?type.getCanonicalName(),?getContents(obj,

type),?0

,?getShallowSize(type),?arraySize,?baseOffset,

indexScale);

}?else

{

final

int

offset?=?(

int

)?unsafe.objectFieldOffset(fld);

root?=?new

ObjectInfo(fld.getName(),?type.getCanonicalName(),

getContents(obj,?type),?offset,?getShallowSize(type),

arraySize,?baseOffset,?indexScale);

}

if

(!isRecursive?&&?obj?!=

null

)?{

if

(isObjectArray(type))?{

//?introspect?object?arrays

final

Object[]?ar?=?(Object[])?obj;

for

(

final

Object?item?:?ar)

if

(item?!=

null

)

root.addChild(introspect(item,?null

));

}?else

{

for

(

final

Field?field?:?getAllFields(type))?{

if

((field.getModifiers()?&?Modifier.STATIC)?!=

0

)?{

continue

;

}

field.setAccessible(true

);

root.addChild(introspect(field.get(obj),?field));

}

}

}

root.sort();?//?sort?by?offset

return

root;

}

//?get?all?fields?for?this?class,?including?all?superclasses?fields

private

static

List?getAllFields(

final

Class?type)?{

if

(type.isPrimitive())

return

Collections.emptyList();

Class?cur?=?type;

final

List?res?=

new

ArrayList(

10

);

while

(

true

)?{

Collections.addAll(res,?cur.getDeclaredFields());

if

(cur?==?Object.

class

)

break

;

cur?=?cur.getSuperclass();

}

return

res;

}

//?check?if?it?is?an?array?of?objects.?I?suspect?there?must?be?a?more

//?API-friendly?way?to?make?this?check.

private

static

boolean

isObjectArray(

final

Class?type)?{

if

(!type.isArray())

return

false

;

if

(type?==

byte

[].

class

||?type?==

boolean

[].

class

||?type?==?char

[].

class

||?type?==

short

[].

class

||?type?==?int

[].

class

||?type?==

long

[].

class

||?type?==?float

[].

class

||?type?==

double

[].

class

)

return

false

;

return

true

;

}

//?advanced?toString?logic

private

static

String?getContents(

final

Object?val,

final

Class?type)?{

if

(val?==

null

)

return

"null"

;

if

(type.isArray())?{

if

(type?==

byte

[].

class

)

return

Arrays.toString((

byte

[])?val);

else

if

(type?==

boolean

[].

class

)

return

Arrays.toString((

boolean

[])?val);

else

if

(type?==

char

[].

class

)

return

Arrays.toString((

char

[])?val);

else

if

(type?==

short

[].

class

)

return

Arrays.toString((

short

[])?val);

else

if

(type?==

int

[].

class

)

return

Arrays.toString((

int

[])?val);

else

if

(type?==

long

[].

class

)

return

Arrays.toString((

long

[])?val);

else

if

(type?==

float

[].

class

)

return

Arrays.toString((

float

[])?val);

else

if

(type?==

double

[].

class

)

return

Arrays.toString((

double

[])?val);

else

return

Arrays.toString((Object[])?val);

}

return

val.toString();

}

//?obtain?a?shallow?size?of?a?field?of?given?class?(primitive?or?object

//?reference?size)

private

static

int

getShallowSize(

final

Class?type)?{

if

(type.isPrimitive())?{

final

Integer?res?=?primitiveSizes.get(type);

return

res?!=

null

??res?:

0

;

}?else

return

objectRefSize;

}

}

先一個測試

類來驗證一下Unsafe的方式計算出的結果

[html]

view

plaincopy

public?class?ClassIntrospectorTest

{

public?static?void?main(String[]?args)?throws?IllegalAccessException?{

final?ClassIntrospector?ci

=

new

ClassIntrospector();

ObjectInfo?res;

res

=

ci

.introspect(?new?ObjectA()?);

System.out.println(?res.getDeepSize()?);

}

private?static?class?ObjectA?{

String?str;??//?4

int?i1;?//?4

byte?b1;?//?1

byte?b2;?//?1

int?i2;??//?4

ObjectB?obj;?//4

byte?b3;??//?1

}

private?static?class?ObjectB?{

}

}

計算結果如下:

32

和我們之前計算結果是一致的,證明是正確的。

最后再來測試一下數組對象的長度。有兩個類如下:

[java]

view

plaincopy

private

static

class

ObjectC?{

ObjectD[]?array?=?new

ObjectD[

2

];

}

private

static

class

ObjectD?{

int

value;

}

它們在內存的大體分布如下圖:

我們可以手工計算一下ObjectC obj = new ObjectC()的大小:

ObjectC的Shallow size = 8(_mark) + 4(oop指針)? +

4(ObjectD[]引用) = 16

new ObjectD[2]數組的長度 =? 8(_mark) + 4(oop指針) +

4(數組長度占4個字節) + 4(ObjectD[0]引用) + 4(ObjectD[1]引用) = 24

由于ObjectD[]數組沒有指向具體的對象大小,所以我們手工計算的結果是16 + 24 = 40

使用Unsafe對象的方式來計算一下:

[java]

view

plaincopy

public

static

void

main(String[]?args)

throws

IllegalAccessException?{

final

ClassIntrospector?ci?=

new

ClassIntrospector();

ObjectInfo?res;

res?=?ci.introspect(?new

ObjectC()?);

System.out.println(?res.getDeepSize()?);

}

計算結果如下,和我們計算的結果是一致的,證明是正確的:

40

再給ObjectD[]數組指向具體的ObjectD對象,再測試一下結果:

[java]

view

plaincopy

public

static

void

main(String[]?args)

throws

IllegalAccessException?{

final

ClassIntrospector?ci?=

new

ClassIntrospector();

ObjectInfo?res;

res?=?ci.introspect(?new

ObjectC()?);

System.out.println(?res.getDeepSize()?);

}

private

static

class

ObjectC?{

ObjectD[]?array?=?new

ObjectD[

2

];

public

ObjectC(){

array[0

]?=

new

ObjectD();

array[1

]?=

new

ObjectD();

}

}

private

static

class

ObjectD?{

int

value;

}

我們可以手工計算一下ObjectC obj = new ObjectC()的大小:

ObjectC的Shallow size = 8(_mark) + 4(oop指針)? +

4(ObjectD[]引用) = 16

new ObjectD[2]數組的長度 =? 8(_mark) + 4(oop指針) +

4(數組長度占4個字節) + 4(ObjectD[0]引用) + 4(ObjectD[1]引用) = 24

ObjectD對象長度 = 8(_mark) + 4(oop指針) + 4(value) = 16

所以ObjectC實際占用的空間 = 16 + 24 + 2 * 16 = 72

使用Unsafe的方式計算的結果也是72,和我們手工計算的方式一致。

參考:??Memory

introspection using sun.misc.Unsafe and reflection

總結

以上是生活随笔為你收集整理的计算 java_两种计算Java对象大小的方法(转)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。