一、JVM整体结构及内存模型
JVM虚拟机主要由以下部分组成:
- 栈(线程):存放线程的局部变量,特点:先进后出(FILO),先执行完子方法再执行完主线程
- 栈帧:存放线程中方法的局部变量、操作数栈、动态链接和方法出口。
a. 局部变量:方法中的变量
b. 操作数栈:临时保存对变量进行操作后的值,例如:临时保存2*3的值6
c. 动态链接:保存方法的地址
d. 方法出口:保存原入口执行位置,用于继续执行原方法剩余代码,定位原方法执行位置 - 程序计数器:记录存放执行代码的位置
- 堆:存放对象的区域
- 方法区(元空间):类存放地址(常量、静态变量和类信息)。使用的是物理内存。
- 本地方法栈:存储C语言写的方法的一个栈
通过分析JVM的各个组成部分可以看出其中占用内存最大的就是堆,堆作为存储对象的空间,咱们再深入了解堆的结构
二、堆的内存模型
堆内存主要由以下部分组成:
- Eden区:对象主要存放位置,
- survivor区(s0/s1):当执行yong gc时,存储新生代里面的非垃圾对象
- 年轻代:eden+survivor
- 老年代:存放多次yong gc后还存在的对象,大对象也存放在这里
JVM默认情况下,堆内存分配比例:
年轻代/老年代=1:2
Eden:s0:s1 = 8:1:1
例如:分配给堆内存工1G,则分配情况如下
老年代占2/3*1G
年轻代占1/3*1G
Eden占1/3*8/10*1G
s0占1/3*1/10*1G
s1占1/3*1/10*1G
GC原因理解
GC的主要原因:堆内存满了。当进行GC时,JVM会进入STW状态(stop the world),此时JVM是无法处理业务的。
GC分为两种:Young GC和Full GC
Young GC出现原因
在不断创建对象的过程中,当Eden区域被占满,此时会开始做Young GC也叫Minor GC,执行GC流程如下:
1) 对Eden区域和S0/S1区域的对象进行扫描,找出可销毁的对象
2)将Eden区域经过GC不能被回收的对象存储到To S0/S1 区域(轮流存储)。大对象和存活很久的对象会被存储到老年代
Full GC出现原因
当老年代区域被占满时就会开始执行Full GC,Full GC是一个长耗时操作应尽量避免。此时若仍无法回收到足够的空间则会出现OOM(out of memory)。
JVM内存参数设置/优化
关于元空间的JVM参数有两个:-XX:MetaspaceSize=N和 -XX:MaxMetaspaceSize=N
-XX:MaxMetaspaceSize: 设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。
-XX:MetaspaceSize: 指定元空间触发Fullgc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M,达到该值就会触发 full gc进行类型卸载, 同时收集器会对该值进行调整: 如果释放了大量的空间, 就适当降低该值; 如果释放了很少的空间, 那么在不超 过-XX:MaxMetaspaceSize(如果设置了的话) 的情况下, 适当提高该值。这个跟早期jdk版本的-XX:PermSize参数意思不一样,- XX:PermSize代表永久代的初始容量。
由于调整元空间的大小需要Full GC,这是非常昂贵的操作,如果应用在启动的时候发生大量Full GC,通常都是由于永久代或元空间发生 了大小调整,基于这种情况,一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大, 对于8G物理内存的机器来说,一般我会将这两个值都设置为256M。
-Xss设置越小count值越小,说明一个线程栈里能分配的栈帧就越少,但是对JVM整体来说能开启的线程数会更多