JVM进阶
- 1. JVM垃圾回收的时候如何确定垃圾?是否知道什么是GC Roots?
- 2. JVM调优和参数设置
- 3. JVM常用配置参数
- 4. 谈谈关于OOM的认识
- 5. GC垃圾回收算法和垃圾收集器
- 6. 查看服务器默认的垃圾回收器,如何配置垃圾回收器以及对垃圾回收的理解
- 7. G1垃圾回收器
1. JVM垃圾回收的时候如何确定垃圾?是否知道什么是GC Roots?
垃圾:内存中已经不再被使用到的空间(对象)
判断方法:
- 引用计数法(计数器为0的对象就是不再被引用的,可回收;无法解决循环依赖问题)
- 枚举根节点做可达性分析(根搜索路径):通过一系列名为“GC Roots”的对象作为起始点,也就是从GC Roots的对象开始向下搜索,如果一个对象到GC Roots没有任何引用链相连时,则说明对象不可达。给定一个即可的引用作为根节点出发,通过引用关系遍历对象图,能被遍历到的对象就判定为存活,没有遍历到的对象判定为死亡
GC Roots:tracing GC的根集合,一组必须活跃的引用
哪些可以作为GC Roots的对象:
- 虚拟机栈中引用的对象:栈中的局部变量,也叫作局部变量表
- 方法区中的类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法Native中引用的对象
2. JVM调优和参数设置
JVM的参数类型:
- 标配参数:java -version java -help java -showversion java -c xxx.class -> xxx.txt 进行反汇编(得到JVM执行的class文件)
- X参数:-Xint 解释执行 -Xcomp 第一次使用就编译成本地代码 -Xmixed 混合模式
- XX参数:
- 布尔类型:-XX:+或者-某个属性值, + 表示开启,- 表示关闭。如==-XX:+PrintGCDetails==:打开详细的GC处理日志
- KV设置类型:-XX:属性key=属性值value 如:-XX:MetaspaceSize=128m -XX:MaxTenuringThreshold=15
- jinfo:使用jps -l查看对应线程的id,查看某一个JVM属性的具体值,jinfo -flag PrintGCDetails 线程id
使用jinfo -flags 线程id查看所有的默认JVM参数初始值以及修改的配置值
-Xms:等同于==-XX:InitialHeapSize== ,初始堆内存,设置初始分配大小,默认为物理内存的1/64
-Xmx:等同于==-XX:MaxHeapSize==,最大堆内存,最大分配内存,默认为物理内存的1/4
查看JVM的默认值
- java -XX:+PrintFlagsinitial 查看JVM初始化的参数默认值
- java -XX:+PrintFlagsFinal 主要查看修改更新的内容 :=表示修改的值 =表示JVM默认加载
- java -XX:+PrintFlagsFinal -version

- PrintFlagsFinal举例,运行java命令的同时打印出参数:java -XX:+PrintFlagsFinal -XX:MetaSpaceSize512m 运行的类名称
- java -XX:+PrintCommandLineFlags (可查看默认的垃圾回收器)

