文档章节

new一个Object对象占用多少内存?

杨尚川
 杨尚川
发布于 2014/03/15 08:16
字数 1806
阅读 3053
收藏 118

Java的自动内存管理机制(automatic storage management system known as a garbage collector)省却了很多编码工作,大大地提高了Java的生产力,而且JVM的性能也越来越好,特别是G1的出现,改善了垃圾回收中stop the world的状况。

 

也许很多人都没有考虑过这个问题,new一个Object对象到底占用多少内存呢( Object obj = new Object() )?

 

这里很明确的是obj是一个指向对象的引用(reference - there are three kinds of reference types: class types,array types, and interface types),引用的长度决定了Java的寻址能力,32位的JDK是4字节,64位的JDK是8字节(指针未被压缩的情况下)。

 

因为obj对象没有任何数据(field),会在堆上为它分配空间吗?如果分配空间,里面存储了什么内容?

 

以面向对象的思维来分析,对象封装了数据和行为,是一个统一的整体,虽然obj对象没有数据,但是有行为(Object类定义了12个方法)。当我们执行完new操作后,obj的值为堆内存的地址,既然obj都指向一块内存了,说明是会在堆上为其分配空间的。

 

那么分配的空间有多大,存储了什么内容呢?The Java Virtual Machine Specification Java SE 7 EditionThe Java Language Specification Java SE 7 Edition里面没有找到相关的描述,这很可能是属于JVM实现自由控制的范畴了。我们可以利用JDK自带的工具jvisualvm.exe来查看分配的空间有多大。为了方便在jvisualvm中查看对象占多少内存,这里使用一个私有的静态内部类EmptyObject来替代Object,因为类定义为空,所以可以等同对待EmptyObject和Object。

 

/**
 * 构造一个无任何字段的空对象占多少内存
 * @author 杨尚川
 */
public class EmptyTest {
    
    public static void main(String[] args) throws InterruptedException{
        //加到集合中,使垃圾无法回收
        List<EmptyObject> emptys = new ArrayList<>();
        for(int i=0;i<100;i++){
            emptys.add(new EmptyObject());
        }
        //打开jvisualvm,查看EmptyObject的大小为16字节
        Thread.sleep(60*1000);
    }
    private static class EmptyObject{}
}

 

 

我们在这里面通过new不同的对象数(for循环次数),来分析内存占用,new 1个对象是16字节,new 2个对象是32字节,new 100个对象是1600字节,通过很多次的尝试,我们从jvisualvm里面可以看到 字节数=对象数*16 ,我们有理由相信对象数跟字节数的线性关系。从这里可以看出,jvisualvm显示的内存占用跟引用的4字节或8字节是没有关系的,也就是说,jvisualvm显示的是堆内存占用,这也很好理解,毕竟所有引用的字节占用是固定的。8字节是引用,16字节是堆内存,总共是8+16=24字节,所以new一个Object对象占用8+16=24字节(64位JDK)

 

如果JDK是32位,按如上分析方法可知new一个Object对象占用4+8=12字节(32位JDK),如下图所示:

 

64位JDK:

32位JDK:

 

 

那么分配的16字节(8字节)的堆内存中存储了什么内容呢?当我们Object obj = new Object();的时候,在栈内存中有一个引用obj,他可能是32位也可能是64位,obj实质只是一个内存地址,那么当我们调用obj.xxx()的时候,JVM怎么知道obj是哪个类的实例呢?所以,可以大胆地推测,obj对象的16字节(8字节)的堆内存中记录了对象属于哪个类的信息,问题是这16字节(8字节)的结构是什么样的?不清楚!

 

不过我们仍然可以大胆地猜测一下,通过上面的64位和32位的堆内存大小对比分析发现,堆内存分配的大小是引用的两倍,上面我们已经猜测堆内存中会记录对象是哪个类的实例,如何记录呢?因为类对象是放置在方法区的,类对象本身也是一个对象,因此可以通过一个引用指向它,所以堆内存有可能就是放置了两个引用,指向两个对象。分析到这里,事情就比较明朗了,堆内存中可能就放置了两个内存地址,一个指向EmptyObject.class(在实验代码中用EmptyObject来代替Object),一个指向什么呢?不清楚!

 

Inside the Java 2 Virtual Machine 2nd by Bill Venners的5.3.5中有这么一段描述:

 

The Java virtual machine specification is silent on how objects should be represented on the heap. Object representation--an integral aspect of the overall design of the heap and garbage collector--is a decision of implementation designers. 

 

好了,事情最终清楚了,JVM规范并没有规定Java对象在堆中是如何表示的,对象的表示是堆和垃圾收集器的整体设计的一个组成部分,这是由JVM实现的设计师来决定的。 因此,如果我们真的想搞清楚对象是如何表示的,那么需要查询HotSpot VM或是其他JVM实现的相关资料。

 

在淘宝工程师莫枢(撒迦)的《JVM分享》PPT的第112页介绍了“HotSpot中的Java对象布局”,这真是现在关心的内容,通过PPT的介绍说明前面的猜测是对的,如下图所示:



 

 

 

 

 

 

 

我们研究new一个Object对象占多少内存可能没什么实际意义,因为我们在编程的时候就可以确定对象树,基本可以确定对象大小,除了变长字段,当然,变长字段我们一般也会有长度限制。所以我们真正关心的是所有数据最终的大小,也就是数据库的大小。

 

