`

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

    博客分类:
  • java
阅读更多

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分享

 

  • 大小: 47 KB
  • 大小: 50.5 KB
  • 大小: 77.2 KB
  • 大小: 77 KB
  • 大小: 92.6 KB
  • 大小: 73.4 KB
  • 大小: 56.9 KB
  • 大小: 182.4 KB
16
8
分享到:
评论
6 楼 yangshangchuan 2014-02-26  
须等待 写道
想起了我自己之前研究的一个问题:一个1.2G的文件读到内存塞进容器里,结果内存爆了,也是因为数据在变成对象装入容器的过程中占用了比直接二进制编码入操作系统更大的内存


编写正确的程序往往比一般人想象的难度要大的多,很多时候,程序是碰巧能运行,尤其是在多线程的情况下。
5 楼 须等待 2014-02-26  
想起了我自己之前研究的一个问题:一个1.2G的文件读到内存塞进容器里,结果内存爆了,也是因为数据在变成对象装入容器的过程中占用了比直接二进制编码入操作系统更大的内存
4 楼 yangshangchuan 2014-02-25  
447214075 写道
你既然猜想堆中存了两个地址,并且一个是Object.class,那另一个应该就是EmptyObject.class吧。


很犀利的眼光,为了方便在jvisualvm中查看对象占多少内存,这里使用一个私有的静态内部类EmptyObject来替代Object,因为类定义为空,所以可以等同对待EmptyObject和Object。
3 楼 447214075 2014-02-25  
你既然猜想堆中存了两个地址,并且一个是Object.class,那另一个应该就是EmptyObject.class吧。
2 楼 yangshangchuan 2014-02-25  
永志_爱戴 写道
永志_爱戴 写道
[size=large]8字节的引用这个毋庸置疑,但是引用应该是分配在线程栈内存里的,不在堆内存里。jvisualvm显示的16字节应该是指堆内存里占用16个字节吧。[/size]



没错你分析的对,new 1个对象是16字节,new 2个对象是32字节,new 100个对象是1600字节,我们从jvisualvm里面可以看到对象数跟字节数的线性关系,从这里可以看出,jvisualvm显示的内存占用跟引用的4字节或8字节是没有关系的,也就是说,jvisualvm显示的是堆内存占用,这也很好理解,毕竟所有引用的字节占用是固定的。
1 楼 永志_爱戴 2014-02-25  
永志_爱戴 写道
[size=large]8字节的引用这个毋庸置疑,但是引用应该是分配在线程栈内存里的,不在堆内存里。jvisualvm显示的16字节应该是指堆内存里占用16个字节吧。[/size]

相关推荐

    new一个Object对象占用多少内存?-附件资源

    new一个Object对象占用多少内存?-附件资源

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

    废话不多说,一起开干 1 前置知识 本次实验基于jdk8 64位以及以上版本。本机环境为jdk11 先查看一下jvm启动的默认参数,里面有...UseCompressedOops:普通对象指针压缩(oop是ordinary object pointer的缩写), UseComp

    ==和equals方法究竟有什么区别

    变量obj是一个内存,new Object()是另一个内存,此时,变量obj所对应的内存中存储的数值就是对象占用的那块内存的首地址。对于指向对象类型的变量,如果要比较两个变量是否指向同一个对象,即要看这两个变量所对应的...

    内存管理内存管理内存管理

    不过,即使是在这样一个简单的计算机中,您也会有问题,尤其是当您不知道程序的每个部分将需要多少内存时。如果您的空间有限,而内存需求是变化的,那么您需要一些方法来满足这些需求: 确定您是否有足够的内存...

    操作系统(内存管理)

    不过,即使是在这样一个简单的计算机中,您也会有问题,尤其是当您不知道程序的每个部分将需要多少内存时。如果您的空间有限,而内存需求是变化的,那么您需要一些方法来满足这些需求: 确定您是否有足够的内存来...

    PHP 面向对象技术(全面讲解).txt

    一个项目要用到多少个类,用多少个对象,在那要定义类,定义 一个什么样的类,这个类实例化出多少个对象,类里面有多少个属性,有多少个方法等等,这就需 要读者通过在实际的开发中就实际问题分析设计和总结了。 类的...

    【05-面向对象(下)】

    •如果一个类始终只能创建一个对象,称为单例类。须符合以下几个条件:  –1.我们把该类的构造器使用Private修饰,从而把该 类的所有构造器隐藏起来。  –2.则需要提供一个public方法作为该类的访问点,用于创建...

    超级有影响力霸气的Java面试题大全文档

    finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。 16、sleep() 和 wait() 有什么区别? sleep是线程类(Thread)...

    java 面试题 总结

    finalize是Object类的一个方法,在垃圾收集器执行的时候会调用被回收对象的此方法,可以覆盖此方法提供垃圾收集时的其他资源回收,例如关闭文件等。 13、sleep() 和 wait() 有什么区别? sleep是线程类(Thread)的...

    net学习笔记及其他代码应用

    1. 简述 private、 protected、 public、 internal 修饰符的访问权限。 答 . private : 私有成员, 在类的内部才可以访问。...47.当一个线程进入一个对象的一个synchronized方法后,其它线程是否可...

    Java 语言基础 —— 非常符合中国人习惯的Java基础教程手册

    这些对象分别占用不同的内存空间,因此改变其中一个对象的状 态不会影响其它对象的状态 。 3.初始化对象 生成对象的最后一步是执行构造方法,进行初始化。由于对构造方法可以进行重写 ,所以通过给出不同个数或类型...

    java面试题

    答:JDO是java对象持久化的新的规范,为java data object的简称,也是一个用于存取某种数据仓库中的对象的标准化API。 CORBA? 答:CORBA标准是公共对象请求代理结构,用途为:用不同的程序设计语言书写,在不同的...

    java经典面试2010集锦100题(不看你后悔)

    C) 双精度类型double比单精度类型float具有更高的精度和更大的表示范围,但float类型具有速度快、占用内存小的优点。 D) 在Java中布尔值可以用true或false来表示,但是同时也可以用1或0来表示。 题目5:b 程序...

    json-store:轻量级的JSON文档存储,可以像NoSQL数据库一样查询

    您可以将所有内容放入一个商店,也可以将许多商店用于不同的JSON对象。 它对文档使用类似于NoSQL的查询系统,并且旨在使用非常低的内存占用空间(也就是不将所有文档都加载到内存中进行处理)。 注意:有一个...

    C++ MFC实现飞机大战游戏

     在程序中使用到的链表、刷子等占用内存资源的对象都要及时的删除。Delete Brush, List.removeall()等。 2.7 CImageList处理爆炸效果  爆炸效果是连续的显示一系列的图片。如果把每一张图片都显示出来的话,占用...

    sesvc.exe 阿萨德

    新增一个 Entry 对象写入当前位置。 void addEntry(int hash, K key, V value, int bucketIndex) { if ((size &gt;= threshold) && (null != table[bucketIndex])) { resize(2 * table.length); hash = (null != key...

    xml入门教程/xml入门教程

    2.xml文件是由元素和元素的内容以及属性组成的,一个xml文件中只能有一个根元素,标签和标签的内容加在一起叫作一个元素。xml是大小写 敏感的,只能以字母或下划线开头。 字符转义 &lt; 代表 ", &gt; 代表 "&gt;" , &...

    jboss as 7 support jsf2.2.1 modules

    #{bean.multipleAttributes}代表一个Map,Object&gt;对象。它的值可以是常量,也可以是表达式。 通过使用Express Language 3(Java EE 7的一部分),多个属性也可以通过EL表达式直接定义。 , "two":2, "three":3}" /...

    JSTL详细标签库介绍

    C++》关于异常处理论文,向对象中每增加一个类,都可能包含一些错误。&lt;BR&gt;Java使用和C++类似的异常处理&lt;BR&gt;1、处理错误的方法有很多流行方法&lt;BR&gt;2、一般方法是把程序处理代码段分散到系统代码中,在可能发生错误的...

Global site tag (gtag.js) - Google Analytics