深入理解虚拟机阅读笔记-运行时数据区域
运行时数据区域
-
- 程序计数器(pc寄存器)
- 线程私有,空间较小,是当前线程所执行字节码的行号指示器
- 字节码解释器工作时,通过改变计数器的值来取得下一条字节码指令。
- 分支、循环、跳转、异常处理、线程恢复等操作的依赖。
- Java方法:当前字节码指令的地址;
- Native方法,则为空。
- 唯一无OutOfMemoryError情况的内存区域
- Java虚拟机栈
- 线程私有,描述的是Java方法执行时的内存模型
- 方法执行时创建栈帧入栈,方法执行结束出栈
- 栈帧:用于存局部变量表、操作数栈、动态链接、方法出入口等信息
- 局部变量表
- 存放编译期可知的数据,内存空间在编译期完全确定,运行期间不会改变
- 各种基本数据类型,64位长度的long和double栈两个局部变量空间 (slot),其余一个
- 对象引用(reference类型,直接指向对象地址的指针,或者指向对象句柄)
- returnAddress类型(指向了一条字节码指令的地址)
- 局部变量表
- 异常情况
- 线程请求的栈深度大于虚拟机运行深度,抛出StackOverflowError
- 若虚拟机栈运行动态扩展,则当扩展时无法申请到足够的内存,抛出OutOfMemoryError
- 本地方法栈
- 与虚拟机栈类似,不过服务于Native方法
- 异常情况与虚拟机栈相同
- 虚拟机规范不对该区域使用的语言、方式和数据结构做强制规定,可自由实现
- Java堆
- 空间最大,线程共享,虚拟机启动时创建,用于存放对象实例
- 内存空间属于物理不连续
- Java虚拟机规范:所有的对象实例与数组都要在堆上分配
- 无法动态扩展时,抛出OutOfMemoryError
- 内存划分
- young:old 默认为 1:2
- 年轻代
- Eden:Survivor = 8 :1
- 存在一个Eden区域与两块Survivor
- Eden
- 存放的刚创建的对象,当该部分被耗尽时,就进行young gc(Minor gc)。
- Survivor
- 每次young gc,将eden存活的对象和另一个Survivor的对象,整理到当前servivor,保证存储自身存储空间的连续性以及另外空间的空闲。
- 老年代
- 默认存放着Survivor中经过了15次Minor gc的对象
- 或者当Minor gc时,Survivor内存不足,依赖老年代做分配担保,直接将对象转移进该区域
- 或者当对象大于一定大小,则直接在老年代分配
- 方法区
- 线程共享,用于存储加载的常量、静态变量、即时编译器编译后的代码等
- 线程安全,在类加载时,只允许有一个线程去加载某个类信息到方法区
- 永久代(1.8后移除):Hotspot把gc分代收集扩展至方法区,方便如堆一般管理内存
- 无法满足内存分配需求时,抛出OutOfMemoryError
- 运行时常量池
- class文件存放编译期生成的字面量和符号引用,在类加载后存入该区域。
- 也存放翻译出来的直接引用
- 动态性:允许运行期将新的变量防区
- 无法满足内存分配需求时,抛出OutOfMemoryError
- 直接内存
- 堆外内存,非虚拟机规范定义,不受虚拟机本身限制
- 无垃圾回收管理
- 1.4引入的NIO类,基于通道Channel与缓冲器Buffer的IO方式,使用Native函数直接分配堆外内存
- 通过Java对中的DirectByteBuffer对象作为这块内存的引用进行操作。
- 通过参数指定大小,默认与堆最大值相同
- 分配内存时若出现内存溢出,并不是申请分配内存,而是计算出内存不足,手动抛出异常
- 元空间
- 存储加载的类信息
- 元数据区取代了永久代,本质和永久代类似,都是对JVM规范中方法区的实现,区别在于元数据区并不在虚拟机中,而是使用本地内存。元数据区在频繁使用,也会发生OutOfMemory异常。
- 元数据区的动态扩展,默认–XX:MetaspaceSize值为21MB的高水位线。一旦触及则Full GC将被触发并卸载没有用的类(类对应的类加载器不再存活),然后高水位线将会重置。新的高水位线的值取决于GC后释放的元空间。如果释放的空间少,这个高水位线则上升。如果释放空间过多,则高水位线下降。