`

深入Java对象大小

阅读更多

 

在大规模Java 应用开发中,总会遇到内存泄漏的问题。通常的做法,通过 Profile 工具,分析 Java  Heap ,一般能够发现哪些对象内存占用巨大,而引起的泄漏问题。为了更好地深入了解问题的本质,以及从另外一个角度来分析问题,特写这篇文章。

 

可能不少的读者,并不清楚Java 对象到底占居多少的空间(单位:字节 =8 比特)。文章中会使用 JDK 6 update 7 自带的 Profile 工具 -Java VisualVM 。引入 Profile 工具的目的正是为了分析对象的大小。

 

首先,要区别Java 对象和 Java 类元信息,其中, JVM 把所有的 Java 对象放到 Java Heap 中,而类的元信息是放在方法区的,通俗地说,在 Java 源代码中,定义的字段和方法等变量标示(比如字段名称和类型等)。请注意,元信息所引用的对象还是在 Java Heap 里面。那么,本章主要 针对 的是Java Heap

 

       早几天,看到了JavaEye 上面提问 -http://www.iteye.com/problems/45423 。问题的本质,和主题一样,不过它想要通过 Java 程序来计算,貌似有点困难。以前外国一个哥们( http://www.javaworld.com/javaworld/javatips/jw-javatip130.html )也写 一个程序计算对象大小,它的计算如下:

public class Sizeof

{

    public static void main (String [] args) throws Exception

    {

        // Warm up all classes/methods we will use

        runGC ();

        usedMemory ();

        // Array to keep strong references to allocated objects

        final int count = 100000;

        Object [] objects = new Object [count];        

        long heap1 = 0;

        // Allocate count+1 objects, discard the first one

        for (int i = -1; i < count; ++ i)

        {

            Object object = null;

            // Instantiate your data here and assign it to object

             object = new Object ();

            if (i >= 0)

                objects [i] = object;

            else

            {

                object = null; // Discard the warm up object

                runGC ();

                heap1 = usedMemory (); // Take a before heap snapshot

            }

        }

        runGC ();

        long heap2 = usedMemory (); // Take an after heap snapshot:        

        final int size = Math.round (((float)(heap2 - heap1))/count);

        System.out.println ("'before' heap: " + heap1 +

                            ", 'after' heap: " + heap2);

        System.out.println ("heap delta: " + (heap2 - heap1) +

            ", {" + objects [0].getClass () + "} size = " + size + " bytes");

        for (int i = 0; i < count; ++ i) objects [i] = null;

        objects = null;

    }

    private static void runGC () throws Exception

    {

        // It helps to call Runtime.gc()

        // using several method calls:

        for (int r = 0; r < 4; ++ r) _runGC ();

    }

    private static void _runGC () throws Exception

    {

        long usedMem1 = usedMemory (), usedMem2 = Long.MAX_VALUE;

        for (int i = 0; (usedMem1 < usedMem2) && (i < 500); ++ i)

        {

            s_runtime.runFinalization ();

            s_runtime.gc ();

            Thread.currentThread ().yield ();

            

            usedMem2 = usedMem1;

            usedMem1 = usedMemory ();

        }

    }

    private static long usedMemory ()

    {

        return s_runtime.totalMemory () - s_runtime.freeMemory ();

    }

    

    private static final Runtime s_runtime = Runtime.getRuntime ();

} // End of class

( 代码 1)

 

通过改变红色区域,来切换测试对象。先运行出结果,以下结果是在Windows XP x86  ,SUN JDK 1.6.0 update 7 ,并且Console 信息部分被截断:

 

{class java.lang.Object} size = 8 bytes

{class java.lang.Integer} size = 16 bytes

{class java.lang.Long} size = 16 bytes

{class java.lang.Byte} size = 16 bytes

{class [Ljava.lang.Object;} size = 16 bytes //长度为0的Object类型数组

{class [Ljava.lang.Object;} size = 16 bytes //长度为1的Object类型数组

{class [Ljava.lang.Object;} size = 24 bytes //长度为2的Object类型数组

 

现在,这个结论有问题,因为,从Byte、Long、Integer源代码的角度,不可能对象空间大小相同,那么接下来需要借助于Java VisualVM来做了。 在测试之前 ,需要一段辅助程序,帮助执行。实现如下:

 

public   class  MainClass {

/**

 * 启动方法

 *  @param  args

 */

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

Object object =  new  Object();

neverStop ();

neverGC (object);

}

private   static   void  neverGC(Object object) {

System. out .println(object);

}

private   static   void  neverStop()  throws  InterruptedException {

Thread. sleep (Long. MAX_VALUE );

}

}

( 代码2 )

这个程序保证对象不会被GC掉,并且不会停止。

首先,在Java VisualVM上面,选择正确的程序进行监控,然后做一个Heap Dump。



 (图1)

Dump之后,点击 Classes ,然后过滤出 java.lang.Object ,如图所示:



 (图2)


 (图3)

 

 

3 过滤出三个结果,暂时不看数组对象,选择java.lang.Object ,然后点击 instances 按钮,或者双击 java.lang.Object


 

( 4)

结果发现,Object 对象的大小总是 8 个字节。再看看 Integer Long Byte Sizeof 类的计算结果不可信,分析错误原因放一下,后面会提到。先来分析 Integer Long Byte 。从源代码的角度来分析, Integer 包含了一个 int value, 其他的也有对应类型。而知晓, int 占用 4 个字节, long 则是 8 个字节, byte 占用 1 个字节。看图说话:


 

(图5)

 

 (图6)


(图7)

 

 

Integer对象空间大小是 12 字节(见图 5) Long 对象空间则是 16 个字节(见图 6 ), Byte 空间则是 9 个字节(见图 7 )。那么除去对象自身的状态 value ,得出的结论是,最后“空壳”对象都 8 字节?不能确定,因为还有类(静态)字段没有考虑,这些字段是否属于对象实例的一部分呢?很多书上面再三强调类的字段不属于对象实例,对于这种说法需要保持怀疑的态度?下面做一个“空壳”对象实验,代码如下:

 

/**

 * 和Object一样,没有任何添加对象状态。

 *  @author  mercy

 */

public   class  SameAsObject  extends  Object {

}

( 代码3 )

修改 MainClass程序, new  一个 SameAsObject 对象,重新 Heap Dump,结果图:

 


 

 

 (图8)

 

8 中表明,证明了“空壳”对象空间大小 8 字节,和 java.lang.Object 类似。目前还不能证明 Integer 的情况,因为 Integer 类的层次是 Integer <- Number <- Object 。虽然 Number 没有对象状态,可是也不能证明出去 value Integer 对象空间大小等于 Object 的。为了证明这一点,再扩展一下 SameAsObject, 使其成为父类,创建一个子类 ExtSameAsObject 。同样的方法,获取大小:

 

 

 (图9)

 

9 中, ExtSameAsObject 的空间大小还是 8 字。 证明了空壳对象大小等同于java.lang.Object 的。 那么自然地可以推导出java.lang.Double 也是 16 字节( 8 字节空壳对象 +8 字节的 double 类型 value)

 

细心的读者会发现,图8 9 中,笔者标记了红色区域,都有 Java frame 的标识。而在MainClass main 方法中,有一句: Object object =  new  ExtSameAsObject(); 这个对象是局部变量。说明什么问题呢? 两个问题:第一,正因为在方法内部执行,这个语句就是一个 frame, frame Java Stack 的组成单位。这个 frame 被压入栈,并且类型是 ExtSameAsObject ,同时带有一个对象的地址 #1 (当然这里不是实际地址,只是一个引用标识)。第二,即使是局部对象,对象仍然分配在 Java Heap 中。

 

既然“空壳”的Integer 对象,占用 8 个字节的大小,那么类的成员就不应该归入对象实例之中。从实验中,我们可以得出结论, 某个类的任何类成员的常量和变量,都不会分配(或计算)到该类的对象实例。

 

回到Sizeof 类,为什么 Sizeof 会计算出问题?虽然 Sizeof 类计算有误,不过它的思想还是几点值得借鉴:

 

第一、 作者深入了了解了Runtime#gc() 方法和 Runtime#runFinalization 方法的语义。这两个方法并不是实时执行,而是建议 JVM 执行,执行与否程序怎么知道呢 ? 在— _runGC 方法中,作者试图通过内存的变化来判断是否 GC GC 后,肯定会变化的),确实有道理。

 

第二、 通过N 次,求得平均数,比单次测试要精确很多。

 

第三、 没有开辟其他对象,不影响结果。

 

同时,不过作者忽略了如下情况:

 

第一、 GC不只是针对需要测试的对象,而是整个 JVM 。因此在 GC 的时候,有可能是 JVM 启动后,把其他没有使用的对象给 GC 了,这样造成了使用空间的变大,而 Heap 空间变小。

 

第二、 Runtime#freeMemory()方法,返回的数据是 近视值 ,不一定能够保证正确性。因此在后面的累计、求平均数来带了误差。

 

第三、 使用了Math#round() 方法,又带来了误差。

 

由于Sizeof 的不精确,不能作为测试基准。

 

那么,更多的疑问产生了。前面测试的都是单一对象,那么数组对象是如何分配的?

 

修改程序如下:

 

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

//Object object = new ExtSameAsObject();

Object [] object =  new   Object [0];

...

( 代码4 )

 

重新Heap Dump:


 (图10)

 

余下内容,看附件。错误内容已经修正,请下载Fix1文档

 

  • 大小: 50.6 KB
  • 大小: 42.8 KB
  • 大小: 64.1 KB
  • 大小: 99.4 KB
  • 大小: 31.8 KB
  • 大小: 32.3 KB
  • 大小: 24.6 KB
  • 大小: 54.9 KB
  • 大小: 22.4 KB
  • 大小: 27 KB
  • 大小: 58.3 KB
  • 大小: 45.3 KB
  • 大小: 27.6 KB
  • 大小: 56.4 KB
  • 大小: 28.1 KB
  • 大小: 11.9 KB
  • 大小: 82.9 KB
分享到:
评论
32 楼 hoszb 2010-08-28  
profile工具,我喜欢JRockit的jrmc,感觉功能比原来的Sun那个jvisualvm好用
31 楼 mercyblitz 2010-08-28  
hoszb 写道
mercyblitz 写道


写得不错,让我萌生了一个想法-递归计算空间大小。


发现这个代码还需要做大改动,有时候多个对象共享一个成员变量,还有时,一个对象循环引用自己:

public class A{
    A a = this;
}


更糟糕的是,刚发现的

不同的JVM,内存管理不一样, 导致一个引用的大小有可能不同,相同长度的数组占用的空间也有可能不同。

(1).32位和64位的JVM有不同

(2).不同厂商的相同位数的JVM也有不同

(3).相同厂商不同版本的JVM,如1.5,1.6的。。。也有可能不同

引用大小:SUN的32位JVM,4字节,SUM的64位JVM,8字节,JRockit的32位/64位JVM,引用都是4字节


另外JVM在server模式下的System.gc()方法难以生效,测试时要在client模式下测



以上这些还是已知的,还有未知的不同。。。。。


嗯,因此Profile工具是必要的,程序写的东西,不确定因素很多。
30 楼 hoszb 2010-08-28  
mercyblitz 写道


写得不错,让我萌生了一个想法-递归计算空间大小。


发现这个代码还需要做大改动,有时候多个对象共享一个成员变量,还有时,一个对象循环引用自己:

public class A{
    A a = this;
}


更糟糕的是,刚发现的

不同的JVM,内存管理不一样, 导致一个引用的大小有可能不同,相同长度的数组占用的空间也有可能不同。

(1).32位和64位的JVM有不同

(2).不同厂商的相同位数的JVM也有不同

(3).相同厂商不同版本的JVM,如1.5,1.6的。。。也有可能不同

引用大小:SUN的32位JVM,4字节,SUM的64位JVM,8字节,JRockit的32位/64位JVM,引用都是4字节


另外JVM在server模式下的System.gc()方法难以生效,测试时要在client模式下测



以上这些还是已知的,还有未知的不同。。。。。
29 楼 mercyblitz 2010-08-28  
kuanchang 写道
这么深入的帖子,竟然是新手帖???那些人给的?

这个我认了,大家都是高手!

民主就是这么来的!
28 楼 kuanchang 2010-08-28  
这么深入的帖子,竟然是新手帖???那些人给的?
27 楼 mercyblitz 2010-08-28  
hoszb 写道
我研究了一晚上,写了一个精确计算对象占用空间的代码,楼主有空帮我测试一吧下:

这个代码在符合我的电脑(64位CPU,装32位操作系统,32位sun的虚拟机)的内存使用情况。
我电脑内存使用情况是拿gc()后的内存增长量做测试的。


import java.lang.reflect.*;

//一个引用:4字节
//一个Object:8字节
//一个Integer:16字节 == (8 + 4) / 8 * 8
//一个int:4字节
//长度l的byte数组:(l+19)/8*8
//长度l的char/short数组:(l*2+19)/8*8 == (l+9)/4*8
//长度l的String:(l+1)/4*8+40
//长度l的int数组:(l*4+19)/8*8 ==(l+4)/2*8
//长度l的long数组:(l*8+19)/8*8 == (l+2)*8
public class Occupy {
	//不写不行...
	public static int occupyof(boolean variable) {return 1;}
	public static int occupyof(byte variable) {return 1;}
	public static int occupyof(short variable) {return 2;}
	public static int occupyof(char variable) {return 2;}
	public static int occupyof(int variable) {return 4;}
	public static int occupyof(float variable) {return 4;}
	public static int occupyof(long variable) {return 8;}
	public static int occupyof(double variable) {return 8;}
	
	public static int occupyof(Object object) {
		if (object == null)
			return 0;
		int size = 8;
		Class clazz = object.getClass();
		if (clazz.isArray()) {
			size = 4;//length变量是int型
			Class<?> componentType = clazz.getComponentType();
			if (componentType.isPrimitive())
				return occupyofSize(size + lengthOfPrimitiveArray(object) * sizeofPrimitiveClass(componentType));
			Object[] array = (Object[]) object;
			size += 4 * array.length;
			for (Object o : array)
				size += occupyof(o);
			return occupyofSize(size);
		}
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (Modifier.isStatic(field.getModifiers()))
				continue;//类成员不计
			Class<?> type = field.getType();
			if (type.isPrimitive())
				size += sizeofPrimitiveClass(type);
			else {
				size += 4;//一个引用型变量占用4个字节
				try {
					field.setAccessible(true);//可以访问非public类型的变量
					size += occupyof(field.get(object));
				} catch (Exception e) {
					size += occupyofConstructor(object, field);
				}
			}
		}
		return occupyofSize(size);
	}

	public static int sizeof(boolean variable) {return 1;}
	public static int sizeof(byte variable) {return 1;}
	public static int sizeof(short variable) {return 2;}
	public static int sizeof(char variable) {return 2;}
	public static int sizeof(int variable) {return 4;}
	public static int sizeof(float variable) {return 4;}
	public static int sizeof(long variable) {return 8;}
	public static int sizeof(double variable) {return 8;}
	
	public static int sizeof(Object object) {
		if (object == null)
			return 0;
		int size = 8;
		Class clazz = object.getClass();
		if (clazz.isArray()) {
			size = 4;//length变量是int型
			Class<?> componentType = clazz.getComponentType();
			if (componentType.isPrimitive())
				return size + lengthOfPrimitiveArray(object) * sizeofPrimitiveClass(componentType);
			Object[] array = (Object[]) object;
			size += 4 * array.length;
			for (Object o : array)
				size += sizeof(o);
			return size;
		}
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (Modifier.isStatic(field.getModifiers()))
				continue;//类成员不计
			Class<?> type = field.getType();
			if (type.isPrimitive())
				size += sizeofPrimitiveClass(type);
			else {
				size += 4;//一个引用型变量占用4个字节
				try {
					field.setAccessible(true);//可以访问非public类型的变量
					size += sizeof(field.get(object));
				} catch (Exception e) {
					size += sizeofConstructor(object, field);
				}
			}
		}
		return size;
	}
	
	private static int occupyofConstructor(Object object, Field field) {
		throw new UnsupportedOperationException("field type Contructor not accessible: " + object.getClass() + " field:" + field);
	}

	private static int sizeofConstructor(Object object, Field field) {
		throw new UnsupportedOperationException("field type Contructor not accessible: " + object.getClass() + " field:" + field);
	}

	/**
	 * 对象的大小 和 占用空间并不相等,就好象Windows下文件一样(大小为1字节时占用空间4k)
	 * 对象占用空间的增长以8个字节为单位,占用空间=大小对8的无条件进位法,
	 * 即occupy = (size + 8 - 1) / 8 * 8;   例如:
	 * 大小8字节:占用8字节,(new Object()就是占用8字节)
	 * 大小9字节:占用16字节
	 * 大小16字节:占用16字节
	 * 大小17字节:占用24字节
	 * @param size 大小,以字节为单位
	 * @return 占用空间
	 */
	private static int occupyofSize(int size) {
		return (size + 7) / 8 * 8;
	}

	private static int sizeofPrimitiveClass(Class clazz) {
		return clazz == boolean.class || clazz == byte.class ? 1 : clazz == char.class || clazz == short.class ? 2 : clazz == int.class || clazz == float.class ? 4
				: 8;
	}

	private static int lengthOfPrimitiveArray(Object object) {
		Class<?> clazz = object.getClass();
		return clazz == boolean[].class ? ((boolean[]) object).length
		: clazz == byte[].class ? ((byte[]) object).length
		: clazz == char[].class ?	((char[]) object).length
		: clazz == short[].class ? ((short[]) object).length
		: clazz == int[].class ?  ((int[]) object).length
		: clazz == float[].class ? ((float[]) object).length
		: clazz == long[].class ?	 ((long[]) object).length
		: ((double[]) object).length;
	}
	
	public static void main(String[] args) throws Throwable {
		System.out.println(occupyof(new String("Web.Zhu")));
	}

}



写得不错,让我萌生了一个想法-递归计算空间大小。
26 楼 hoszb 2010-08-28  
我研究了一晚上,写了一个精确计算对象占用空间的代码,楼主有空帮我测试一吧下:

这个代码在符合我的电脑(64位CPU,装32位操作系统,32位sun的虚拟机)的内存使用情况。
我电脑内存使用情况是拿gc()后的内存增长量做测试的。


import java.lang.reflect.*;

//一个引用:4字节
//一个Object:8字节
//一个Integer:16字节 == (8 + 4 + 7) / 8 * 8
//一个int:4字节
//长度l的byte数组:(l+19)/8*8
//长度l的char/short数组:(l*2+19)/8*8 == (l+9)/4*8
//长度l的String:(l+1)/4*8+40
//长度l的int数组:(l*4+19)/8*8 ==(l+4)/2*8
//长度l的long数组:(l*8+19)/8*8 == (l+2)*8
public class Occupy {
	//不写不行...
	public static int occupyof(boolean variable) {return 1;}
	public static int occupyof(byte variable) {return 1;}
	public static int occupyof(short variable) {return 2;}
	public static int occupyof(char variable) {return 2;}
	public static int occupyof(int variable) {return 4;}
	public static int occupyof(float variable) {return 4;}
	public static int occupyof(long variable) {return 8;}
	public static int occupyof(double variable) {return 8;}
	
	public static int occupyof(Object object) {
		if (object == null)
			return 0;
		int size = 8;//对象本身8字节
		Class clazz = object.getClass();
		if (clazz.isArray()) {
			size += 4;//length变量是int型
			Class<?> componentType = clazz.getComponentType();
			if (componentType.isPrimitive())
				return occupyofSize(size + lengthOfPrimitiveArray(object) * sizeofPrimitiveClass(componentType));
			Object[] array = (Object[]) object;
			size += 4 * array.length;
			for (Object o : array)
				size += occupyof(o);
			return occupyofSize(size);
		}
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (Modifier.isStatic(field.getModifiers()))
				continue;//类成员不计
			Class<?> type = field.getType();
			if (type.isPrimitive())
				size += sizeofPrimitiveClass(type);
			else {
				size += 4;//一个引用型变量占用4个字节
				try {
					field.setAccessible(true);//可以访问非public类型的变量
					size += occupyof(field.get(object));
				} catch (Exception e) {
					size += occupyofConstructor(object, field);
				}
			}
		}
		return occupyofSize(size);
	}

	public static int sizeof(boolean variable) {return 1;}
	public static int sizeof(byte variable) {return 1;}
	public static int sizeof(short variable) {return 2;}
	public static int sizeof(char variable) {return 2;}
	public static int sizeof(int variable) {return 4;}
	public static int sizeof(float variable) {return 4;}
	public static int sizeof(long variable) {return 8;}
	public static int sizeof(double variable) {return 8;}
	
	public static int sizeof(Object object) {
		if (object == null)
			return 0;
		int size = 8;
		Class clazz = object.getClass();
		if (clazz.isArray()) {
			size = 4;//length变量是int型
			Class<?> componentType = clazz.getComponentType();
			if (componentType.isPrimitive())
				return size + lengthOfPrimitiveArray(object) * sizeofPrimitiveClass(componentType);
			Object[] array = (Object[]) object;
			size += 4 * array.length;
			for (Object o : array)
				size += sizeof(o);
			return size;
		}
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (Modifier.isStatic(field.getModifiers()))
				continue;//类成员不计
			Class<?> type = field.getType();
			if (type.isPrimitive())
				size += sizeofPrimitiveClass(type);
			else {
				size += 4;//一个引用型变量占用4个字节
				try {
					field.setAccessible(true);//可以访问非public类型的变量
					size += sizeof(field.get(object));
				} catch (Exception e) {
					size += sizeofConstructor(object, field);
				}
			}
		}
		return size;
	}
	
	private static int occupyofConstructor(Object object, Field field) {
		throw new UnsupportedOperationException("field type Contructor not accessible: " + object.getClass() + " field:" + field);
	}

	private static int sizeofConstructor(Object object, Field field) {
		throw new UnsupportedOperationException("field type Contructor not accessible: " + object.getClass() + " field:" + field);
	}

	/**
	 * 对象的大小 和 占用空间并不相等,就好象Windows下文件一样(大小为1字节时占用空间4k)
	 * 对象占用空间的增长以8个字节为单位,占用空间=大小对8的无条件进位法,
	 * 即occupy = (size + 8 - 1) / 8 * 8;   例如:
	 * 大小8字节:占用8字节,(new Object()就是占用8字节)
	 * 大小9字节:占用16字节
	 * 大小16字节:占用16字节
	 * 大小17字节:占用24字节
	 * @param size 大小,以字节为单位
	 * @return 占用空间
	 */
	private static int occupyofSize(int size) {
		return (size + 7) / 8 * 8;
	}

	private static int sizeofPrimitiveClass(Class clazz) {
		return clazz == boolean.class || clazz == byte.class ? 1 : clazz == char.class || clazz == short.class ? 2 : clazz == int.class || clazz == float.class ? 4
				: 8;
	}

	private static int lengthOfPrimitiveArray(Object object) {
		Class<?> clazz = object.getClass();
		return clazz == boolean[].class ? ((boolean[]) object).length
		: clazz == byte[].class ? ((byte[]) object).length
		: clazz == char[].class ?	((char[]) object).length
		: clazz == short[].class ? ((short[]) object).length
		: clazz == int[].class ?  ((int[]) object).length
		: clazz == float[].class ? ((float[]) object).length
		: clazz == long[].class ?	 ((long[]) object).length
		: ((double[]) object).length;
	}
	
	public static void main(String[] args) throws Throwable {
		System.out.println(occupyof(new String("Web.Zhu")));
	}

}

25 楼 aswang 2010-08-19  
楼主钻研的很深入,值得学习!
24 楼 likeblood 2010-07-22  
个人以为 由于32为操作系统的原因 必须4字节为一个单元 因为只能是4的倍数 不够的也要补齐 因此 5字节的也是8字节 1字节的是4字节

64位的话 就是8字节一单元了
23 楼 zgz888 2010-07-15  
支持一下,有空详细看看
22 楼 kingwood2005 2010-07-15  
mark

好文章,写的不错
21 楼 zhao103804 2010-07-14  
amazeur 写道
zhao103804 写道
写的不错,我们项目中内存泄露都没有这样精密的计算过,研究对象的大小还没深入到这块,看来要想LZ多多学习了


看来使用的时候要多注意了。


确实,我们项目中解决的办法就是加些限制条件
比如说从数据库中查询几万条数据放到list里面
但是又不知道那个对象到底能装多少,所有只有加限制条件,一次少装点了。
20 楼 kongxx 2010-07-14  
收下,慢慢学习。
19 楼 mercyblitz 2010-07-13  
RednaxelaFX 写道
well……有有同学想自己计算HotSpot里Java对象的布局的话,请参考HotSpot自身所使用的逻辑:
hotspot/src/share/vm/classfile/classFileParser.cpp
找这个方法:
ClassFileParser::parseClassFile(),看2833行开始:
// Field size and offset computation

对象布局的计算就在里面,包括instanceKlass自身的布局(包含有Java类的静态变量)和Java对象实例的布局都在这里算。


谢谢,尽管我没有过(最近也在研究JVM实现),但是我比较偏好HotSpot的实现。不过我打算再写一篇计算篇,通过反向推到出来。

18 楼 RednaxelaFX 2010-07-13  
well……有同学想自己计算HotSpot里Java对象的布局的话,请参考HotSpot自身所使用的逻辑:
hotspot/src/share/vm/classfile/classFileParser.cpp
找这个方法:
ClassFileParser::parseClassFile(),看2833行开始:
// Field size and offset computation

对象布局的计算就在里面,包括instanceKlass自身的布局(包含有Java类的静态变量)和Java对象实例的布局都在这里算。
17 楼 RednaxelaFX 2010-07-13  
其实在32位HotSpot里java.lang.Integer和java.lang.Long的大小就是一样的啊……
原因是32位HotSpot的对象分配是8字节对齐的。当对象自身所需数据占不足8字节的倍数的时候就会在一些位置加上padding。“一些位置”一般是整个对象的末尾,也可能是在中间。

HotSpot是按成员类型来计算对象布局的。可以读一下这篇,讲得还不错:Java Objects Memory Structure
(看不到的话请自行想办法翻……)
这帖讲的内容适用于32位HotSpot。其它JVM则有各自不同的对象布局方式。例如说早期的Sun JVM(后来叫Classic VM)采用的对象布局就跟HotSpot的完全不同,而像SableVM采用的对象布局则相当有特色。JVM规范并没有对对象布局做定义——JVM实现可以用自己喜欢的方式来做。

要靠谱的Java对象大小数据,可以参考这帖使用JVMTI来获取:Again about determining size of Java object
VisualVM也是通过JVMTI来获取对象大小、数量等的信息。

以前我也发过一帖是拿32位HotSpot的对象布局来玩的,[标题党] 跑得好好的C#程序咋移植为Java就不够内存用了呢?——忽悠一把
16 楼 mercyblitz 2010-07-13  
beneo 写道
mercyblitz 写道
beneo 写道
java 对象的大小跟 虚拟机有关系,你自己只能估算,不可能准确。

如果你只是问JVM中一个 new object的大小,其实我也不知道


可以计算的,只是在x86和x64上面有不同而已,不然Profile工具就是失去了意义。

从JVM实现的角度,统一了对象大小。


我没说清楚

你这个是用jprofile计算出来的,如果你那IBM VM看的话,可能就不一样。再次不同的VM版本,对象的大小也不一样。

我们在日常使用过程中,我自己只能估算一下,开多少数组,耗费多少内存。那究竟包含数组的对象耗费多少heap,你是不能计算出来的,除非拿jprofile等工具去实际测。


对于不同的JVM的实现,这个没有关系,最多是多测试而已。
15 楼 beneo 2010-07-13  
mercyblitz 写道
beneo 写道
java 对象的大小跟 虚拟机有关系,你自己只能估算,不可能准确。

如果你只是问JVM中一个 new object的大小,其实我也不知道


可以计算的,只是在x86和x64上面有不同而已,不然Profile工具就是失去了意义。

从JVM实现的角度,统一了对象大小。


我没说清楚

你这个是用jprofile计算出来的,如果你那IBM VM看的话,可能就不一样。再次不同的VM版本,对象的大小也不一样。

我们在日常使用过程中,我自己只能估算一下,开多少数组,耗费多少内存。那究竟包含数组的对象耗费多少heap,你是不能计算出来的,除非拿jprofile等工具去实际测。
14 楼 mercyblitz 2010-07-13  
beneo 写道
java 对象的大小跟 虚拟机有关系,你自己只能估算,不可能准确。

如果你只是问JVM中一个 new object的大小,其实我也不知道


可以计算的,只是在x86和x64上面有不同而已,不然Profile工具就是失去了意义。

从JVM实现的角度,统一了对象大小。
13 楼 beneo 2010-07-13  
java 对象的大小跟 虚拟机有关系,你自己只能估算,不可能准确。

如果你只是问JVM中一个 new object的大小,其实我也不知道

相关推荐

    深入Java虚拟机(原书第2版).pdf【附光盘内容】

    《深入Java虚拟机(原书第2版)》,原书名《Inside the Java Virtual Machine,Second Edition》,作者:【美】Bill Venners,翻译:曹晓钢、蒋靖,出版社:机械工业出版社,ISBN:7111128052,出版日期:2003 年 9 ...

    125集专攻JAVA基础 JAVA零基础入门学习视频教程 动力节点JAVA视频教程.txt

    北京动力节点-Java编程零基础教程-111-Java基本语法-方法详解-返回值与return的深入讲解.avi 北京动力节点-Java编程零基础教程-112-Java基本语法-方法详解-无返回值与void.avi 北京动力节点-Java编程零基础教程-...

    Java优化编程(第2版)

    展示了如何提升Java应用性能,并且给出了优化前与优化后的Java应用程序的性能差别,以实际的实例与数字告诉你,为什么不可以这么做,应该怎么做,深入分析了影响Java应用程序性能的根本原因。本书不是教你怎样使用...

    深入理解Android:卷I--详细书签版

     笔者认为,本书最大的特点在于,较全面、系统、深入地讲解了Android系统中的几大重要组成部分的工作原理,旨在通过直接剖析源代码的方式,引领读者一步步深入于诸如Binder、 Zygote、Audio、Surface、Vold、Rild...

    什么是Java对象分配率?

    类似“不可持续的内存分配率”和“你需要维持低的内存分配率”这样的短语看...这是全部的内容——没那么神秘,仅仅是指 Java 代码在一定时期内内存分配的大小。  不过只知道这一点没有太大的意义。如果你能忍受,我将

    基于JAVA的搜索引擎 lucene-2.2.0

    必须深入地理解IndexWriter索引器初始化的过程,以及在构造一个IndexWriter索引器过程中涉及到的一些其他的类,应该深入研究这些相关的类的定义。这样才能有助于深化IndexWriter索引器构造的原理机制。 可见,...

    Java完美编程(第3版).pdf

     《java完美编程(第3版)》侧重于面向对象设计,通过浅显易懂的语言和代码,很好地平衡了完整实例和解释性讨论,并围绕着java语言的特征全面、透彻地介绍诸多编程技术。书中全面介绍java语言已经实现的封装、继承和...

    java的微型版本j2me进行贪吃蛇手机游戏

    在编写游戏程序的过程中将程序设计的知识点有机地分散在游戏中,是设计语言众多的对象、属性、方法以及程序开发工具的各种设置和操作都变得具体、形象、直观,通俗易懂,深入浅出。我想我们能够通过游戏程序的设计学会...

    Java并发编程实战

    本书深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及验证线程安全的规则...

    java猜数字游戏课程设计.doc

    本课程设计是为了配合Java程序设计课程,帮助学生深入学习掌握Java语言,熟练运用 这个工具而设立的课程。通过课程设计各个项目的综合训练,加强学生对JAVA语言的理 解,检验学生对专业理论知识理解与掌握的程度,...

    一个java正则表达式工具类源代码.zip(内含Regexp.java文件)

    这个工具类目前主要有25种正规表达式(有些不常用,但那时才仔细深入的研究了一下正规,写上瘾了,就当时能想到的都写了): 1.匹配图象; 2 匹配email地址; 3 匹配匹配并提取url ; 4 匹配并提取http ; 5.匹配日期 6...

    Java 虚拟机面试题全面解析(干货)

    Java 虚拟机面试题全面解析,《深入理解Java虚拟机》干货版,自己总结,希望能够帮助大家,免费下载~什么是类加载机制? 虚拟机和物理机的区别是什么? 运行时栈帧结构 Java方法调用 什么是方法调用? Java的方法调用,...

    Spring技术内幕:深入解析Spring架构与设计原理

    Spring技术内幕:深入解析Spring架构与设计原理 1/2资源大小:59MB 上传日期:2011-11-15 资源积分:5分 下载次数:30 上 传 者:musehilt 资源类型:文档 标 签: Spring技术内幕 深入解析 Spring 架构与设计原理 ...

    编程思想下篇

    由于上传文件大小限制该资源为上下篇 本资源为下篇 第1章 对象导论 1.1 抽象过程 1.2 每个对象都有一个接口 1.3 每个对象都提供服务 1.4 被隐藏的具体实现 1.5 复用具体实现 1.6 继承 1.6.1 “是一个”(is-a)与...

    深入云计算 MongoDB管理与开发实战详解pdf.part1

    11.6.2 变更oplog的大小 11.6.3 复制的认证问题 11.7 本章小结 第12章 sharding 分片 12.1 分片简介 12.2 MongoDB中的自动分片 12.3 片键 12.3.1 将已有的集合分片 12.3.2 递增片键还是随机片键...

    深入云计算 MongoDB管理与开发实战详解pdf.part2

    11.6.2 变更oplog的大小 11.6.3 复制的认证问题 11.7 本章小结 第12章 sharding 分片 12.1 分片简介 12.2 MongoDB中的自动分片 12.3 片键 12.3.1 将已有的集合分片 12.3.2 递增片键还是随机片键...

    java项目开发实录第四版源码-Guide-to-QC-and-QI:本指南适用于进入量子计算和量子信息科学领域的研究人员

    java项目开发实录第四版源码QC 和 QI 指南 “……物理定律对减小计算机的尺寸没有任何障碍,直到比特达到原子的大小,并且量子行为占主导地位。” 理查德·费曼 (1985) 本指南适用于进入量子计算和量子信息科学领域...

    Jootil:为任何 JavaFX 应用程序提供关键管道的 Java 库

    一个全面的 Java 库,为 JavaFX 应用程序提供额外的有用管道,使您可以专注于实际开发它。 Jootil 不需要凌乱的 XML 或预设配置。 它旨在简单地提供任何应用程序想要的功能。 特征 用于在 XML 文件中加载和保存应用...

Global site tag (gtag.js) - Google Analytics