1、JVM运行时数据区
2、基础
(1)、类加载器
(2)、双亲委派
(2)、沙箱机制
3、垃圾回收算法
(1)、引用计数
(2)、复制
(3)、标记清除
(4)、标记整理
4、GCRoot :
(1)虚拟机栈中的引用对象
(2)方法区中的类静态属性引用的对象
(3)方法区中的常量引用的对象
(4)本地方法栈中JNI(native)引用的对象
5、jvm调优参数
- 标配参数(-version,-help)
- X参数(Xint,Xcomp,Xmixed)
- XX参数(-XX:+/- =)
-Xms = -XX:+InitialHeapSize 初始化堆内存
-Xmx = -XX:+MasHeapSize 最大堆内存
-Xmn = -XX:+ThreadStackSize
-Xms 默认1/64物理内存
-Xmx 默认1/4物理内存
jps -l 查看java后台进程
jinfo 正在运行的程序信息
查看jvm 参数
jinfo -flag 参数 进程号,
jinfo -flags 进程号
java -XX:+PrintFlagsInitial JVM初始化家底
java -XX:+PrintFlagsFinal -version
运行java 打出参数
-XX:+PrintCommandLineFlags
-XX:SurvivorRatio=8 eden/survior
-XX:NewRatio=1 老年代/年轻代
-XX:MaxTenuringThreshold=15 survivor取复制15次后进入老年代
-XX:+PrintGCDetails打印GC细节
[GC (Allocation Failure) [PSYoungGen: 2048K->506K(2560K)] 2048K->1021K(9728K), 0.0501431 secs] [Times: user=0.00 sys=0.00, real=0.07 secs]
[Full GC (Allocation Failure) [PSYoungGen: 0K->0K(2560K)] [ParOldGen: 1475K->1455K(7168K)] 1475K->1455K(9728K), [Metaspace: 3259K->3259K(1056768K)], 0.0096555 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
OOM
- java.lang.StackOverflowError:堆栈溢出,线程请求栈深大于jvm最大深度,-Xss配置单个栈大小
- java.lang.OutOfMemoryError:Java heap space 堆内存溢出
- java.lang.OutOfMemoryError:GC overhead limit exceeded 超过98%的时间用来GC,只回收了2%的对象
- java.lang.OutOfMemoryError:Direct buffer memory 本地内存中,不断new出新对象,导致溢出,由于堆内存大小够用不发生GC,netty中出现
- java.lang.OutOfMemoryError:unable to create native thread 项目中线程数超过了服务器最大线程数的限制,linux非root用户限制为1024
- java.lang.OutOfMemoryError:Metaspace 元空间溢出
JVM参数演示(GC overhead limit exceeded)
-Xms10m -Xmx10m -XX:+PrintGCDetails -XX:MaxDirectMemorySize=5m
GC回收时间过长导致OOM,过长的定义是,超过98%的时间用来GC,只回收了不到2%的堆内存空间,若不抛出
GC overhead limit exceeded 会发生:CPU使用率一直是100%,而GC没有任何成果
Direct buffer memory
nio程序经常使用ByteBuffer来读取或写入数组,这是一个基于通道与缓冲区的I/O方式,他可以使用native函数库直接分配堆外内存,然后通过一个存储在java堆里面的DirectByteBuffer对象作为这块内存的引用进行操作,这样能在一些场景中显著的提高性能,因为避免了在Java堆和native堆中来回复制数据
ByteBuffer.allocate(capability)分配jvm内存,属于GC范畴,由于需要拷贝,速度较慢
ByteBuffer.allocateDirect(capability)分配DS本地内存,不属于GC范畴,速度较快
但如果不断分配本地内存,堆内存很少使用,那么jvm就不需要执行GC,DirectByteBuffer对象就不会被回收,这种情况堆内存充足,本地内存已经用光,在尝试分配内存,就会发生OOM,程序就直接奔溃了
unable to create native thread
应用程序创建太多线程超过系统负载极限
解决:限制创建线程程数,扩大系统线程限制配置
Metaspace 元空间溢出,-XX:MaxMetaspaceSize=5m
java8版本之后使用Metaspace替代永久代,Metaspace不属于虚拟机内存中,而是使用本地内存
垃圾回收器就是GC算法 的落地实现(4种,7大收集器)
Serial (UseSerialGC,UseSerialOldGC)
Parallel(UseParallelGC)
CMS(ConcMarkSweep)(UseConcMarkSweepGC,UseParNewGC)
G1(UseG1GC)
我们公司的一段jvm配置,使用的CMS收集器
-XX:+UseConcMarkSweepGC,-XX:+UseParNewGC
gceasy.io网站很好用,一个很好的在线工具配置jvm参数
查看默认的垃圾收集器
$ java -XX:+PrintCommandLineFlags -version
-XX:InitialHeapSize=132860928 -XX:MaxHeapSize=2125774848 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC
java version “1.8.0_102”
java8默认用的ParallelScavenge,Parallel Old
-
Serial:串行收集器
开启:-XX:+UseSerialGC
它为单线程环境设计,只使用一个线程进行垃圾回收,会暂停所有用户线程,执行GC
开启后 Serial(Young区)+Serial Old(Old区)收集器组合
表示新生代和老年代都会使用串行收集器,新生代用复制算法,老年代用标记整理算法
-XX:+UseParNewGC 启用ParNew收集器,只影响新生代,不影响老年代。老年代不配的话默认Serial Old, 新生代用复制算法,老年代用标记整理算法 -
Parallel:并行收集器
开启:-XX:+UseParallelGC,-XX:+UseParallelOldGC可相互激活,
多个垃圾收集线程一起执行,也会暂停所有用户线程,
开启后Parallel Scavenge +Parallel old 收集器组合,
新生代用复制算法,老年代用标记整理算法
-XX:ParallelGCThreads=N 表示会使用N个线程执行GC
CPU>8 N=5/8
CPU<8 N=实际个数 -
CMS收集器(ConcurrentMarkSweep并发标记清除)
开启:-XX:+UseConcMarkSweepGC
是一种以获得最短停顿时间的收集器,适合互联网或者B/S服务器上,适合内存大,cpu核多的服务器,是G1出现前最适合的垃圾回收器
开启后 ParNew(Young区)+CMS和Serial(Old区)收集器组合
新生代用复制算法,老年代用标记清除算法
大部分互联网公司使用
缺点:产生大量内存碎片
-
G1收集器
开启:-XX:+UseG1GC
充分利用多CPU,多环境优势充分缩短STW(停顿时间)
使用整体标记整理算法
优势明显
STW可控,甚至可以预测GC停顿时间自己设置期望的时间
原理:
总结:
JVM 结合spring boot参数调优
实际工作中如何进行调优
1.idea开发完微服务工程
2.maven进行clean打包
3.要求微服务启动的时候配置jvm gc的调优配置
4.java -server jvm各种参数 -jar 包名