运行时常量池(Run-time Constant Pool)
写在前面
运行时数据区在JVM规范中属于方法区的一部分,这里之所以单独拎出来阐述有两方面的考虑:
- 在JVM规范中也单独开了一个小节介绍运行时常量池(Run-time Constant Pool),可见其重要性。
- 在JVM实现中可能根据实际情况,并不会吧运行时常量池中的所有部分全部划分到方法区中,因此提前讲述方便后续理解。
概述
- 运行时常量池中的大部分信息来自于Class文件中的常量池,只不过将其中的符号引用转变为了直接引用。
- 当类和接口被创建时,它们对应的运行时常量池同时被创建。
常量池的分类
这里的分类不是指对运行时常量池的分类,实际上在JVM规范中也没有对运行时常量池的分类,只是在一些实际场景中为了方便沟通因此出现了一些名词,这里做一个简单的整理,方便读者在遇到这类名词的时候不至于理解不一致。
静态常量池
静态常量池其实是相对于运行时常量池,也就是说这个静态常量池指代的就是class文件格式中的常量池。
运行时常量池
即静态常量池加载到jvm中后形成的常量池。
字符串常量池
设计理念:字符串作为最常用的数据类型,为减小内存的开销,专门为其开辟了一块内存区域(字符串常量池)用以存放。
- JDK1.6及之前版本,字符串常量池是位于永久代(相当于现在的方法区)。
- JDK1.7之后,字符串常量池位于Heap堆中。
- 字符串常量池其实也是属于运行时常量池的,只不过在实际规范落地时特殊开辟了这样的区域
方法区(Method Area)
概述
- 方法区在JVM启动时创建,属于JVM中工作线程共享的区域。
- 虽然Java虚拟机规范中将方法区描述为堆的一个逻辑部分,但是方法区又被称为非堆(No-Heap),目的是为了和堆区别开来
- 方法区主要存放虚拟机加载的运行时常量池,类元信息,即时编译器之后的代码等数据。
- 注意:方法区(Method Area)只是jvm规范中的一个定义名称,例如hotspotJVM中的永久代(Perm Space)或者JDK1.8之后的元空间(Metaspace)都是方法区(Method Area)的具体实现。
元空间(Metaspace)
元空间(Metaspace)是JDK1.8的hotspot虚拟机对方法区(Method Area)的实现。
为什么使用元空间(Metaspace)
- 元空间(Metaspace)和永久代(Prem Space)最大的区别就是元空间(Metaspace)没有使用JVM的内存区域而是直接使用堆外内存。
- 由于类的数量不好把控,类的大小随程序迭代也不好及时计算,因此如何分配类信息所占的大小是件困难的事情,而采用堆外内存就可以避免程序员对方法区的内存大小的管理。
- 元空间中不包含字符串常量池,字符串常量池转移到了堆区,这样可以防止性能问题和内存溢出。
堆(heap)
概述
- 堆区在JVM启动时创建,属于JVM中工作线程共享的区域。
- 堆区是JVM管理的内存中最大的区域。
- 主要用于存储:对象实例
对象实例的数据结构(对象模型)
对象的实例数据结构如下图所示:
概要图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FMgH1FG3-1692787565745)(C:\Users\qianhao\Downloads\Java对象模型.png)]
对象头
对象头主要被分为三大部分:Mark Word,Class Pointer,Length
Mark Word
概述
- 一系列标记位,包括(哈希码,分代年龄,锁状态标志等)
- 主要用于GC和Java内置的对象锁的使用
- 64位操作系统占 8字节
结构图
Class Pointer
概述
- 类信息指针,主要指向方法区内的类信息。
- 64位操作系统占 8字节
指针压缩
- jdk1.6之后对于64位的操作系统,Jvm默认开启指针压缩技术,将原本占8字节的指针压缩至4字节。
- 对于JVM对象存储内存超过32G后,指针压缩技术就会失效。
至于为什么会失效因为jvm中最小单位是字节也就是8bit,而32位表示的大小是4G,因此在jvm中如果填满32位的内存,实际的JVM内存大小就是4G*8=32G。
实例数据
- 存放对象中所拥有的成员变量
- JVM采用大端方式存储数据
对齐填充
对齐填充的意义是 提高CPU访问数据的效率 ,主要针对会存在该实例对象数据跨内存地址区域存储的情况。
TLAB
TLAB(Thread Local Allocate Buffer),是在堆中开辟的一块专属于线程的内存空间,因此堆中也是存在一部分区域是线程私有的。
TLAB主要是为了尽量避免在高并发环境下从堆上直接分配内存从而避免频繁的锁争用。
TLAB由于是线程私有的因此是在线程初始化的时候被开辟的空间,但是线程消亡后对应的TLAB空间不会被回收只是和被回收的线程无关了,此时只有发生GC操作才会将TLAB空间回收。