一、JVM虚拟机组成
JVM虚拟机主要由三块组成:
类装载子系统:由c++实现
运行时数据区(内存模型):堆、栈、方法区、程序计数器等
字节码执行引擎:由c++实现
我们要想想执行一个java.class文件,首先类装载子系统会将字节码文件放入jvm的内存区域(运行时数据区),最终由字节码执行引擎来执行内存区域的这些代码。
二:jvm内存区域详解
1、堆:存放new出来的对象。
2、栈:先进后出(线程栈,线程私有)存放局部变量。在线程执行方法时,会为该线程在栈内分配一块专属的内存,用于存放该线程内部的局部变量。而每一个方法都对应一块栈帧内存区域。 栈帧内部:局部变量表、操作数栈、动态链接、方法出口
3、本地方法栈:(线程私有)为native方法分配
4、程序计数器:(线程私有)存放代码的位置标识(即程序要运行的那行代码的内存指针位置),每执行完一行代码,字节码执行引擎会动态修改程序计数器的值。作用:当另一优先级更高的线程抢占cpu后,当前线程需挂起,一段时间后,若该线程再次获取cpu后,此线程会根据程序计数器的值判断出上次已执行到该程序的哪行。
5、方法区(元空间):存放常量、静态变量、类元信息(类的代码信息)
注:有些new出的对象也存放在栈里
三:堆
minor gc:回收年轻代中对象,年轻代中对象年龄到15岁时,对象会复制到老年代。
最终放入老年代的对象:静态变量引用的对象、对象池、缓存、缓存对象、spring容器中的对象。
若老年代中对象放满,触发full gc,若对象还是无法回收,触发OOM。
full gc 回收整个堆以及方法区中对象。
在整个gc过程中可能会发生STW机制。
我们将线程分为两类:字节码执行引擎开启的后台线程和用户发起的线程用户线程。
STW机制:当触发gc时,所有的用户线程被停止,这时用户会感到卡顿,minor gc回收时间较短,full gc 回收时间会比较长,所以full gc 会导致STW的时间变长。
设计STW机制的目的是为了什么?
若无STW机制,在后台线程找垃圾对象的过程中,用户线程仍在执行,即用户线程仍在生产对象,后台线程在找到这些刚生产的对象时,它们还被引用着,后台线程判定这些对象为非垃圾对象,而这些对象又很快被执行完,变为垃圾对象,若用户线程特别多,那这种对象的产生也会很多,让后台线程再重新扫描进行一次gc就不太合适了。我们jvm调优的目的也就是为了减少gc尤其full gc。
四、JVM内存参数设置
- 堆:-Xms -Xmx
- 新生代:-Xmn
- 栈:-Xss(一个线程占用大小)
- 方法区:-XX:MetaspaceSize -XX:MaxMetaspaceSize
-XX:MaxMetaspaceSize:元空间真正使用的是直接内存(物理内存、内存条、非堆内存),若不设置默认-1,分配堆栈等的内存空间后剩下的方法区均可使用,即只受限于本地物理内存大小。
-XX:MetaspaceSize:默认21M,动态水平自动伸缩机制,若方法区占用内存达到21M,触发full gc回收垃圾信息,如果释放了大量空间, 就适当较小该值; 如果释放少量空间,适当提高该值。
-Xss:默认1M。因为不能决定具体开多少线程,所以整个栈的大小不能设置。-Xss值越小,一个线程栈里能分配的栈帧就越少,能开启的线程数就越多。