- 调优诊断工具:Arthas
直接在github上下载:wget http://alibaba.github.io/arthas/arthas-boot.jar
使用java -jar arthas-boot.jar运行,可以监控所有JVM进程
- 能否进行JVM调优,使得在老年代的Full GC不会频繁的发生?
- 内存太大使得GC停顿时间太长,控制Full GC的频率关键在于太多数对象生命周期不会太长,不能有成批量的对象在老年代进行GC
- 什么情况下需要进行JVM调优:
- Heap内存持续上涨到设置的最大内存值
- Full GC次数频繁
- GC停顿时间过长
- 出现OOM
- 系统吞吐量与响应性能下降
- MinorGC尽可能多的收集垃圾对象,降低Full GC的发生频率,Full GC是导致延迟低和吞吐量的最大原因
- 堆大小的调整,从Minor GC持续时间,Minor GC的次数,Full GC的最大持续时间和频率几个角度出发
- 吞吐量优先的GC收集器:-XX:+UseParallelGC -XX:UseParallelOldGC
- 响应时间的GC收集器:CMS或者G1
3. JVM常用配置参数
元空间与永久代的区别:
java8以后,永久代已经被移除,使用元空间替代。
最大区别在于:永久代使用的是JVM的堆内存,元空间使用的是本机物理内存。因此元空间仅仅受本地内存限制
- -Xms
- -Xmx
- -Xss:-XX:ThreadStackSize 设置单个线程的大小(栈空间),一般默认为512K-1024K(初始值为0则表示使用默认值1024KB)
- -Xmn: 设置年轻代大小
- -XX:MetaSpaceSize 设置元空间大小
- -XX:+PrintGCDetails:打开详细的GC处理日志
- -XX:SurvivorRatio:设置新生代Eden区和Survivor From/To区的比例 默认为8:1:1 设置-XX:SurvivorRatio=4则Eden:From:To = 4:1:1
- -XX:NewRatio:配置老年代和年轻代在堆中的比例 默认为2:1 设置-XX:NewRatio=4 则New:Old = 1:4
- -XX:MaxTenuringThreshold:设置垃圾的年龄 如果设置为0,则年轻代的对象不经过Survivor区,直接进入老年代,对于老年代较多的应用,可以提高效率;如果将值设置较大,则年轻代对象在Survivor区进行多次复制,增加对象在年轻代的存活时间(该值默认为0-15)
设置参数案例:
-XX:SurvivorRatio=4 -XX:MetaspaceSize=512m -XX:+PrintGCDetails -XX:+PrintCommandLineFlags -XX:+UseSerialGC
4. 谈谈关于OOM的认识
-
java.lang.StackOverFlowError:一直递归调用方法,栈溢出错误
-
java.lang.OutOfMemoryError:java heap space :创建对象太多,java堆内存溢出
-
java.lang.OutOfMemoryError:GC overhead limit exceed:GC回收时间过长,超过98%的时间用来做GC并且回收了不到2%的堆内存
-
java.lang.OutOfMemoryError:Direct buffer memory:写NIO程序经常使用ByteBuffer来读取或者写数据,一种基于通常channel和缓冲区Buffer的IO方式,可以使用Native函数库直接分配堆外内存,通过一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作,可以在一些场景中显著提高性能,因为避免了java堆和Native堆中来回复制数据。如果不断的分配本地内存,堆内存很少使用,JVM不需要执行GC,DirectByteBuffer对象就不会被回收;而堆内存充足,单本地内存已经使用完了,再次尝试分配本地内存会出现OOM
-
java.lang.OutOfMemoryError:unable to create new native thread:高并发请求服务器时,经常出现该异常。
原因:
- 应用创建了太多线程,一个应用进程创建多个线程,超过系统承载极限
- 服务器并不允许你的应用程序创建这么多线程,Linux系统默认允许单个进程可以创建的线程数为1024个,一旦超过这个数量,直接报该错误
解决:
- 降低应用程序创建线程的数量,分析应用程序是否真的需要创建这么多线程,修改代码
- 对于有的应用确实需要创建许多线程,可通过修改Linux服务器配置,扩大默认限制
-
java.lang.OutOfMemoryError:Metaspace:元空间内存溢出,而元空间存放的信息有
- 虚拟机加载的类信息
- 常量池(运行时常量池在方法区中,字符串常量池在堆中)
- 静态变量
- 即时编译的代码
当不断生成类往元空间送,类占据的空间总是会超过MetaspaceSize
5. GC垃圾回收算法和垃圾收集器
-
回收算法:
-
垃圾回收器(回收算法的实现):
- Serial:串行GC,为单线程环境设计且只使用一个线程进行垃圾回收,会暂停所有的用户线程
- ParNew:并行GC(多线程),复制算法,搭配CMS
- ParallelOld:并行GC(多线程),复制算法,老年代收集器
- SerialOld:是Serial收集器的老年代版本,串行GC(单线程),“标记-整理”
- Parallel:多个垃圾收集器并行工作,用户线程是暂停的,适用于科学计算和大数据计算
- CMS:Concurrent Mark Sweep并发标记GC(多线程),用户线程和垃圾收集线程同时执行(可能交替执行),不需要暂停用户线程,适用于对响应时间有要求的场景
- G1:全区域(整个堆)的垃圾回收器Garbage First,将堆内存分割成不同的区域然后并发的执行垃圾回收
6. 查看服务器默认的垃圾回收器,如何配置垃圾回收器以及对垃圾回收的理解
java -XX:+PrintCommandLineFlags -version 查看JVM默认参数配置
或者使用jinfo -flags 线程id
在VM options中修改GC回收器:
-XX:+UseParallelGC
-XX:+UseCMSGC
-XX:+UseSerialGC
-XX:+UseG1GC
7大垃圾回收器分类以及理解
Young Generation |
Old Generation |
Serial |
Serial MSC(Serial Old) |
Parallel Scavenge |
Parallel Compacting(Parallel Old) |
ParNew |
CMS |
另外,G1 GC在Young和Old区都能用
-
SerialGC:为单线程环境设计且只使用一个线程进行垃圾回收,会暂停所有的用户线程,只使用一个线程进行垃圾回收可能产生较长的等待(Stop-The-World,STW),是JVM在Client模式下默认的新生代垃圾回收器。
对应的参数为 -XX:+UseSerialGC,开启后会使用:Serial(Young)+ SerialOld(Old)的收集器组合,新生代使用复制算法,老年代使用标记整理压缩算法
-
ParNewGC:使用多线程进行垃圾回收,在垃圾收集时,会STW暂停其他所有工作线程直到收集结束。
对应的参数为:-XX:+UseParNewGC,开启后会使用:ParNewGC(New区)+ SerialOld(Old区)的收集器组合(过时),只影响新生代的收集,不影响老年代,新生代使用复制算法,老年代使用标记整理压缩算法
-
Parallel Scavenge:默认的回收器ParallelGC,ParallelGC(New区)+ ParallelOld(Old区)的组合,并行的多线程垃圾收集器,也叫吞吐量优先收集器。与ParNew的区别在于存在自适应调整策略,动态调整最合适的停顿时间
-
Parallel Old:Parallel Scavenge的老年代版本,使用多线程的标记整理,可触发新生代的ParallelScavenge收集器
-
CMS:并发标记清除,是一种获取最短的回收停顿时间为目标的收集器,并发收集停顿低,与用户线程一起执行
对应参数:-XX:+UseConcMarkSweepGC,开启后会使用:ParNewGC(New区)+ CMS(Old区)+ SerialOld的收集器组合,SerialOld作为CMS出错的后备收集器。CMS的过程:
- 初始标记Initial Mark(STW)
- 并发标记Concurrent Mark(并发)
- 重新标记Remark(STW)
- 并发清除Concurrent Sweep(并发)
优缺点:并发收集停顿低;并发执行对CPU资源压力大,采用的标记清除算法会导致大量碎片
-
SerialOld:Serial收集器的老年代版本,使用标记整理,作为CMS的后备收集器。对应参数:-XX:+UseSerialOldGC(过时)
GC日志信息的参数说明:
- DefNew:Default New Generation
- Tenured:Old
- ParNew:Parallel New Generation
- PSYoungGen:Parallel Scavenge
- ParOldGen:Parallel Old Generation
JVM中的Server和Client模式是什么?
32位OS,无论硬件如何都默认使用Client的JVM模式;64位OS,默认使用Server的JVM模式
如何选择7大垃圾回收器(从吞吐量,延迟,内存中选择两个设计)
- 单CPU或者小内存,单机程序使用-XX:+UseSerialGC
- 多CPU,需要大吞吐量,使用-XX:+UseParallelGC或者-XX:+UseParallelOldGC
- 多CPU,追求低停顿时间,快速响应,使用-XX:+UseConcMarkSweepGC或者-XX:+UseParNewGC
参数 |
新生代收集器你 |
新生代算法 |
老年代收集器 |
老年代算法 |
-XX:+UseSerialGC |
SerialGC |
复制 |
SerialOldGC |
标记整理 |
-XX:+UseParNewGC |
ParNew |
复制 |
SerialOldGC |
标记整理 |
-XX:+UseParallelGC -XX:+UseParallelOldGC |
Parallel [Scavenge] |
复制 |
Parallel Old |
标记整理 |
-XX:+UseConcMarkSweepGC |
ParNew |
复制 |
CMS+SerialOld |
标记清除 |
-XX:+UseG1GC |
G1整体采用标记整理 |
局部是通过复制,不产生内存碎片 |
|
|
7. G1垃圾回收器
- 以前收集器的特点:
- 年轻代和老年代都是各自独立且连续的内存块
- 年轻代Eden+From+To进行复制算法
- 老年代收集必须扫描整个区域
- 尽可能少且快速的执行GC为设计原则
- G1:Garbage First 面向服务端应用的收集器
G1的STW可控,在停顿时间STW上添加了预测机制,可以指定期望停顿时间。解决内存碎片问题,又保留了CMS垃圾收集器低暂停时间的优点。对于Java heap的改变为:不再区分老年代和新生代,宏观上将整个内存划分成多个独立的区域
- 回收步骤:
- 参数设置:
- -XX:+UseG1GC
- -XX:MaxGCPauseMills = 100 最大停顿时间为100ms
- -Xmx32g 设置最大内存
- 相比CMS的优势: