1.JVM内存模型,栈、本地方法栈、程序计数器、堆、元空间、方法区、本地方法区,除程序计数器外,其他区域都能进行垃圾收集
2.栈,它的生命周期与线程相同,线程私有,会使用操作系统原生内存,方法内的局部变量、对象引用等,使用的内存会随着方法执行完,线程销毁而被操作系统回收,栈内存默认1M,JVM通过 -Xss256k 命令设置大小,.栈设置的越大,允许的栈深度越深(如递归),如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常
3.本地方法栈是执行本地Native方法的
4.程序计数器,保存代码执行位置,CPU下次再次执行时会从保存的位置处往下执行
5.方法区(元空间)用来存储类型的元数据信息,如:类全限定名、字段信息、方法名、方法代码、方法返回类型、常量池
6.堆,JVM最大内存区域,参数调优主要是该区域,虚拟机把堆内存按 分代 模型划分 新生代、老年代
6.1.新生代中的对象大多数都符合“朝生夕灭”,使用Young GC 往往能快速清理掉大部分垃圾,留下来的会在Survivor1与Survivor2直接来回拷贝,并增加对象的年龄,达到年龄的和大对象一起放到老年代
6.2.老年代存放新生代中多次未被回收的对象和大对象, 使用 Full GC,(1.每次Young GC之前会进行一些判断,看看是否需要老年代先进行一下Full GC,2.Young GC之后,存活对象Survivor区放不下,老年代也放不下了,那么触发一次Full GC)如果收集后还无法申请到内存,则会抛出OutOffMemoryError
6.3.垃圾对象判定标准:1.引用计数法(2个类互相引用时无法释放),2.可达性分析算法(JVM使用此方法)
6.4.垃圾回收算法:堆内存中,JVM根据不同年代(或JDK版本),使用不同垃圾回收算法
- 标记-清除:标记出垃圾对象,直接清楚掉,会有内存碎片
- 复制:复制算法速度快,内存使用率不高,新生代回收后只保留少量存活对象,复制到Survivor区
- 标记-整理:标记所有可达对象,完成后未被标记的对象将会被清理掉,然后将所有存活的对象压缩到内存的一端,标记-整理算法解决复制算法内存减半的高额代价问题
6.5.JVM垃圾回收算法
- Serial垃圾收集器:单线程回收,进行 Young GC 和 Full GC时所有的应用线程都会被暂停,适用于32位小型机小内存环境,使用 -XX:+UseSerialGC
- ParallelGC垃圾收集器:多线程回收,进行 Young GC 和 Full GC时所有的应用线程都会被暂停,JDK 7u4 及之后版本的默认该收集器,使用 -XX:+UseParallelGC -XX:+UseParallelOldGC显示指定(前者是收集新生代、后者是收集老年代)
- CMS收集器:多线程回收,进行 Young GC时所有的应用线程都会被暂停,Full GC 时不再暂停应用线程,(使用多个后台线程收集老年代,付出的代价是更高的 CPU 使用),JDK8中CMS收集器默认是关闭的,使用 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC 显示开启(前者是收集新生代、后者是收集老年代)
- G1垃圾收集器:多线程回收,新生代的垃圾收集仍然采用暂停所有应用线程,它把整个堆分成了2048个小区域,每个区域可能是新生代或老年代,对符合回收的区域进行回收,JDK8中使用 -XX:+UseG1GC 开启
- ZGC垃圾收集器 :JDK11中可使用的回收算法,在G1上进行了多项优化,使用XX:+UnlockExperimentalVMOptions -XX:+UseZGC ,可以参考腾讯开源的Tencent Kona JDK11
6.6.怎么选择垃圾回收算法,建议如下:
- CPU核心少,内存1-2GB的,使用JDK默认的收集器即可
- CPU核心大于4,内存4-6GB的,使用CMS垃圾收集器: ‐XX:+UseParNewGC -XX:+UseConcMarkSweepGC
- CPU核心大于8,内存6-32GB的,使用G1垃圾收集器: ‐XX:+UseG1GC
与CPU有关是因为CMS、G1都会开启多个后台线程来收集垃圾,如果CPU核心不够,会造成CPU资源竞争、退化为单线程收集,性能更慢
6.7.JVM关键参数设置:
- 默认启动:java -jar xxx.jar
- 设置最大内存启动:java -jar -Xms2g -Xmx2g xxx.jar
- 设置新生代启动:java -jar -Xms4g -Xmx4g -Xmn1g xxx.jar
- 使用CMS收集器启动:java -jar -Xms6g -Xmx6g ‐XX:+UseParNewGC -XX:+UseConcMarkSweepGC xxx.jar
- 使用G1收集器启动:java -jar -Xms16g -Xmx16g ‐XX:+UseG1GC -XX:MaxGCPauseMillis=120 -XX:ParallelGCThreads=8 xxx.jar
预期停顿时间是120ms:-XX:MaxGCPauseMillis=120
垃圾收集线程数:-XX:ParallelGCThreads= N ,N = 8 + ((N - 8) * 5 / 8), N代表CPU 的数目
如果服务器上有多个JVM实例,则需要调整ParallelGCThreads数量,过多的线程数会导致争抢CPU资源,ParallelGCThreads = min(CPU数量/JVM实例数, 8+((N-8)*5/8))
更多其他参数请自行查阅
7.直接内存,NIO相关类使用了直接内存,避免数据在内核与JVM之间来回拷贝
8.JVM工具
1.jps 查看java进程
2.jstat 虚拟机统计工具
jstat -gcutil 2764
S0 S1 E O M YGC YGCT FGC FGCT GCT
0.00 0.00 6.20 41.42 47.20 16 0.105 3 0.472 0.577
E表示新生代Eden区使用了6.2%的空间,2个S0、S1表示Survivor0、Survivor1,老年代(O,表示Old)和元空间(M)则分别使用了41.42%和47.20%的空间。
程序运行以来共发生Minor GC(YGC,表示YoungGC)16次,总耗时0.105秒;发生Full GC(FGC,表示Full GC)3次,总耗时(FGCT,表示Full GCTime)为0.472秒;
所有GC总耗时(GCT,表示GC Time)为0.577秒
3.jinfo 实时查看和调整虚拟机各项参数, jinfo pid
4.jmap 用于生成堆转储快照dump文件,jmap pid ,jmap -dump:format=b,file=zy.bin 30790
5.jhat 分析jmap生成的快照文件,jhat zy.bin (一般使用VisualVM,不用此命令工具)
6.jstack 用来查看线程停顿情况,jstack [option] pid ,jstack -l 30790,option有 -F(当正常请求不被响应时,强制输出线程堆栈),-l(除堆栈外还输出锁信息),-m(可以显示c/c++本地方法堆栈)
7.VisualVM https://visualvm.github.io
8.JVM参数调优可以应用到eclipse或Idea,如:
-XX:+UseParNewGC
-XX:+UseConcMarkSweepGC
或单独使用-XX:+UseG1GC
9.查看JVM运行时长:jcmd process_id VM.uptime
10.查看JVM属性:jcmd process_id VM.system_properties 或 jinfo -sysprops process_id
11.获取JVM版本:jcmd process_id VM.version
12.查看JVM内存:jinfo -flags process_id
13.查看每个线程栈信息:jstack process_id