一、对象的内存布局

        以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. _bstr_t可接受多字节、UNICODE字符串,方便用以字符集转换

    使用_bstr_t需要包含的头文件: #include <comutil.h> #include <comdef.h> // test.cpp : 定义控制台应用程序的入口点. ...

  2. Windows 10推送的锁屏壁纸保存方法

    Windows 10推送的锁屏壁纸保存方法 工作中使用的系统为Windows 10,锁屏时显示的壁纸很漂亮,并且每天都会更新,有几张特别喜欢,于是就想这些壁纸到底保存在哪里呢?经过一番摸索,终于搞明白 ...

  3. Office365学习笔记—获取当前用户

    1,页面上有个_spPageContextInfo对象,可以获取一些我们需要的东西. (1)获取当前用户Id var userId=_spPageContextInfo.userId; (2)获取当前 ...

  4. 一点一点看JDK源码(四)java.util.ArrayList 中篇

    一点一点看JDK源码(四)java.util.ArrayList 中篇 liuyuhang原创,未经允许禁止转载 本文举例使用的是JDK8的API 目录:一点一点看JDK源码(〇) 1.综述 在前篇中 ...

  5. NDK-C++ support

    1.NDK相关各种可用的C++运行库Android平台自带微型C++运行库(system),NDK提供补充功能的C++运行库(gabi++, stlport, gnustl)运行库 异常支持 RTTI ...

  6. Struts2-01

    一.Struts2的介绍 讲Struts2框架之前,我们需要知道框架是什么呢?估计大多数初学者都只知道其名却不知其意,框架就是一个半成品,别人将一些功能已经写好了,我们只需要拿来用即可,像我们之前使用 ...

  7. JS中some(),every(),fiflter(),map()各种循环的区别理解

    1.some():返回一个Boolean,判断是否有元素符合func条件const arr = [1,2,3,4]; arr.some((item)=>{return item>1}) 打 ...

  8. 【POJ 3368】Frequent values(RMQ)

    Description You are given a sequence of n integers a1 , a2 , ... , an in non-decreasing order. In ad ...

  9. Jqgrid利用正则匹配表达式正确移除html标签

    在使用JqGrid表格插件过程中,遇到一个问题:后台取出来的字段是带有Html标签的,于是将内容填充到表格之后,带有的html标签会把表格撑开或者每一行的内容显示不统一,导致非常难看,就像下图所示: ...

  10. 原型 - 实现自己的jQuery

    每个第一次使用jq的开发者都感到惊叹,jq的$太神奇了,究竟是怎么做到的使用$控制dom 赞叹前人之余,探究其本源才是前端开发者应该做的事,社区常常说,不要重复造轮子, 可是啊,连轮子都造不出来,又怎 ...