JVM虚拟机-了解Java堆中对象分配、布局和访问的全过程
前言
对象是Java中最常被提到的概念,也是Java的灵魂,Java中一切皆是对象。
JVM是Java的根基,理解灵魂与根基是如何融合的,对于理解Java本身至关重要。
对象的创建
对象的创建从Java语言层面上,往往就是一个简单的new即可搞定,而深入JVM的底层,可就复杂的多了。
JVM中对象的创建过程,分为如下5步,如图:

类加载检查
虚拟机遇到一条 new 指令后,会执行如下几个步骤:
- 首先将去检查这个指令的参数是否能在常量池中定位到这个类的符号引用。
- 检查这个符号引用代表的类是否已被加载过、解析过、初始化过。
- 如果没有,那必须先执行相应的类加载过程。
- 确定对象所需的内存大小。
- 类加载检查通过后,进入下一步骤,为新生对象分配内存。
分配内存
为对象分配空间相当于分田地,就是将空余的内存划分一块给对象。
由于不同的JVM虚拟机,Java堆的内存规整情况是不同的,所以分配方式上也会有一定区别。
Java 堆是否规整,由JVM所采用的垃圾收集器是否带有压缩整理功能决定的
内存空间分配方式
指针碰撞
Java堆中内存是规整(无内存碎片)时使用,即垃圾收集器有压缩整理功能时。
原理就是将用过的内存放在一块,没用的放一块,中间用指针做标识,分配的时候将指针向空闲内存区域移动。
空闲列表
Java堆中内存不规整,即垃圾收集器无压缩整理功能时。
使用内存和空闲内存相互交错,JVM需维护一个列表,记录上哪些内存是可用的,在分配时从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录。
两者具像化呈现如图:

并发时的内存分配
创建对象是很频繁的事,并发创建对象的时候有一个很重要的问题,就是线程安全。
如:程序中创建对象A和对象B,底层VM给A对象分配内存,指针没来及修改,对象B同时使用原来的指针分配内存。
JVM一般采用一下两种方式来保障线程安全。
同步处理:CAS
CAS 是乐观锁的一种实现方式。
所谓乐观锁就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。
虚拟机采用 CAS 配上失败重试的方式保证更新操作的原子性。
本地线程分配缓冲:TLAB
为每一个线程预先在 Eden 区分配一块儿内存,即为TLAB
JVM 在给线程中的对象分配内存时,首先在 TLAB 分配
当对象大于 TLAB 中的剩余内存不足或已用尽时,再采用上述的 CAS 进行内存分配
更多关于CAS的内容可以参考博主的另一篇文章Java并发/多线程-CAS原理分析
初始化零值
内存分配完成后,虚拟机需要将分配到的内存空间都初始化为零值(不包括对象头)
这一步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就直接使用,程序能访问到这些字段的数据类型所对应的零值。
设置对象头
对象头是虚拟机对对象进行必要的设置
对象头存放了关于对象的各种信息如:
- 对象是哪个类的实例
- 如何才能找到类的元数据信息
- 对象的哈希码
- 对象的 GC 分代年龄
- ……
根据虚拟机当前运行状态的不同,如是否启用偏向锁等,对象头会有不同的设置方式。
执行 init 方法
在上面工作都完成之后,从虚拟机的视角来看,一个新的对象已经产生
从 Java 程序的视角来看,对象创建才刚开始,<init> 方法还没有执行,所有的字段都还为零。
所以一般来说,执行 new 指令之后会接着执行 <init> 方法,把对象按照程序员的意愿进行初始化,这样一个真正可用的对象才算完全产生出来。
对象的内存布局
在 Hotspot 虚拟机中,对象在内存中的布局可以分为 3 块区域。
对象头
对象头上面已经介绍过了,其实主要就是分为两类:
- 用于存储对象自身的运行时数据
- 类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是那个类的实例
实例数据
这部分是对象真正存储的有效信息,也是在程序中所定义的各种类型的字段内容。
对齐填充
对齐填充不是必然存在的,没有实际意义,就是用来补位的。
因为 Hotspot 虚拟机的自动内存管理系统要求对象起始地址必须是 8 字节的整数倍,对象的大小必须是 8 字节的整数倍。
所以,当对象实例数据部分没有对齐时,就需要通过对齐填充来补全。
对象的访问定位
建立对象就是为了使用对象,我们的 Java 程序通过栈上的 reference 数据来操作堆上的具体对象。
对象的访问方式由虚拟机实现而定,目前主流的访问方式有如下两种方式
使用句柄
Java 堆中将会划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自的具体地址信息,如图:

直接指针
如果使用直接指针访问,那么 Java 堆对象的布局中就必须考虑如何放置访问类型数据的相关信息,而 reference 中存储的直接就是对象的地址。

