一、对象的内存布局

        以Hotspot虚拟机为例,对象在内存中的结构可以分为三部分:对象头(header)、实例数据(instance data)、对齐填充(padding)。

1.1.对象头

        对象头的结构大体相似,但不同JVM的具体实现使得它们略有差别。一般来说,对象头都包含了标记字、类型指针两部分信息,如果对象是数组,还会额外包含数组长度信息。

1.1.1.标记字

        存储对象自身的运行时数据(即状态),包括哈希码、GC分代年龄、锁状态标志、线程持有锁、偏向线程id、偏向时间戳等。它们的存储结构类似于C语言中的“位字段”,官方称之为“Mark Word(标记字)”。“标记字”以“字”作为基本的存储单元,即在32位虚拟机中,数据长度为32bit;而在64位虚拟机中,数据长度为64bit。
        以32bit虚拟机为例,有固定的2bit用于存储锁标志位,随着锁标志位值不同,其他位存储的内容与位长度也不同。这一点类似于C语言中的联合结构(union),且联合的每一个成员都是位字段结构。

1.1.2.类型指针

        类型指针即对象指向它的类元数据(class metadata)的指针,虚拟机通过该指针确定这个对象是哪个类的实例。但需要注意的是,并非所有虚拟机实现中都会在对象头包含类型指针,也可以采用其他方式保留对象的类型信息。

1.1.3.数组长度

        在java中,数组也属于对象,那么理所当然的需要维护数组长度,该信息存放在对象头中。
 

1.2.实例数据

        实例数据即对象的字段(或称为成员变量)存储的数据信息,包含了从父类继承及自己定义的所有字段。且字段在内存中存储的顺序并不等于类中的定义顺序,它受到虚拟机策略的影响(主要考虑到内存对齐以及使用率的问题)。
 

1.3.对齐填充

        类似于C中结构体struct的内存对齐,java对象的内存位置也需要对齐。
        我们常用的Hotspot虚拟机要求每个对象的起始地址为8字节的整数倍,也就是说,若一个对象结束地址非8字节整数倍,则需要占位符进行填充以保证对齐。
 

二、对象的访问定位

        虚拟机规定,需要通过栈上的“reference(引用)”来操作具体对象。对于该规定,目前有两种主流的实现方式:
  • 通过句柄(handler)实现:该种方式会在堆中划出一块“句柄池”内存空间,每个栈上的引用直接指向句柄池中的句柄,而句柄中又会维护对象指针和类型指针。使用句柄带来的好处是,栈上的reference存储稳定的句柄地址,GC造成的对象移动只会导致句柄中相应的指向地址改变,而reference地址不改变。

  • 通过直接指针(direct point)实现:即在对象的对象头中维护类型指针。栈的reference指向对象,而对象头中的类型指针指向对象类型数据。使用直接指针的好处是,对象的访问速度快,节省了指针二次寻址的开销。
 
 

三、对象的创建过程

        对象的创建过程要经历以下几个阶段:

3.1.类加载

  • 检查到new指令;
  • 虚拟机检查在常量池中是否有该类的符号引用,包括该符号引用代表的类是否已被加载、解析、初始化;
  • 没有,则先加载类(加载过程后续章节会详细讲述);有的话,直接创建对象;

3.2.内存分配

(1)内存分配的方式
         一个对象所需内存在类加载时便可确定,内存分配方式有两种:
  • 指针碰撞法:若java堆中内存是绝对规整的,所有用过内存都放到一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那分配内存就仅仅是把指针向空闲空间那边挪动一段与对象大小相等的距离;
  • 空闲列表法:若堆内存不规整,就无法通过简单的移动指针分配内存。这种情况下虚拟机会维护一个列表记录哪些内存可用,分配时查找并更新列表;
        使用哪种方式取决于内存是否规整,而内存是否规整又取决于垃圾收集器的GC算法。典型如serial、parnew这两种垃圾收集器,它们在GC时带有压缩整理的功能,因此系统会采用“指针碰撞”的方式分配内存;而CMS这种基于Mark-Sweep(标记-清除)算法的垃圾收集器,则会采用“空闲列表法”分配内存。
(2)内存分配的安全
        需要注意的是,若多个线程同时申请分配内存,如果不加以同步控制,则会导致内存分配不对。不同的虚拟机会采用不同的机制避免线程安全问题:
  • 同步锁定:通过CAS配上失败重试的方式保证更新操作的原子性。注:CAS,即CPU硬件同步原语,全称为compare and swap(比较并交换),若比较不对则失败;
  • TLAB:即线程分配缓冲区。在堆中预先为每个线程分配一小块内存,线程在各自分配的内存上进行内存分配来保证安全。只有当TLAB用尽并申请新的TLAB时,才进行同步锁定。

3.3.内存初始化

        内存初始化指的是将对象分配到的内存所有位重置为0(不含对象头)。若对象通过TLAB分配的,该过程会提前至“内存分配”执行.

3.4.对象头初始化

        设置对象的对象头信息。

3.5.对象实例数据初始化

        设置对象的实例数据信息,即成员变量值。只有这步完成了,一个真正的对象才产生并能提供给我们使用。

