运行时数据区域
所有线程共享的数据区:
- 方法区 :
- 别名“非堆”,用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。
- JDK7的HotSpot,将放在永久代的字符串常量池、静态变量等移至Java堆中。
- JDK8后,永久代概念被完全废弃,改用本地内存中实现的元空间代替。
- Java堆 :
- Java世界里“几乎”所有对象实例都在这里分配内存——栈上分配、标量替换等优化手段导致对象实例并不绝对分配在堆上了。
- Java堆中无论设计风格下的“区域”都只能存储对象的实例,将堆细分的目的只是为了更好地回收内存 或 更快地分配内存。
- Java堆可以处于物理上不连续的内存空间中,但对于大对象(典型的如数组对象)多数虚拟机实现出于实现简单、存储高效的考虑会要求连续的内存空间。
线程隔离的数据区:
-
虚拟机栈 :
- 描述Java方法执行的线程内存模型,每个方法被执行时JVM会同步创建一个帧栈用于存储局部变量表、操作数栈、动态连接、方法出口等信息。
- 每一个方法被调用直至执行完毕,对应一个帧栈在虚拟机栈中从入栈到出栈的过程。
- 程序员们通常指的“堆栈”中的栈狭义指局部变量表:存放了各种JVM基本数据类型、对象引用、returnAddress类型。
- 局部变量槽的概念,64位的long和double占用两个;进入一个方法时该方法被分配多少变量槽是固定的。
-
本地方法栈 :
- 与虚拟机栈作用类似,区别在于虚拟机栈为虚拟机执行JAVA方法服务,而本地方法栈为JVM使用本地方法提供服务。
-
程序计数器 :
- 记录当前线程执行的字节码行号,为了线程切换后能恢复到正确的位置。
- 若线程执行Java方法,则计数器记录正在执行的虚拟机字节码指令的地址;若正在执行本地方法,则值为空。
JVM运行时数据区域抛出异常的情况
- 抛出
StackOverflowError
:- 虚拟机栈:线程请求的栈深度大于虚拟机所允许的深度。
- 本地方法栈:线程请求的栈深度大于虚拟机所允许的深度。
- 抛出
OutOfMemoryError
:- 虚拟机栈:若Java虚拟机栈 容量可以动态扩展,当栈扩展时无法申请到足够的内存。
- 本地方法栈:若本地方法栈 容量可以动态扩展,当栈扩展时无法申请到足够的内存。
- Java堆:若Java堆中没有内存完成实例分配,且堆也无法再扩展时。
- 方法区:无法满足新的内存分配需求时。
HotSpot在Java堆中对象分配、布局和访问全过程
对象的创建
检查该类的符号引用是否已被加载 → 分配内存空间 → 初始化零值 → 进行必要设置 → 构造函数 → 将对象入栈
对象的内存布局
对象在堆内存中的存储布局分为:对象头、实例数据、对其填充(不必要)。
对象头(Mark Word)
对象头包括两类信息:
- 用于存储对象自身运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有锁、偏向线程ID、偏向时间戳等。
- 类型指针,即指向该对象类型元数据的指针。
实例数据
定义的各种类型字段内容。
对象的访问定位
Java程序通过栈上的reference数据操作堆上的具体对象,定位的方式由虚拟机实现而定,主流有 使用句柄 和 直接指针。
使用句柄
Java堆中划分一块内存作为句柄池,reference存储句柄地址,句柄包含了对象实例数据(Java堆的实例池中)与类型数据(方法区中)的地址信息。
优势:对象被移动时(垃圾收集时移动对象非常普遍)只会改变句柄中的实例数据,不会修改reference。
直接指针
栈中reference直接存储Java堆中的实例数据地址,类型数据的地址需要保存在对象的内存中(如对象头)。
优势:相比句柄节省一次指针定位的时间开销(从句柄指向实例数据),在Java中访问对象很普遍,因此积少成多也是一项可观的执行成本。
OOM(内存溢出)实战
P55-P66
其中虚拟机栈和本地方法栈存在由于给栈设置分配内存越大导致越容易内存溢出OOM的原理:
- 设n为设置的栈分配内存,m为可动态扩展数量上限
- 在32位Windows下单个进程最大内存限制2GB,则 n * m = 2GB - 最大堆容量
- n越大,则m越小,所以更容易导致OOM
一些虚拟机参数
-
-XX:+HeapDumpOnOutOfMemoryError
:让虚拟机在OOM时Dump出当前的内存堆转储快照。 -
-Xmx
、-Xms
:堆内存的最大值、堆的最小值,设为一样则可以避免堆自动扩展 -
-Xss
:栈内存容量 -
-XX:MaxPermSize
、-XX:PermSize
:永久代内存最大值、最小值(1.8后弃用),1.6前可间接控制常量池大小 -
-XX:MaxMetaspaceSize
:元空间最大值;-XX:MetaspaceSize
:元空间初始空间大小,单位字节,达到该值会触发垃圾收集进行类型卸载,同时调整元空间最大值;-XX:MinMetaaspaceFreeRatio
:在垃圾收集后控制最小的元空间剩余容量百分比,可减少因元空间不足导致的垃圾收集频率;XX:MaxMetaaspaceFreeRatio
:在垃圾收集后控制最大的元空间剩余容量百分比。 -
-XX:MaxDirectMemorySize
:直接内存容量大小,默认与Java最大堆一致。