本系列笔记主要基于《深入理解Java虚拟机:JVM高级特性与最佳实践 第2版》,是这本书的读书笔记。

对象的创建

  1. 虚拟机遇到一条 new 指令时,首先去检查这个指令的参数是否能在方法区常量池中定位到一个类的符号引用,并检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,就必须先执行相应的类加载过程。

  2. 类加载检查过后,虚拟机为新生对象分配内存。对象所需内存大小在类加载过后就完全确定,为对象分配空间就等同于,从 Java 堆中划分出一块确定大小的内存。

    此时有两种情况:

    • 如果 Java 堆中内存是规整的,那么虚拟机将会采用指针碰撞(Bump the Pointer)分配内存。也就是用过的内存放在一边,空闲的内存放另一边,中间有个指针作为分界点指示器,那么分配内存就是把指针向空闲那一边挪动了与对象相同大小的距离。

    • 如果 Java 堆中内存不规整,已使用和未使用的内存相互交错,那么虚拟机将会采用空闲列表(Free List)分配内存。也就是虚拟机会维护一个空闲列表,记录哪些内存块可用,分配内存时就从空闲列表中找到一块足够大的空间分给对象实例,并更新列表。

    选择哪种分配方式由 Java 堆是否规整决定,而 Java 堆是否规整由垃圾收集器是否带有压缩整理功能决定。比如Serial、ParNew这种基于复制算法和标记-整理算法的收集器,就是采用指针碰撞法分配内存。而CMS这种基于标记-清除算法的收集器就是采用空闲列表。

    关于垃圾收集算法和垃圾收集器,后面会单独有文章讲到。

    还有一个问题就是要保证分配内存时的线程安全,即使是简单的指针碰撞修改一下指针的位置,在并发情况下也不是线程安全的。解决这个问题有两种方案,一种是对分配内存的动作进行同步处理,保证操作的原子性;另一种就是前面提到的本地线程分配缓冲TLAB。

  3. 在 Java 堆中内存分配完成后,虚拟机会将已分配的内存空间初始化为零值(不包括对象头)。这保证了对象的实例字段在 Java 代码中可以不用赋初始值就可以直接使用,程序能访问到这些字段的数据类型所对应的零值。如果采用了 TLAB,这一步也可以提前至 TLAB 分配时进行。

  4. 接下来对对象进行必要的设置,例如这个对象是哪个类的实例、如何找到类的元数据信息、对象的哈希码、对象的GC分代年龄等信息,这些信息都存放在对象的对象头(Object Header)中。

  5. 这个时候,从虚拟机的角度看一个新的对象已经产生了,但从 Java 程序角度看对象的创建才刚开始,init 方法还没执行,所有的字段都还为零。所以这时会执行 init 的方法,把对象按照程序员的意愿初始化。

对象的内存布局

在 HotSpot 虚拟机中,对象在内存中的布局分为三块区域:对象头(Header)、实例数据(Instance Data)、对齐填充(Padding)。

对象头包括两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,官方称这部分数据为 Mark Word。另外一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针确定对象是哪个类的实例。但不是所有的虚拟机实现都会在对象数据上保留类型指针,也就是说查找对象的元数据信息不一定要经过对象本身,这点会在下面对象定位方式中讲到。另外如果对象是一个 Java 数组,对象头中还会记录数组长度,用来确定 Java 对象的大小。

HotSpot虚拟机对象头Mark Word:

存储内容 标志位 状态
对象哈希码、分代年龄 01 未锁定
指向锁记录的指针 00 轻量级锁定
指向重量级锁的指针 10 膨胀(重量级锁定)
空,不需要记录信息 11 GC标记
偏向线程ID、偏向时间戳、对象分代年龄 01 可偏向

实例数据是对象的真正有效数据,也就是代码中定义的字段内容。这部分存储数据受到虚拟机分配策略参数和字段在 Java 源码中定义顺序的影响。HotSpot 虚拟机默认分配策略为 longs/doubles、ints、shorts/chars、bytes/booleans、oops。相同宽度的字段总是被分配到一起。在这个前提下,父类中定义的变量会出现在子类之前。如果CompactFields参数为true,那么子类中较窄的变量也可能插到父类变量的空隙中。

对齐填充并不是必然存在的,也没有特殊含义,它仅仅是占位符。由于 HotSpot VM 的自动内存管理系统要求对象起始地址必须是8字节的整数倍,也就是对象大小必须是8字节的整数倍。因此当对象实例数据没有对齐时,就会通过对齐填充来补全。

对象的访问定位

建立对象是为了使用对象,Java 程序需要通过 Java 栈上的 reference 数据来操作 Java 堆上的对象。reference 类型就是一个指向对象的引用。对象的访问方式取决于虚拟机实现,目前的访问方式主要有使用句柄和直接指针两种。

  • 如果使用句柄访问,Java 堆中将会划分出一块内存作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象的实例数据与类型数据的地址,如下图:

  • 如果使用直接指针访问,reference 中存储的直接就是对象地址,但是对象中还会放置访问类型数据的指针,如下图:

这两种对象访问方式各有优劣,使用句柄访问的话,reference 中存储的是稳定的句柄地址,垃圾收集时移动对象的话,只会修改句柄中的实例数据指针,而 reference 本身不需要修改。

使用直接指针访问的最大好处就是速度更快,节省了一次指针定位的时间,对象访问非常频繁,这种节省极少成多的话也是非常可观。

