论坛
潜水/灌水快乐,沉淀知识,认识更多同行。
ToB圈子
加入IT圈,遇到更多同好之人。
朋友圈
看朋友圈动态,了解ToB世界。
ToB门户
了解全球最新的ToB事件
博客
Blog
排行榜
Ranklist
文库
业界最专业的IT文库,上传资料也可以赚钱
下载
分享
Share
导读
Guide
相册
Album
记录
Doing
应用中心
搜索
本版
文章
帖子
ToB圈子
用户
免费入驻
产品入驻
解决方案入驻
公司入驻
案例入驻
登录
·
注册
账号登录
立即注册
找回密码
用户名
Email
自动登录
找回密码
密码
登录
立即注册
首页
找靠谱产品
找解决方案
找靠谱公司
找案例
找对的人
专家智库
悬赏任务
圈子
SAAS
qidao123.com技术社区-IT企服评测·应用市场
»
论坛
›
数据库
›
Oracle
›
JVM学习(四)--对象内存布局
JVM学习(四)--对象内存布局
惊雷无声
论坛元老
|
5 天前
|
显示全部楼层
|
阅读模式
楼主
主题
1988
|
帖子
1988
|
积分
5964
目录
一、对象内存布局
1、对象的实例化
1.1、你有几种方式创建对象?
1.2、创建对象的步骤
1.2.1、从字节码角度看待对象创建过程
1.2.2、从实行步骤角度分析
2、对象的内存布局
2.1、对象头
2.2、实例数据
2.3、对齐填充
3、对象的访问定位
3.1、句柄访问
3.2、直接使用指针访问
HotSpot使用哪种方式呢?
一、对象内存布局
1、对象的实例化
1.1、你有几种方式创建对象?
1、new ①最常见的方式 ②变形1:Xxx的静态方法 ③变形2:XxxBuilder/XxxFactory的静态方法
2、Class的newInstance():反射的方式,可以调用空参、带参的构造器,权限必须是public
3、Constructor的newInstance(Xxx):反射的方式,可以调用空参、带参的构造器,权限没有要求,实用性更广
4、使用clone():不调用任何构造器,当前类必要实现Cloneable接口,实现clone(),默认浅拷贝
5、使用反序列化:从文件中,数据库中,网络中获取一个对象的二进制流,反序列化为内存中的对象
6、第三方库Objenesis,利用了asm字节码技能,动态生成Constructor对象
1.2、创建对象的步骤
1.2.1、从字节码角度看待对象创建过程
(1)、下面从最简单的0bject ref=new object(); 代码进行分析,利用javap-verbose -p 下令检察对象创建的字节码如下:
cmd下令实行class
在IDE编译器上面会被转换成如下
NEW :如果找不到class对象,则进行类加载。加载乐成后,则在堆中分配内存,从0bject开始到本类路径上的所有属性值都要分配内存。分配完毕之后,进行零值初始化。在分配过程中,注意引用是占据存储空间的,它是一个变量,占用4个字节。这个指令完毕后,将指向实例对象的引用变量压入虚拟机栈顶。
DUP :在栈顶复制该引用变量,这时的栈顶有两个指向堆内实例对象的引用变量。如果<init>方法有参数,还必要把参数压人利用栈中。两个引用变量的目的差异,其中压至底下的引用用于赋值,大概生存到局部变量表,另一个栈顶的引用变量作为句柄调用相关方法。
INVOKESPECIAL:调用对象实例方法,通过栈顶的dup引用变量调用<init>方法。
增补:
<clinit>是类初始化时实行的方法,而<init>是对象初始化时实行的方法。
面试题:
1、new对象流程?
2、对象创建方法,对象内存分配?
1.2.2、从实行步骤角度分析
1、判定对象对应的类是否加载、链接、初始化
虚拟机会到一条new指令,起首去检查这个指令的参数能否在Metaspace的常量池中定位到一个类的符号引用,而且检查这个符号引用代表的类是否已经被加载、解析和初始化。(即判定类元信息是否存在)。
如果没有,那么在双亲委派模式下,使用当前类加载器以classLoader+包名+类名为Key进行查找对应的.class 文件。
如果没有找到文件,则抛出ClassNotFoundException 异常。。
如果找到,则进行类加载,并生成对应的Class类对象。
2、为对象分配内存
起首,盘算对象占用空间巨细,接着在堆中划分一块内存给新对象。如果实例成员变量是引用变量,仅分配引用变量空间即可,即4个字节巨细。
说明:选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。
①指针碰撞
如果内存规整,使用指针碰撞
如果内存是规整的,那么虚拟机将采用的是指针碰撞法(BumpThe Pointer)来为对象分配内存。意思是所有用过的内存在一边,空闲的内存在别的一边,中间放着一个指针作为分界点的指示器,分配内存就仅仅是把指针向空闲那边挪动一段与对象巨细相等的间隔罢了。 如果垃圾收集器选择的是Serial、ParNew这种基于压缩算法的,虚拟机采用这种分配方式。 一般使用带有compact(整理)过程的收集器时,使用指针碰撞。
②空闲列表
如果内存不规整,虚拟机必要维护一个列表,使用空闲列表分配
如果内存不是规整的,已使用的内存和未使用的内存相互交错,那么虚拟机将采用的是空闲列表法来为对象分配内存。意思是虚拟机维护了一个列表,记录上哪些内存块是可用的再分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的内容。这种分配方式称为“空闲列表(Free List)”。
3、处理并发安全标题
在分配内存空间时,别的一个标题是实时保证new对象时候的线程安全性:创建对象黑白常频仍的利用,虚拟机必要办理并发标题。 虚拟机采用了两种方式办理并发标题:
CAS(Compare And Swap )失败重试、地域加锁:保证指针更新利用的原子性;
TLAB 把内存分配的动作按照线程划分在差异的空间之中进行,即每个线程在Java堆中预先分配一小块内存,称为本地线程分配缓冲区,(TLAB,Thread Local Allocation Buffer)虚拟机是否使用TLAB,可以通过-XX:+/-UseTLAB参数来设定
4、初始化分配到的空间
内存分配结束,虚拟机将分配到的内存空间都初始化为零值(不包括对象头)。这一步保证了对象的实例字段在Java代码中可以不消赋初始值就可以直接使用,步调能访问到这些字段的数据类型所对应的零值。
5、设置对象的对象头
将对象的所属类(即类的元数据信息)、对象的Hashcode和对象的GC信息、锁信息等数据存储在对象的对象头中。这个过程的具体设置方式取决于JVM实现。
6、实行init方法进行初始化
在Java步调的视角看来,初始化才正式开始。初始化成员变量,实行实例化代码块,调用类的构造方法,并把堆内对象的首地址赋值给引用变量。
因此一般来说(由字节码中是否跟随有invokespecial指令所决定),new指令之后会接着就是实行方法,把对象按照步调员的意愿进行初始化,如许一个真正可用的对象才算完全创建出来。
2、对象的内存布局
2.1、对象头
对象头:它重要包括两部门。
1、一个是对象自身的运行时元数据(mark word)。
哈希值(hashcode):对象在堆空间中都有一个首地址值,栈空间的引用根据这个地址指向堆中的对象,这就是哈希值起的作用。
GC分代年事:对象起首是在Eden中创建的,在颠末多次GC后,如果没有被进行回收,就会在survivor中来回移动,其对应的年事计数器会发生变化,达到值后会进入养老区
锁状态标志,在同步中判定该对象是否是锁
线程持有的锁
线程偏向ID
偏向时间戳
2、另一个是类型指针,指向元数据区的类元数据InstanceKlass,确定该对象所属的类型。
别的,如果对象是一个数组,对象头中还必须有一块用于记录数组的长度的数据。
由于正常对象元数据就知道对象的确切巨细。所以数组必须得知道长度。
2.2、实例数据
作用:它是对象真正存储的有用信息,包括步调代码中定义的各种类型的字段(包括从父类继承下来的和本身拥有的字段)。
这里必要遵照的一些规则:雷同宽度的字段总是被分配在一起父类中定义的变量会出如今子类之前(由于父类的加载是优先于子类加载的)
如果CompactFields参数为true(默认为true):子类的窄变量可能插入到父类变量的空隙
2.3、对齐填充
对齐填充:不是必须的,也没特别含义,仅仅起到占位符的作用
3、对象的访问定位
创建对象的目的是为了使用它。定位,通过栈上reference访问。
JVM是如何通过栈帧中的对象引用访问到其内部的对象实例的呢?
《java虚拟机规范》没有说明,所以对象访问方式由虚拟机实现而定。主流有两种方式:
1、使用句柄访 2、使用直接指针访问
3.1、句柄访问
实现:
堆必要划分出一块内存来做句柄池,reference中存储对象的句柄池地址,句中包含对象实例与类型数据各自具体的地址信息。
长处:
reference中存储稳固句柄地址,对象被移动(垃圾收集时移动对象很普遍)时只会改变句柄中实例数据指针,reference本身不必要被修改。
3.2、直接使用指针访问
实现:
reference中存储的就是对象的地址,如果只是访问对象本身的话,就不必要多
一次间接访问的开销。
HotSpot使用哪种方式呢?
Hotspot这里重要使用直接指针访问
JVM可以通过对象引用准确定位到]ava堆区中的instance0opDesc对象,如许既可乐成访问到对象的实例信息,当必要访问目的对象的具体类型时,JVM则会通过存储在instance0opDesc中的元数据指针定位到存储在方法区中的instanceKlass对象上。
JVM学习(一)
JVM学习(三)--运行时数据区
再小的努力,乘以365都很显着!
每天⽤⼼记录⼀点点。内容也许不告急,但风俗很告急!
一个步调员最告急的能力是:写出高质量的代码!!
有道无术,术尚可求也,有术无道,止于术。
无论你是年轻照旧年长,所有步调员都必要记着:时候努力学习新技能,否则就会被时代扬弃!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
本帖子中包含更多资源
您需要
登录
才可以下载或查看,没有账号?
立即注册
x
回复
使用道具
举报
0 个回复
倒序浏览
返回列表
快速回复
高级模式
B
Color
Image
Link
Quote
Code
Smilies
您需要登录后才可以回帖
登录
or
立即注册
本版积分规则
发表回复
回帖并转播
回帖后跳转到最后一页
发新帖
回复
惊雷无声
论坛元老
这个人很懒什么都没写!
楼主热帖
记一次实战 Shiro反序列化内网上线 ...
基于 Sealos 的镜像构建能力,快速部署 ...
蚂蚁金服杨军:蚂蚁数据分析平台的演进 ...
Linux之iptables(NAT表)——实验篇 ...
超融合走向红海,下一代超融合是新蓝海 ...
1亿条数据批量插入 MySQL,哪种方式最 ...
联合评测 DapuStor Roealsen5 NVMe SSD ...
【学习笔记】WPF-02:XMAL基础 ...
Linux(CentOs7)基础配置及安装本地yum ...
Neo4j
标签云
渠道
国产数据库
集成商
AI
运维
CIO
存储
服务器
快速回复
返回顶部
返回列表