面向对象的分析、设计和编程都把“封装”奉为圭臬,“分层”更是架构设计中至关重要的设计准则。因为只有这样,才能实现基本的解耦、让协作分工成为可能,满足工业要求的最大化生产力的最终目标。

 

那么这种没有什么实际意义的问题为什么要研究呢?我觉得只能用三个字来形容:好奇心好奇心是驱使我们研究技术的强大推力,当我们工作了很多年,尤其是在不重视技术的公司,我们对技术还有激情吗?保持一颗敏感好奇的心,也许技术之路可以走的更长更远。

 

这篇文章的重点是展示一种分析问题的思路,要大胆猜测,小心求证,追本溯源,引经据典。求证方式:查找标准规范、查找经典权威书籍、自己做实验、查找源代码等。

 

参考资料:

1、Java™ Virtual Machine Technology

2、The Java Virtual Machine Specification Java SE 7 Edition

3、The Java Language Specification Java SE 7 Edition

4、Inside the Java 2 Virtual Machine 2nd by Bill Venners

5、JVM分享

6、http://www.javamex.com/classmexer/

© 著作权归作者所有

杨尚川

杨尚川

粉丝 1102
博文 220
码字总数 1624053
作品 12
东城
架构师
私信 提问
加载中

评论(9)

杨尚川
杨尚川 博主

引用来自“OSC_saTTWP”的评论

看这个,感觉想到东西好多呀,gc,线程锁,反射,等,就锁的各种变化,各种引用,就够花好多时间。。
是的,深挖无止境
OSC_saTTWP
OSC_saTTWP
看这个,感觉想到东西好多呀,gc,线程锁,反射,等,就锁的各种变化,各种引用,就够花好多时间。。
罗慢慢
罗慢慢
深入Java虚拟机中有两个关于mark word的地方稍微涉及到这些内容,但理解的不是特别清楚,看过这篇博客之后豁然开朗!谢谢川哥!
陈文锦的秘密
陈文锦的秘密
没研究过
杨尚川
杨尚川 博主

引用来自“飞翔的猴子”的评论

这排版 是我打开的方式不对么?

排版是差点,虚心接受批评21
木川瓦兹
木川瓦兹
这排版 是我打开的方式不对么?
麦壳原野
麦壳原野
好文
FuYung
FuYung
79
guoxue668
guoxue668
历害
Java 基本数据类型 sizeof 功能

Java基本数据类型 int 32bit short 16bit long 64bit byte 8bit char 16bit float 32bit double 64bit boolean 1bit,This data type represents one bit of information, but its "size" is......

长平狐
2013/01/06
79
0
关于Java占用内存的研究

这篇文章来自newsmthBBS java版原版主zms的经验总结,感觉挺不错。转到这里,以供参考。 最近对程序占用内存方面做了一些优化,取得了不错的效果,总结了一些经验 简要说一下,相信会对大家写...

晨曦之光
2012/04/12
142
0
关于Java占用内存的研究--程序员必读

最近对程序占用内存方面做了一些优化,取得了不错的效果,总结了一些经验 简要说一下,相信会对大家写出优质的程序有所帮助 下面的论述针对32位系统,对64位系统不适用,后叙 经常你写了一个...

余路
2012/05/03
252
0
new Java对象占用内存分析

最近在读《深入理解Java虚拟机》,对Java对象的内存布局有了进一步的认识,于是脑子里自然而然就有一个很普通的问题,就是一个Java对象到底占用多大内存? 在网上搜到了一篇博客讲的非常好:...

pricker
2015/08/13
128
0
Java对象占用内存大小

new Object()将占用多少bytes的内存空间? 原生类型(primitive type)的内存占用 Primitive Type Memory Required(bytes) ————————————————————— boolean 1 byte 1 short...

中成才
2015/12/19
56
0

没有更多内容

加载失败,请刷新页面

加载更多

正则表达式匹配

请实现一个函数用来匹配包括 '.' 和 '*' 的正则表达式。模式中的字符 '.' 表示任意一个字符,而 '*' 表示它前面的字符可以出现任意次(包含 0 次)。 在本题中,匹配是指字符串的所有字符匹配...

Garphy
53分钟前
6
0
Laravel 5.1的多路由文件的配置

默认的路由配置文件只有一个, \app\Http\routes.php。 在同一个文件中写路由容易起冲突,文件会越来越大,就需要定义多个路由文件。 找到加载\app\Http\routes.php的文件, 打开\app\Provid...

mdoo
今天
5
0
Hibernate 5 开始使用指南前言

同时在面向对象软件和关系型数据库进行工作,可能会非常复杂和费时。数据在对象和数据库之间可能会不一致,然后导致开发成本会非常高。 Hibernate 是一个针对 Java 环境的对象关系映射(Obj...

honeymoose
今天
6
0
聊聊nacos ServiceManager的UpdatedServiceProcessor

序 本文主要研究一下nacos ServiceManager的UpdatedServiceProcessor ServiceManager.init nacos-1.1.3/naming/src/main/java/com/alibaba/nacos/naming/core/ServiceManager.java @Compone......

go4it
今天
7
0
正则表达式的使用(QQ格式的判断与空格的切割)

//正则表达式的使用 public static void main(String[] args) throws IOException, ClassNotFoundException { //test1("123456"); test2("-1 99 kk"); } /** * ......

zhengzhixiang
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部