HotSpot 虚拟机使用的是直接指针访问的方式。

JVM探秘:Java对象的更多相关文章

  1. JVM总结-java对象的内存布局

    在 Java 程序中,我们拥有多种新建对象的方式.除了最为常见的 new 语句之外,我们还可以通过反射机制.Object.clone 方法.反序列化以及 Unsafe.allocateInstance ...

  2. 【JVM】java对象

    一.对象内存布局 对象在内存中存储可分为3块区域:对象头,实例数据,对齐填充 1.对象头 对象头包含两部分内容. 第一部分:存储对象自身的运行时数据,哈希吗(hashCode),GC分代年龄,锁状态标 ...

  3. Java对象在JVM中的生命周期

          当你通过new语句创建一个java对象时,JVM就会为这个对象分配一块内存空间,只要这个对象被引用变量引用了,那么这个对象就会一直驻留在内存中,否则,它就会结束生命周期,JVM会在合适的时 ...

  4. Java对象的创建 —— new之后JVM都做了什么?

    Java对象创建过程 1. 类加载检查 虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载.解析和初始化过.如果没 ...

  5. 【深入理解JVM】:Java对象的创建、内存布局、访问定位

    对象的创建 一个简单的创建对象语句Clazz instance = new Clazz();包含的主要过程包括了类加载检查.对象分配内存.并发处理.内存空间初始化.对象设置.执行ini方法等. 主要流 ...

  6. JVM —— Java 对象占用空间大小计算

    零. 为什么要知道 Java 对象占用空间大小 缓存的实现: 在设计 JVM 内缓存时(不是借助 Memcached. Redis 等), 须要知道缓存的对象是否会超过 JVM 最大堆限制, 假设会超 ...

  7. JVM(2)--深入理解java对象创建始终

    java对象探秘 java是一门面向对象的语言,我们无时无刻不在创建对象和使用对象,那么java虚拟机是如何创建对象的?又是如何访问对象的?java对象中究竟存储了什么运行时所必需的数据?在学习了ja ...

  8. 99.9%的Java程序员都说不清的问题:JVM中的对象内存布局?

    本文转载自公众号:石彬的架构笔记,阅读大约需要8分钟. 作者:李瑞杰 目前就职于阿里巴巴,资深 JVM 研究人员 在 Java 程序中,我们拥有多种新建对象的方式.除了最为常见的 new 语句之外,我 ...

  9. JVM源码分析之Java对象头实现

    原创申明:本文由公众号[猿灯塔]原创,转载请说明出处标注 “365篇原创计划”第十一篇. 今天呢!灯塔君跟大家讲: JVM源码分析之Java对象头实现 HotSpot虚拟机中,对象在内存中的布局分为三 ...

随机推荐

  1. IP应用加速技术详解:如何提升动静混合站点的访问速率?

    全站加速(DCDN)-IPA是阿里云自主研发四层加速产品,它基于TCP/UDP的私有协议提供加速服务,包括解决跨运营商网络不稳定.单线源站.突发流量.网络拥塞等诸多因素导致的延迟高.服务不稳定的问题, ...

  2. 实时计算轻松上手,阿里云DataWorks Stream Studio正式发布

    Stream Studio是DataWorks旗下重磅推出的全新子产品.已于2019年4月18日正式对外开放使用.Stream Studi是一站式流计算开发平台,基于阿里巴巴实时计算引擎Flink构建 ...

  3. @loj - 2290@ 「THUWC 2017」随机二分图

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 一个左右各 n 个点的二分图,图中的边会按照一定的规律随机出现. ...

  4. 云数据库 MySQL 8.0 重磅发布,更适合企业使用场景的RDS数据库

    点击订阅新品发布会! 新产品.新版本.新技术.新功能.价格调整,评论在下方,下期更新!关注更多内容,了解更多 最新发布 云数据库MySQL 8.0 升级发布会 2019年5月29日15时,阿里云云数据 ...

  5. 【Pandas】Pandas求某列字符串的长度,总结经验教训

    测试集大小: test.shape(898, 11) 对某列的字符串做统计长度1.for遍历法:start = time.time()for i in test.index.values: test. ...

  6. LA 4973 Ardenia (3D Geometry + Simulation)

    ACM-ICPC Live Archive 三维几何,题意是要求求出两条空间线段的距离.题目难度在于要求用有理数的形式输出,这就要求写一个有理数类了. 开始的时候写出来的有理数类就各种疯狂乱套,TLE ...

  7. 冒泡排序&直接插入排序&快速排序

    一.冒泡排序 0       1      2      3      4      5 假设有一个6个数的数组,0,1,2,3,4,5是索引,冒泡排序就是相邻两个对比,比如5和4比,如果满足条件就互 ...

  8. supersockets接收过滤器(ReceiveFilter)

    接收过滤器(ReceiveFilter)用于将接收到的二进制数据转化成请求实例(RequestInfo). 实现一个接收过滤器(ReceiveFilter), 你需要实现接口 IReceiveFilt ...

  9. 浅谈Transformer 及Attention网络

    1 Transformer 模型结构处理自然语言序列的模型有 rnn, cnn(textcnn),但是现在介绍一种新的模型,transformer.与RNN不同的是,Transformer直接把一句话 ...

  10. AWS Credentials 使用

    AWS的文档系统真是烂到家了!!!!! To connect to any of the supported services with the AWS SDK for Java, you must ...