分享
 
 
 

你知道数据大小吗?

王朝other·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

摘要:

过去很多年里面,许多的Java开发人员都一直在问一个问题:“一个Java对象到底耗费多少内存呢?”在本文中,Vladimir Roubtsov用以前的解决方案来解释了这个问题,在此之外,基于他的经验演示了内存的使用,并且还提供了一些技巧来让你的Java程序更加高效。

作者:Vladimir Roubtsov

近来,我们帮助开发了一个Java服务器,这是一个类似内存数据库的应用。那就是说,我们特别强调设计,因为设计要特别考虑在内存中缓存大量的数据来提高查询的性能。

一旦我们得到运行的原型,那么在数据从硬盘上宰入经过分析以后,我们自然的决定了数据存储的轮廓。不太满意的初始效果,促使我们寻找更好的解决方案。

工具:

既然Java有目的的隐藏了很多内存管理的细节信息,要发现你的对象要消耗多少内存的确要花一些功夫。你可以使用Runtime.freeMemory()方法来测量在一个或者多个对象被分配前前后的堆的变化值。有一些文章,例如(Ramchander Varadarajan's "Question of the Week No. 107" (Sun Microsystems, September 2000) and Tony Sintes's "Memory Matters" (JavaWorld, December 2001))都详细地介绍了这些方法。但是不幸的是,这些先前的文章的解决方案的失败在于实现中使用了一个错误的Runtime方法。即使后来的文章也有它的不完整性。

l 调用Runtime.freeMemory() 方法提供的功能是不够的,因为JVM可以在任何时候(只要需要,特别是在运行垃圾收集的时候)决定增加它的当前的堆大小。除非在运行的时候已经使用了参数-Xmx指定了堆的最大值,否则我们应该使用Runtime.totalMemory()-Runtime.freeMemory()作为在使用的堆大小。

l 执行单个Runtime.gc()方法并不能保证有效的请求垃圾收集。举例来说,我们可以请求对象的finalizer运行正常。既然Runtime.gc()不能保证阻塞到垃圾处理,那么一直等待到当堆大小稳定以后是一个很好的办法。

l 如果轮廓类创建了一个静态的数据作为先前的类初始化的一部分,那么堆内存对于第一个类实例的分配的空间应该包括这个数据。我们应该忽略被第一个类实例消耗的堆空间。

考虑这些问题:我们给出了一个Sizeof,作为一个工具来查看各种Java核心和应用类。

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

{

Object object = null;

// Instantiate your data here and assign it to object

object = new Object ();

//object = new Integer (i);

//object = new Long (i);

//object = new String ();

//object = new byte [128][1]

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

objects = null;

}

private static void runGC () throws Exception

{

// It helps to call Runtime.gc()

// using several method calls:

for (int r = 0; r

}

private static void _runGC () throws Exception

{

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

for (int i = 0; (usedMem1

{

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

Sizeof的关键方法是runGC() 和usedMemory()方法,我使用了runGC()这样的封装方法来调用_runGC()几次,是为了让这个方法有更加明显的效果。

注意我调用runGC()方法的地方,你可以在heap1和heap2编辑你的代码,加入你任何你感兴趣的例子。

也请注意Sizeof怎么样输出对象的大小,数据的传递闭包要求被用所得count类实例所用到,然后被count整除。对于大多数类来说,这个结果将是单个类实例对象所耗费的内存大小,包括所有它自己的成员域。内存的界限值不同于由一些商业的工具报告的影子内存界限(比如说,如果一个对象有一个int[],那么它的内存消耗将显得很特别)。

结果:

让我们来对一些类使用这个工具,察看是否结果和我们预想的一样。

注意:以下的结果都是基于Windows平台的jdk1.3.1版本,并不能保证所有的平台或者jdk版本都得到相同的信息。

l java.lang.Object

这个所有对象的基类作为我们的第一个例子。对于java.lang.Object我们将得到:

'before' heap: 510696, 'after' heap: 1310696

heap delta: 800000, {class java.lang.Object} size = 8 bytes

所以,一个简单的Object对象要占用8个字节的内存空间,当然我们不能希望它所占的空间是0,因为每个实例都至少必须包含一些最基本的操作,比如equals(), hashCode(), wait()/notify()等。

l java.lang.Integer

我和我的同事都经常封装本地的int到Integer的实例中去,以便于我们能在集合的对象中使用它们,那样做到底要耗费多少内存呢?

'before' heap: 510696, 'after' heap: 2110696

heap delta: 1600000, {class java.lang.Integer} size = 16 bytes

这个16字节的结果比我们预想的要糟糕,因为一个int值恰好是4个字节,但是使用了Integer以后,多使用了3倍的空间。

l java.lang.Long

Long看起来应该比Integer使用更多的空间,可是事实并非如此:

'before' heap: 510696, 'after' heap: 2110696

heap delta: 1600000, {class java.lang.Long} size = 16 bytes

很明显,因为一种特别的JVM实现必须符合特定的CPU类型,所以事实上的对象大小在堆中所占的空间必须和低级的内存边界对齐。看起来一个Long是一个8字节的大小的Object对象加上8字节用来保存long值。相比之下,Integer就有4个字节没有使用的空间。所以,应该是JVM强制对象使用8字节作为字的边界。

l Arrays

接下来比较一些基本类型的数组,比较有指导意义,能够部分的发现一些隐藏的信息和证明另一些流行的诡计:使用一个size-1的数组封装基本类型来当作对象。通过修改Sizeof.main()来使用一个循环增加数组的长度。然后能够得到int数组:

length: 0, {class [I} size = 16 bytes

length: 1, {class [I} size = 16 bytes

length: 2, {class [I} size = 24 bytes

length: 3, {class [I} size = 24 bytes

length: 4, {class [I} size = 32 bytes

length: 5, {class [I} size = 32 bytes

length: 6, {class [I} size = 40 bytes

length: 7, {class [I} size = 40 bytes

length: 8, {class [I} size = 48 bytes

length: 9, {class [I} size = 48 bytes

length:

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有