写点什么

理解 JVM 工作机制(三)对象的内存布局

作者:ue4
  • 2023-05-24
    上海
  • 本文字数:1746 字

    阅读完需:约 6 分钟

存储布局

在 HostSpot 虚拟机中,对象在堆内存中的存储布局可用划分为三个部分


  • 对象头(Header)

  • 实例数据(Instance Data)

  • 对齐填充(Padding)


各部分存储的数据

对象头

第一部分,存储对象自身的运行时数据


  • HashCode

  • GC 分代年龄

  • 锁状态标志

  • 线程持有的锁

  • 偏向锁 ID

  • 偏向时间戳


这部分的数据的长度在 32 位和 64 位的虚拟机中(未开启压缩指针)分别为 32bit 和 64bit,称为Mark Word


第二部分,类型指针


即对象指向它的类型元数据的指针,用来确定该对象属于哪个类的实例


如果对象是一个数组,那在对象头中还必须有一块用于记录数组长度的数据

实例数据

存储各种类型的字段内容,包括父类继承和子类定义的字段。


分配策略


-xx:FieldAllocationStyle


默认顺序:longs、doubles、ints、shorts/chars/bytes/booleans、oops

对齐填充

HotSpot 虚拟机的自动内存管理要求对象的起始地址都必须是 8 字节的整数倍。对象头已经被设计成 8 字节的整数倍了(1 倍或者 2 倍)。因此,如果实例数据没有对齐的话,就需要这对其填充来补全。


通过栈中的reference来访问对象,一种通过句柄访问,另外一种则是直接直接访问


Java 堆溢出

Java 堆用于储存对象实例,我们只要不断地创建对象,并且保证 GC Roots 到对象之间有可达路径来避免垃圾回收机制清除这些对象,那么随着对象数量的增加,总容量触及最大堆的容量限制后就会产生内存溢出异常。


VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError


关于虚拟机栈和本地方法栈,在《Java 虚拟机规范》中描述了两种异常:1)如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出 StackOverflowError 异常。2)如果虚拟机的栈内存允许动态扩展,当扩展栈容量无法申请到足够的内存时,将抛出 OutOfMemoryError 异常。


《Java 虚拟机规范》明确允许 Java 虚拟机实现自行选择是否支持栈的动态扩展,而 HotSpot 虚拟机的选择是不支持扩展,所以除非在创建线程申请内存时就因无法获得足够内存而出现 OutOfMemoryError 异常,否则在线程运行时是不会因为扩展而导致内存溢出的,只会因为栈容量无法容纳新的栈帧而导致 StackOverflowError 异常。


-Xss1024k 栈内存设置


在 Java 8 里面不存在永久代了,取而代之的是元空间。


补充一下:方法区只是一个概念,永久代或者元空间是它的实现方法区里面的那些东西,大部分都被移到堆里面去了,还剩下一些元数据被保存在元空间里面

元空间简介

元空间是保存元数据的地方


元数据:描述数据的数据例如一部电影,导演:---、主演:---、投资方:---,这些信息中的导演、主演、投资方就叫元数据,他们加一起描述了整部电影的详细数据那么在元空间里面保存的就是类的元数据,如方法、字段、类、包的描述信息,这些信息可以用于创建文档、跟踪代码中的依赖性、执行编译时检查

元空间存在的意义

在方法区里面,由于方法区大小是启动时就设置好的,可以用默认的也可以手动指定: -XX:MaxPermSize ,但是他始终还是有限定大小的(触发永久代 OOM 后该大小会动态调整),也就是说会发生 OOM


此外永久代的 GC 特别难搞,严重影响 Full GC 的性能。于是抛弃永久代,使用元空间,从而解决这些问题

元空间如何提高性能

永久代里面的常量池都移到堆里面,只保存元数据,从而让 Full GC 不再关心方法区元空间使用直接内存,理论上系统内存有多大,元空间就可以有多大,不存在 OOM 元空间有单独的元空间虚拟机执行内存分配与垃圾回收元空间工作流程虽然元空间在本地内存中,但是还是可以通过:-XX:MetaspaceSize 来控制它的初始大小,达到该值就会触发垃圾收集进行类型卸载


同时 GC 时会对该值进行调整:如果释放了大量的空间,就适当降低该值;如果释放了很少的空间,那么在不超过 MaxMetaspaceSize 时,适当提高该值。


类加载器存储的位置就是元空间,每一个类加载器的存储区域都称作一个元空间,所有的元空间合在一起就是我们一直说的元空间。当一个类加载器被垃圾回收器标记为不再存活,其对应的元空间会被回收。

元空间的内存分配

由元空间虚拟机负责,采用的形式为:组块分配。


元空间虚拟机维护着一个全局的空闲组块列表,当一个类加载器需要元空间内存的时候就从这里面找一块,用完后再释放。类加载器维护的这个组块又分为多个小块,每一块存储一个单元的元信息,分配方式为指针碰撞法。


存在的问题:由于元空间的内存分配值组块方式的,所以存在内存碎片问题(目前不支持压缩)

发布于: 刚刚阅读数: 5
用户头像

ue4

关注

还未添加个人签名 2020-08-26 加入

还未添加个人简介

评论

发布
暂无评论
理解JVM工作机制(三)对象的内存布局_Java_ue4_InfoQ写作社区