Loading [MathJax]/jax/output/CommonHTML/config.js
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >(3)JVM——对象的创建和内存布局

(3)JVM——对象的创建和内存布局

作者头像
凡人飞
发布于 2020-09-20 12:16:56
发布于 2020-09-20 12:16:56
7040
举报
文章被收录于专栏:指缝阳光指缝阳光

一、简介

介绍:在开发中,我们大多是使用 new 关键字来创建对象。但是对于对象的创建具体细节和对象在堆内存中的存储布局不怎么了解,此处主要简单介绍一下。

二、对象的创建

概括:对象的创建过程可以简单描述为如图所示。下面进行具体讲解

  1. 类加载检查:当 Java 虚拟机遇到 new 执行时,首先检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,则进行相应的类的加载过程。
  2. 分配内存:在类加载检查通过后,就需要为新对象分配内存。对象所需内大小在类加载完成后便可完全确定,为对象分配空间的任务实际上等同于把一块确定大小的内存块从 Java 堆中划分出来。分配方法主要是:指针碰撞空闲列表
    • 指针碰撞:前提是堆内存是绝对规整的。所有被使用过的内存都放在一边,空闲的内存被放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针指向空闲空间方向挪动一段与对象大小相等的距离。比如:Serial、ParNew
    • 空闲列表:虚拟机维护一个列表,记录了哪些内存块是可用的,在分配的时候从列表中找到一块足够大大的空间划分给对象实例,并更新列表上的记录。比如:CMS
    • 分配空间有线程安全问题,解决方法两种:① CAS 配上失败重试保证原子性。 ② 线程预分配一小块内存(本地线程分配缓冲TLAB),在进行分配内存时,优先使用本地缓冲区,不够时再进行同步锁定。
  3. 初始化零值:内存分配完成之后,虚拟机需要将分配到的内存空间(不包括对象头)都初始化为零值。这步操作保证了对象的实例字段在 Java 代码中可以不赋初始值就可以直接使用,使程序能访问到这些字段的数据类型所对应的零值。
  4. 设置对象头:对对象进行必要的设置,例如这个对象是哪个类的实例、如何才能找到类的元数据信息、对象的哈希吗、对象的 GC 分代年龄等信息,以及根据虚拟机的运行状态来设置是否启用偏向锁等。这些设置都在对象头中。
  5. 执行 init 方法:当执行前四步后,从虚拟机来看对象创建已经完成了。但是对于我们开发来说,是还没有完成的,因为我们创建对象一般会有构造方法等来初始化数据(在编译生成的字节码中,构造函数会被命名成 < init>() 方法,参数列表与Java语言书写的构造函数的参数列表相同),执行完 init 方法后才算构造完成。

三、对象的内存布局

简介:在 HotSpot 虚拟机中,对象在堆内存中的存储布局可以划分为三个部分:对象头实例数据对齐填充

  1. 对象头:对象头主要包括两类信息
    • 对象自身的运行数据:如哈希吗、GC 分代年龄、锁状态标志等,这部分数据被称为 “Mark Word”
    • 类型指针:该指针为对象指向它的类型元数据的指针,Java 虚拟机通过这个指针来确定该对象是哪个类的实例。(如果对象是数组,对象头还需记录数组的长度)
  2. 实例数据:这部分数据就是我们程序中定义的各种类型的字段内容,父类变量在子类变量之前。
  3. 对齐填充:这部分不是必然存在,也没有含义,是占位符作用。因为要求对象的大小必须是 8 字节的整数倍,如果对象头(已设计好为 8 字节的倍数)和实例数据部分加起来不满足整数倍,则通过对齐填充来补全。

四、对象的访问

简介:Java 程序会通过栈上的 reference 数据来操作堆上的具体对象。具体的实现方式主要是两种:使用句柄直接指针

  1. 句柄访问:Java 堆中将可能划分出一块内存来作为句柄池,reference 中存储的就是对象的句柄地址,而句柄中包含了对象实例数据与类型数据各自具体的地址信息,结构如下图。
  1. 直接指针:堆中的对象需要放置访问类型数据的相关信息,reference 中存储的直接就是对象地址。如下图

