论坛首页 Java企业应用论坛

深入Java对象大小

浏览 19886 次
该帖已经被评为新手帖
作者 正文
   发表时间:2010-07-14  
amazeur 写道
zhao103804 写道
写的不错,我们项目中内存泄露都没有这样精密的计算过,研究对象的大小还没深入到这块,看来要想LZ多多学习了


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


确实,我们项目中解决的办法就是加些限制条件
比如说从数据库中查询几万条数据放到list里面
但是又不知道那个对象到底能装多少,所有只有加限制条件,一次少装点了。
0 请登录后投票
   发表时间:2010-07-22  
个人以为 由于32为操作系统的原因 必须4字节为一个单元 因为只能是4的倍数 不够的也要补齐 因此 5字节的也是8字节 1字节的是4字节

64位的话 就是8字节一单元了
0 请登录后投票
   发表时间:2010-08-19  
楼主钻研的很深入,值得学习!
0 请登录后投票
   发表时间:2010-08-28   最后修改: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")));
	}

}

0 请登录后投票
   发表时间: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")));
	}

}



写得不错,让我萌生了一个想法-递归计算空间大小。
0 请登录后投票
   发表时间:2010-08-28  
这么深入的帖子,竟然是新手帖???那些人给的?
0 请登录后投票
   发表时间:2010-08-28  
kuanchang 写道
这么深入的帖子,竟然是新手帖???那些人给的?

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

民主就是这么来的!
0 请登录后投票
   发表时间: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模式下测



以上这些还是已知的,还有未知的不同。。。。。
0 请登录后投票
   发表时间: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工具是必要的,程序写的东西,不确定因素很多。
0 请登录后投票
   发表时间:2010-08-28  
profile工具,我喜欢JRockit的jrmc,感觉功能比原来的Sun那个jvisualvm好用
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics