`

计算java对象的大小的方法

    博客分类:
  • Java
 
阅读更多

   最近这段时间在看Ehcache的源码,突然想起如何估算内存中的缓存空间的大小呢,缓存空间 = ∑ object_1 + object_2 + ... + object_N ,就必须计算每个缓存对象的大小。我们知道java内存分为两种,堆内存和栈内存,栈是Java线程运行的独立空间,而堆内存是多线程运行的公共空间。堆是java对象实际存储的地点。那么计算对象大小就是计算对象在堆中占用内存空间的大小。

    于是在互联网上收集了几种计算策略,在本文中作简要的归纳和总结。一种就是通过java对象序列化的方式,用序列化后字节流的大小来粗略描述java对象大小;一种是通过Java 虚拟机工具接口(Java Virtual Machine Tool Interface,JVMTI)里面的Instrumentation对象中提供的getObjectSize方法;最后一种通过创建大量的对象,然后通过计算堆内存的的使用情况,然后通过取平均值的方式计算对象大小。下面将给出几种计算方式的源码。

   先给出基类AbstractTestCase.java,基类中完成待测试对象的初始化。

 

import java.util.HashMap;
import java.util.Map;

public abstract class AbstractTestCase {
	
	//the count method's name 
	private String name = null;
	
	public static final Object aNullObject = new Object();
	
	public static final byte aByte = (byte)255;
	
	public static final char aChar = 'a';
	
	public static final char aChineseChar = '中';
	
	public static final short aBufferedShort = 0;
	
	public static final short aUnBufferedShort = (short)65535;
	
	public static final int aBufferedInt = 0;
	
	public static final int aUnBufferedInt = 100000;
	
	public static final long aLong = 100000000L;
	
	public static final float aFloat = 5000.0f;
	
	public static final double aDouble = 109.21d;
	
	public static final String aNullString = null;
	
	public static final String aConstantString = "0123456789";
	
	public static final String aNewConstructString = new String("9876543210");
	
	public static final Object[] aNullObjectArray = new Object[0];
	
	public static final Object[] aOneObjectArray = new Object[1];
	
	public static final Object[] aTwoObjectArray = new Object[2];
	
	public static final Object[] aThreeObjectArray = new Object[3];
	
	public static final Object[][] aNullTwoDimensionObjectArray = new Object[0][0];
	
	public static final byte[][] aBigByteArray = new byte[8*1024][8*1024];
	
	//result Map
	private static final Map<String,Long> result = new HashMap<String,Long>(0);
	private static final Map<String,Object> data = new HashMap<String,Object>(0);
	
	public static final String resultString = "the 【%s】 object use 【%d】 bytes memory size "; 
	
	static {
		
		data.put("aNullObject", aNullObject);
		data.put("aByte", aByte);
		data.put("aChar", aChar);
		data.put("aChineseChar", aChineseChar);
		data.put("aBufferedShort", aBufferedShort);
		data.put("aUnBufferedShort", aUnBufferedShort);
		data.put("aBufferedInt", aBufferedInt);
		data.put("aUnBufferedInt", aUnBufferedInt);
		data.put("aLong", aLong);
		data.put("aFloat", aFloat);
		data.put("aDouble", aDouble);
		
		data.put("aNullString", aNullString);
		data.put("aConstantString", aConstantString);
		data.put("aNewConstructString", aNewConstructString);
		data.put("aNullObjectArray", aNullObjectArray);
		data.put("aOneObjectArray", aOneObjectArray);
		data.put("aTwoObjectArray", aTwoObjectArray);
		data.put("aThreeObjectArray", aThreeObjectArray);
		data.put("aNullTwoDimensionObjectArray", aNullTwoDimensionObjectArray);
		
		data.put("aBigByteArray", aBigByteArray);
		
		
	}
	
	public AbstractTestCase(String name){
		this.name = name;
	}
	
	//实现策略
	public abstract long countObjectSize(Object obj);
	
	//测试用例
	public void countTheResult(){
		for(String testCaseName : data.keySet()){
			Object obj = data.get(testCaseName);
			Long count = this.countObjectSize(obj);
			result.put(testCaseName, count);
		}
	}
	
	//打印结果
	public void printTheResult(){
		System.out.println("the " + name + " count method run start.........................");
		for(String testCaseName : result.keySet()){
			System.out.println(String.format(resultString, testCaseName,result.get(testCaseName)));
		}
		System.out.println("the " + name + " count method run finished .........................");
		System.out.println("~~  :)~");
	}
	
}

    1、使用Instrumentation对象的getObjectSize方法

 

    

import sizeof.agent.SizeOfAgent;


public class TestCaseUseJVMTI extends AbstractTestCase {

	public TestCaseUseJVMTI() {
		super("JVMTI");
	}

	@Override
	public long countObjectSize(Object obj) {
		return SizeOfAgent.fullSizeOf(obj);
	}
	
}

   运行过程中,需要下载sizeofag.jar,放在Eclipse工程目录/lib/下,添加到classpath,并添加虚拟机运行参数“-javaagent:lib/sizeofag.jar”。

 

2、使用java序列化的方式计算对象大小

 

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;

/**
 * @author thinkpad
 *
 */
public class TestCaseUseSerialization extends AbstractTestCase{

	public TestCaseUseSerialization() {
		super("Serialization");
	}
	
	//重写Write方法,只计数,不输出
	private class CountOutputStream extends OutputStream {  
		long count = 0;  
		
		@Override
		public void write(int b) throws IOException {  
			count++;   
		}  
	} 
	
	@Override
	public long countObjectSize(Object obj) {
		if (obj == null) 
			return 0L;
		if (!(obj instanceof Serializable)) {
			return 0L;
			//throw new IllegalArgumentException("没有实现序列化接口");
		}
		long result = 0L;
		try{
			CountOutputStream buffer = new CountOutputStream();
			ObjectOutputStream out = new ObjectOutputStream(buffer);
			out.writeObject(obj);
			out.close();
			result = buffer.count;
		}catch(Exception e){
			e.printStackTrace();
			throw new RuntimeException();
		}
		
		return result;
	}
}

 3、国外一个兄弟写的一个程序计算对象大小 链接地址

 

  

public class Sizeof
{
    public static void main (String [] args) throws Exception
    {
        //System.exit(-1);
    	// 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
            //object = new Object();
            //计算byte
            //object = (byte)-129;
            //计算char
            //object = '0';
            //计算中文char
            //object = '中';
            //计算short -128 到  127均已经缓存
            //object = (short)0;
            //计算int 0
            //object = (int)0;
            //计算int 超出缓存  10000  -127~128
            //object = (int)10000; 
            //计算long 超出缓存
            //object = 10000L;
            //计算float 
            //object = 0f;
            //计算double
            //object = 0d;
            object = new String("");
            //计算长度为0的object数组
            //object = Array.newInstance(Object.class, 0);
            //计算长度为1的object数组
            //object = Array.newInstance(Object.class, 1);
            //计算长度为2的object数组
            //object = Array.newInstance(Object.class, 2);
            //计算长度为3的object数组
            //object = Array.newInstance(Object.class, 3);
            //object = Array.newInstance(Character.class, 0);
            
            
            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

  

   测试主方法:

   

public class TestMain {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		AbstractTestCase [] testes = {
				new TestCaseUseJVMTI(),
				new TestCaseUseSerialization()
		};
		for(AbstractTestCase item :testes){
			item.countTheResult();
			item.printTheResult();
		}
	}	
}

 

 

    对比和分析和对比,JVMTI是官方提供的计算Object大小的方式,应该算是比较好,但是官方的方法说明中也明确表示也是一种模糊的计算方式。

   序列化的方式,应该是一定程度上反映Java对象内存的大小,但是序列化的方式对其影响很大,例如序列化过程中是否压缩,字符串的编码方式是否相同(JVM内部是用UTF-16来保存,而某种序列化方式采用UTF-8来保存)等等。

   采用对象统计学求平均值的办法,从统计结果来看也是一种比较好的近似的方法。

 

  参考文章:

 

老外的文章  http://www.jroller.com/maxim/entry/again_about_determining_size_of

大神的博客  http://rednaxelafx.iteye.com/blog/482058

iteye上讨论计算java对象大小话题  http://www.iteye.com/topic/580802

深入对象大小  http://www.iteye.com/topic/710998

讲解instrument的用法 http://blog.csdn.net/ykdsg/article/details/12080071

 

本文的附件中Dive in Java Object size.doc是《深入对象大小》这篇博文中博主的总结,大家可以下载来看看,一定会有收获的哦。

 

 

分享到:
评论

相关推荐

    计算java对象大小

    NULL 博文链接:https://sdfx.iteye.com/blog/2201116

    计算一个Java对象占用字节数的方法

    主要介绍了计算一个Java对象占用字节数的方法,较为详细的分析了Java中各类对象所占用的字节数,需要的朋友可以参考下

    测量Java对象大小的demo

    注入javaagent使用Instrumentation测量Java对象的大小

    java 通过sizeOf计算一个对象的大小

    NULL 博文链接:https://312256159-qq-com.iteye.com/blog/1582196

    JAVA对象所占内存大小计算例子

    JAVA对象所占内存大小计算例子,博文地址:http://blog.csdn.net/u012787710/article/details/53164226

    java 对象 内存 大小

    有效测量出 java 对象 内存 大小 可供缓存等计算

    统计缓存(java对象所占的内存)大小

    统计缓存大小(查看java对象所占的内存大小).

    计算机后端-Java-Java核心基础-第15章 面向对象07 21. 接口练习:比较对象大小.avi

    计算机后端-Java-Java核心基础-第15章 面向对象07 21. 接口练习:比较对象大小.avi

    Java 对象(数组)占多大空间(几个字节) 手把手做实验

    命令行: java -XX:+PrintCommandLineFlags -version 查看jvm默认参数 分别是 -XX:+UseCompressedOops 和 -XX:+UseCompressedClassPointers 这2个参数都是默认开启(+代表开启,-代表关闭) UseCompressedOops:普通...

    java源码包---java 源码 大量 实例

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    史上最全Java面试题目大集合

    面向对象计算始于这个基本概念,20. 即现实世界可以被描绘成一系列完全自治、封装的对象,21. 这些对象通过一个受保护的接口访问其他对象。 22. 多态性:多态性是指23. 允许不同24. 类的对象对同25. 一消息作出响应...

    [java]读书笔记整理:一切都是对象

    这一约束限制了程序的灵活性,所以虽然某些java数据存储于堆栈中——特别是对象引用,但是java对象并不存储其中。 3) 堆 一种通用的内存池(也位于RAM区),用于存放所有的java对象。堆不同于堆栈的好处是:编译器...

    java源码包4

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    java源码包3

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    JAVA上百实例源码以及开源项目源代码

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    JAVA上百实例源码以及开源项目

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    如何计算Java对象占用了多少空间?

    在Java中没有sizeof运算符,所以没办法知道一个对象到底占用了多大的空间,但是在分配对象的时候会有一些基本的规则,我们根据这些规则大致能判断出来对象大小,需要的朋友可以参考下

    java源码包2

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

Global site tag (gtag.js) - Google Analytics