优缺点对比

  • 句柄的优点就是 reference 中存储的是稳定句柄地址,在对象移动时只会改变句柄中的实例数据指针,而 reference 本身不需要被修改
  • 直接指针优点就是速度更快,节省了一次指针定位的时间开销。HotSpot 主要使用直接指针,句柄方式在其他语言和框架中使用也多。
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/08/10 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
JVM:全面解析Java对象的创建、内存布局 & 访问定位流程
由于引用类型数据(reference)在 Java虚拟机中只规定了一个指向对象的引用,但没定义该引用应该通过何种方式去定位、访问堆中的对象的具体位置
Carson.Ho
2019/10/25
1.9K0
jvm学习记录-对象的创建、对象的内存布局、对象的访问定位
简述 今天继续写《深入理解java虚拟机》的对象创建的理解。这次和上次隔的时间有些长,是因为有些东西确实不好理解,就查阅各种资料,然后弄明白了才来做记录。 (此文中所阐述的内容都是以HotSpot虚拟机为例的。) 对象的创建 java程序在运行过程中无时无刻都有对象被创建出来,那么创建对象是个怎么样的过程呢?还是看看我自己的理解吧。 判断是否已经执行类加载 当虚拟机遇到一条new指令时 ,首先去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化
纪莫
2018/04/19
1.1K0
jvm学习记录-对象的创建、对象的内存布局、对象的访问定位
浅谈对象的创建、内存布局和访问定位
  这里的对象的创建是指普通的对象(不包括数组和Class对象)。对象的创建简单来说就是执行new的时候,虚拟机做出对应的响应。让我们看看一下虚拟机创建对象的过程: 1.虚拟机遇到new指令时,首先尝试在常量池中定位到对应类的符号引用,并检查这个符号引用代表类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程(后续会写一下关于类加载的问题)。 2.类加载检查通过后,为新生对象分配内存。对象内存的大小在类加载完成后便可完全确定。对象内存分配有“指针碰撞”和“空闲列表”两种方法,“指针碰撞”是把已用内存放到指针的一边,未用的放到另一边,以指针分隔,当需要分配一个新对象内存时把指针往未分配内存那边移动相对应的空间即可;“空闲列表”是因为内存已用的和未用的并不是规整的,它们是交错的,所以需要一个列表记录内存块的情况。Java堆是线程之间共享的内存,虚拟机采用CAS配上失败重试的方式保证更新操作的原子性保证内存指针修改并发安全性;另一种方法是“本地线程分配缓冲(Thread Local Allocation Buffer TLAB)”。 3.将虚拟机分配到的内存空间初始化为零值。 4.对对象进行必要的设置。其实是对对象头编写。 5.完成上面4个步骤执行new指令后会接着执行方法 到此对象才算完成生产出来。