二者对比
使用句柄来访问的最大好处是 reference 中存储的是稳定的句柄地址,在对象被移动时只会改变句柄中的实例数据指针,而 reference 本身不需要修改。
使用直接指针访问方式最大的好处就是速度快,它节省了一次指针定位的时间开销。
参考:《深入理解JVM虚拟机》第三版(吹爆)
JVM虚拟机-了解Java堆中对象分配、布局和访问的全过程的更多相关文章
- JVM 堆中对象分配、布局和访问
本文摘自深入理解 Java 虚拟机第三版 对象的创建 Java 是一门面向对象的语言,Java 程序运行过程中无时无刻都有对象被创建出来.从语言层面看,创建对象只是一个 new 关键字而已,而在虚拟机 ...
- JVM高级特性-二、JVM在堆中对象的分配、布局、访问过程
前面介绍了jvm运行时数据区域后,下面讲解下对内存中数据的其他细节,看他们是如何创建.布局及访问的 一.对象的创建 1.对象的分配 对象的创建分配方式主要有两种:指针碰撞和空闲列表 指针碰撞: 假设堆 ...
- 面试题:JVM在Java堆中对对象的创建、内存结构、访问方式
一.对象创建过程 1.检查类是否已被加载 JVM遇到new指令时,首先会去检查这个指令参数能否在常量池中定位到这个类的符号引用,检查这个符号引用代表的类是否已被加载.解析.初始化,若没有,则进行类加载 ...
- jvm详情——1、堆中存什么?栈中存什么?
数据类型 Java虚拟机中,数据类型可以分为两类:基本类型和引用类型.基本类型的变量保存原始值,即:他代表的值就是数值本身:而引用类型的变量保存引用值.“引用值”代表了某个对象的引用,而不是对象本身, ...
- 翻译:JVM虚拟机规范1.7中的运行时常量池部分(二)
本篇为JVM虚拟机规范1.7中的运行时常量池部分系列的第二篇. 4.4.4. The CONSTANT_Integer_info and CONSTANT_Float_info Structures ...
- JVM学习--(八)java堆分析
上一节介绍了针对JVM的监控工具,包括JPS可以查看当前所有的java进程,jstack查看线程栈可以帮助你分析是否有死锁等情况,jmap可以导出java堆文件在MAT工具上进行分析等等.这些工具都非 ...
- Java虚拟机三 Java堆和栈
Java堆是和Java应用程序关系最为紧密的内存空间,几乎所有的对象都存放在堆中.并且堆是完全自动化管理的. 根据垃圾回收机制的不同,Java堆有可能有不同的结构.最为常见的一种就是将整个Java堆分 ...
- 翻译:JVM虚拟机规范1.7中的运行时常量池部分(一)
原文链接: https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.4 Java Virtual Machine i ...
- JVM虚拟机21: 1.8中废弃永久代(PermGen)迎来元空间(Metaspace)
1.JDK8永久代的废弃 JDK8 永久代变化如下图: 1.新生代:Eden+From Survivor+To Survivor 2.老年代:OldGen 3.永久代(方法区的实现) : PermGe ...
随机推荐
- RabbitMQ简介、安装、基本特性API--Java测试
新的阅读体验地址:http://www.zhouhong.icu/post/141 本篇文章所有的代码:https://github.com/Tom-shushu/Distributed-system ...
- ProBuilder快速原型开发技术 ---模型精细化操作
前面我们讲解了很多ProBuilder功能,但是对于制作一款复杂的模型来说,还远远不够.需要更多的对于模型细节的处理,这就是本篇文章要讲解的"模型精细化操作"技术. 关于PB对于模 ...
- java二叉树遍历——深度优先(DFS)与广度优先(BFS) 递归版与非递归版
介绍 深度优先遍历:从根节点出发,沿着左子树方向进行纵向遍历,直到找到叶子节点为止.然后回溯到前一个节点,进行右子树节点的遍历,直到遍历完所有可达节点为止. 广度优先遍历:从根节点出发,在横向遍历二叉 ...
- springboot 配置文件application
application.properties # ----------------------------------------# 核心属性# --------------------------- ...
- IntelliJ IDEA热部署配置总结
Intellij IDEA 4种配置热部署的方法: 热部署可以使的修改代码后,无须重启服务器,就可以加载更改的代码. 第1种:修改服务器配置,使得IDEA窗口失去焦点时,更新类和资源 菜单Run -& ...
- Announcing cnblogs-hardening 1.0 Preview 1
Release Notes Write about coding Note About coding Share about coding Talk about coding Comment abou ...
- [系统重装日志2]win10系统安装pytorch0.4.1(gpu版本)
目录 0,资源整理 1,安装最新版的显卡驱动 2,安装visual studio 3,安装cuda 4,安装cudnn,配置环境变量 5,安装pytorch 6,安装torchvision 7,验证 ...
- 【linux】系统编程-4-共享内存
目录 前言 6. 共享内存 6.1 概念 6.2 操作函数 6.2.1 shmget() 6.2.2 shmat() 6.2.3 shmdt() 6.2.4 shmctl() 6.3 例子 参考: 前 ...
- PAT (Advanced Level) Practice 1041 Be Unique (20 分) 凌宸1642
PAT (Advanced Level) Practice 1041 Be Unique (20 分) 凌宸1642 题目描述: Being unique is so important to peo ...
- Linux 以非root用户安装zsh&配置on my zsh
此文章参考以下三篇文章,如有侵权请联系 Linux非root用户安装zsh, 并用oh-my-zsh进行配置 在没有sudo权限的Linux服务器下安装oh-my-zsh 不使用root权限安装zsh ...