05-JVM对象探秘的更多相关文章

  1. JVM总结(一):概述--JVM对象探秘

    这一节我们来讨论一下JVM对象建立过程. JVM对象探秘 对象的建立 对象的内存布局 对象的访问定位 JVM对象探秘 对象的建立 对象的建立过程   图一:对象建立过程 1.类加载检查. 当JVM检测 ...

  2. JVM对象创建

    1.JVM对象创建:java程序运行过程中,无时无刻都有对象被创建出来.在语言层面上就是new关键字. 2.JVM对象创建过程: (1)JVM遇到一条new指令后,首先会去常量池中,检查这个指令的参数 ...

  3. JVM探究之 —— HotSpot虚拟机对象探秘

    本节以常用的虚拟机HotSpot和常用的内存区域Java堆为例,深入探讨HotSpot虚拟机在Java堆中对象分配.布局和访问的全过程. 1. 对象的创建 Java是一门面向对象的编程语言.在语言层面 ...

  4. 深入理解JVM:HotSpot虚拟机对象探秘

    对象的创建 java是一门面向对象的语言.在Java程序执行过程中无时无刻有Java对象被创建出来.在语言层面上,创建对象(克隆.反序列化)一般是一个newkeyword而已,而在虚拟机中,对象的创建 ...

  5. 深入理解JVM(③)——之HotSpot虚拟机对象探秘

    前言 上篇文章介绍了Java虚拟机的运行时数据区域,大致明白了Java虚拟机内存模型的概况,下面就基于实用优先的原则,以最常用的虚拟机HotSpot和最常用的内存区域Java堆为例,升入探讨一下Hot ...

  6. JVM学习十三 - (复习)HotSpot 虚拟机对象探秘

    对象的内存布局 在 HotSpot 虚拟机中,对象的内存布局分为以下 3 块区域: 对象头(Header) 实例数据(Instance Data) 对齐填充(Padding) 对象头 对象头记录了对象 ...

  7. 阿里面试官:小伙子,你给我说一下JVM对象创建与内存分配机制吧

    内存分配机制   逐步分析 类加载检查: 虚拟机遇到一条new指令(new关键字.对象的克隆.对象的序列化等)时,会先去检查这个指令的参数在常量池中定位到一个类的符号引用,并且这个符号引用代表的类是否 ...

  8. JAVA虚拟机之对象探秘

    上一章主要写到了JVM中运行时数据区域各个部分的功能及其作用.上一章说到了对象是分配在堆上面的,所以接下来我们写到对象在堆内存中是如何创建.如何布局.如何访问.1. 对象的创建 在java程序中对象的 ...

  9. JVM对象分配和GC分布【JVM】

    最近在学习java基础结构,刚好学到了jvm,总结了以下并可以结合思维导图认识以下Jvm的对象: 栈:什么是栈? 先说一下栈的数据结构吧,栈它是一种先进后出的数据结构(FILO),跟队列刚好相反(先进 ...

随机推荐

  1. php 处理微信账单

    最近要做支付对账,即检查第三方支付与数据库中账单是否一一对应,涉及到微信对账单的处理,微信账单接口返回为一个字符串类似如下结果: 交易时间,公众账号ID,商户号,子商户号,设备号,微信订单号,商户订单 ...

  2. 为什么机器能够学习——PAC Learnability

    机器学习中,我们根据训练集训练一个模型,来对测试数据进行预测.通常我们并不关心模型在训练集上的好坏(即训练误差,in sample error),举个简单例子,我们想要根据前六个月股市行情训练一个模型 ...

  3. Android 复制 粘贴 剪贴板的使用 ClipboardManager

    Copy and Paste 版本:Android 4.0 r1  快速查看 用于复制粘贴数据的基于剪贴板的框架. 同时支持简单和复杂的数据,包括文本串.复杂的数据结构.文本和二进制流数据.程序 as ...

  4. HDU 2102 A计划(两层地图加时间限制加传送门的bfs)

    传送门: http://acm.hdu.edu.cn/showproblem.php?pid=2102 A计划 Time Limit: 3000/1000 MS (Java/Others)    Me ...

  5. Java添加事件的几种方式(转载了codebrother的文章)

    /** * Java事件监听处理——自身类实现ActionListener接口,作为事件监听器 * * @author codebrother */ class EventListener1 exte ...

  6. PL/SQL添加Oracle对象

    1.首先用system的身份进入数据库 2.找到user文件夹 3.右击新建用户 在“创建用户”窗口中,输入新用户的名称.口令,默认表空间.临时表空间等 4.赋予新用户权限,赋予其角色权限:conne ...

  7. 一点一点看JDK源码(一)Collection体系概览

    一点一点看JDK源码(一)Collection体系概览 liuyuhang原创,未经允许进制转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 Collection为集 ...

  8. 基于Bootstrap Ace模板+bootstrap.addtabs.js的菜单

    这几天研究了基于bootstrap Ace模板+bootstra.addtabs.js实现菜单的效果 参考了这个人的博客 https://www.cnblogs.com/landeanfen/p/76 ...

  9. 解决 ajax 跨域

    用两个服务器处理一个项目的代码,其中一台服务器只处理接口请求. 本来PHP可以使用CURL来处理,但是领导不允许使用PHP来处理数据.会影响网站的功能.如果接口端出现问题会导致整个网站或其页面的崩溃, ...

  10. day 15 装饰器

    装饰器(重点,难点) 开闭原则:             对功能的扩展开放            对代码的修改是封闭的 在目标函数前和后插入一段新的代码.不改变原来的代码 通用装饰器写法: # 存在的 ...