GreizLiao
2019/09/24
8050
JVM之对象的实例化内存布局与访问定位
将对象的所属类(即类的元数据信息)、对象的HashCode和对象的GC信息、锁信息等数据存储在对象的对象头中。这个过程的具体设置方式取决于JVM实现
Java微观世界
2025/01/20
1340
JVM之对象的实例化内存布局与访问定位
JVM之对象创建流程及对象内存布局
当JAVA虚拟机碰到new字节码指令时,首先会去常量池中查找是否有对应的类名(也就是去查找是否有对应的符号引用),然后去检查这个符号引用代表的类是否已经被加载,解析和初始化过。如果没有会先进行类加载过程。
北洋
2021/12/20
5180
一文详解JVM对象内存布局以及内存分配规则
对象头可能包含类型指针,通过该指针能确定对象属于哪个类。如果对象是一个数组,那么对象头还会包括数组长度。
架构狂人
2023/08/16
5020
一文详解JVM对象内存布局以及内存分配规则
JVM02-JVM的对象创建以及访问方式
前言对象创建1.类加载检查2.分配内存分配内存的方式内存分配的并发问题3.初始化零值4.设置对象头:5. 执行init方法;对象内存布局对象头实例数据对齐填充对象访问方式使用句柄访问使用直接指针访问总结
码农飞哥
2021/08/18
5270
JVM-解密Java对象
在Java程序运行过程中时时刻刻都有对象被创建出来,对象的创建方式有很多种,最常见的就是new,其次还有clone和反序列化。下面我们一起来解密对象的创建、内存布局以及如何定位一个对象。
shysh95
2020/08/11
4080
深入理解JVM - 对象分配内存
这一节我们来讨论对象分配内存的细节,这一块的内容相对比较简单,但是也是比较重要的内容,最后会总结书里面的OOM的溢出案例,在过去的文章已经讲到过不少类似的情况。
阿东
2021/08/16
4490
JVM-02内存区域与内存溢出异常(中)【hotspot虚拟机对象】
在 JVM-01自动内存管理机制之Java内存区域与内存溢出异常(上)中我们介绍了 运行时数据区域,这里我们来继续探讨下hotspot虚拟机对象
小小工匠
2021/08/17
3780
最通俗易懂的JVM内存管理与对象创建原理
对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要像 C/C++程序为每一个new操作去写配对 的delete/free代码,不容易出现内存泄漏和内存溢出问题。也正是因为Java程序员把控制内存的权力交给了Java虚拟机,一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那排查错误、修正问题将会成为一项异常艰难的工作。
仲君Johnny
2024/01/24
5150
最通俗易懂的JVM内存管理与对象创建原理
JVM内存与垃圾回收篇第10章对象的实例化内存布局与访问定位
将对象的所属类(即类的元数据信息)、对象的HashCode和对象的GC信息、锁信息等数据存储在对象的对象头中。这个过程的具体设置方式取决于JVM实现。
yuanshuai
2022/08/17
2760
JVM内存与垃圾回收篇第10章对象的实例化内存布局与访问定位
JVM 中对象咋创建啊,又怎么访问啊
多学一点,这里的几个步骤涉及多个指令操作,所以就有了 DCL 单例使用 volatile 来禁止指令重排来保证单例模式的实例同步
星尘的一个朋友
2020/12/30
6660
JVM 中对象咋创建啊,又怎么访问啊
《深入理解Java虚拟机》读书笔记(二)
当Java虚拟机遇到字节码new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过;如果没有,那么必须先执行相应的类加载过程
DestinySkywalker
2023/01/01
3760
《深入理解Java虚拟机》读书笔记(二)
深入理解JVM(③)——之HotSpot虚拟机对象探秘
上篇文章介绍了Java虚拟机的运行时数据区域,大致明白了Java虚拟机内存模型的概况,下面就基于实用优先的原则,以最常用的虚拟机HotSpot和最常用的内存区域Java堆为例,升入探讨一下HotSpot虚拟机在Java堆中对象分配、布局和访问的全过程。
纪莫
2020/06/04
6520
JVM 系列(2) —— Java 的对象
当 Java 虚拟机遇到一条字节码指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号的引用,并检查这个符号引用代表的类是否被加载解析和初始化过。如果没有则先执行相应的类加载过程。
求和小熊猫
2020/12/18
4190
JVM 系列(2) —— Java 的对象
HotSpot 虚拟机对象
虚拟机遇到一条 new 指令时,首先将去检查这个指令的参数,是否能在常量池中定位到这个类的符号引用,并且检查这个符号引用代表的类是否已被加载过、解析和初始化。如果没有,那必须先执行相应的类加载过程。
happyJared
2019/07/27
5290
JVM系列一(Java内存区域和对象创建).
线程共享,JVM中最大的一块内存,此内存的唯一目的就是存放对象实例,Java 堆是垃圾收集器管理的主要区域,因此很多时候也被称为“GC堆”(Garbage Collected Heap),可以通过 -Xmx 和 -Xms 参数来控制该区域大小。
JMCui
2019/12/02
4870
JVM系列一(Java内存区域和对象创建).
深入浅出JVM(一)之Hotspot虚拟机中的对象
对象的创建可以分为五个步骤:检查类加载,分配内存,初始化零值,设置对象头,执行实例构造器<init>
菜菜的后端私房菜
2024/09/24
3160
JVM:HotSpot虚拟机----对象的创建简单介绍及对象内存布局详解
例如:在32位HotSpot虚拟机中,如果对象处于未被锁定的状态下,那么Mark Word的32bit空间中的25bit用于存储对象哈希码,4bit用于存储对象分代年龄,2bit用于存储锁标志位,1bit固定为0。下表是不同状态下对象存储内容的详细表:
鲲志说
2025/04/07
1560
JVM:HotSpot虚拟机----对象的创建简单介绍及对象内存布局详解
推荐阅读
相关推荐
JVM:全面解析Java对象的创建、内存布局 & 访问定位流程
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档