1.对象被判定成垃圾的标准
没有被其他对象引用
2.判断对象是否为垃圾的算法
(1)引用计数法
优点and缺点
(2)可达性分析算法
根节点:类加载器、Thread、虚拟机栈的本地变量表、static 成员、常量引用、本地方法栈的变量
垃圾回收回收算法
1.标记-清除(Mark and Sweep)
缺点是内存空间碎片化太严重
2.复制算法(Copying)
(1)复制算法介绍
(2)复制算法优势
3.标记-整理算法(Compacting)
(1)标记整理算法介绍
(2)标记整理算法优点
4.分代收集算法
年轻代用的是复制算法
老年代 用的是标记清除或者标记整理算法
(2)年轻代内存空间
如果Eden区不能保存一个较大的对象,则会保存到老年代中,默认十五岁之后会转移到老年代中
-XX:PretenureSizeThreshold 对象分配:对象优先在 Eden 区分配、大对象直接进入Old 区
-XX:MaxTenuringThreshold 长期存活对象进入 Old 区
-XX:+PrintTenuringDistribution如果希望跟踪每次新生代GC后,survivor区中对象的年龄分布,可在启动参数上增加。
-XX:TargetSurvivorRatio
设定survivor区的目标使用率。默认50,即survivor区对象目标使用率为50%。
为什么要动态的计算tenuring threshold的值呢?假设有很多年龄还未达到TenuringThreshold的对象依旧停留在survivor区,这样不利于新对象从eden晋升到survivor。因此设置survivor区的目标使用率,当使用率达到时重新调整TenuringThreshold值,让对象尽早的去old区。
(1)GC分类
从年轻代空间(包括 Eden 和 Survivor 区域)回收内存被称为 Minor GC
Full GC 是清理整个堆空间—包括年轻代和老年代。
Major GC 是清理老年代
JVM中的堆,一般分为三大部分:新生代、老年代、永久代:
永久代
指内存的永久保存区域,主要存放Class和Meta(元数据)的信息,Class在被加载的时候被放入永久区域. 它和和存放实例的区域不同,GC不会在主程序运行期对永久区域进行清理。所以这也导致了永久代的区域会随着加载的Class的增多而胀满,最终抛出OOM异常。
在Java8中,永久代已经被移除,被一个称为“元数据区”(元空间)的区域所取代。
(3)对象如何晋升到老年代
(4)设置参数
(5)触发full GC的方法
5.stop-theWorld
6.Safepoint
JVM有两种运行模式Server与Client。两种模式的区别在于,Client模式启动速度较快,Server模式启动较慢;但是启动进入稳定期长期运行之后Server模式的程序运行速度比Client要快很多。这是因为Server模式启动的JVM采用的是重量级的虚拟机,对程序采用了更多的优化;而Client模式启动的JVM采用的是轻量级的虚拟机。所以Server启动慢,但稳定后速度比Client远远要快。
1. 当前是Client or Server?
使用Java -version命令就能显示出当前虚拟机处于哪种模式。
Client:
如下图所示,可以看到HotSpot虚拟机采用Client模式启动的。
垃圾回收器分类
串行收集器
单线程收集器
Serial::Serial, Serial Old
并行收集器
吞吐量优先 是说垃圾收集线程之间并行工作 用户线程处在等待状态 适合交互性比较弱的场景
吞吐量:花在垃圾收集的时间和花在应用时间的占比 -XX:GCTimeRatio=<n>,垃圾收集时间占1/(1+n)
Parallel(吞吐量优先, Server 模式默认收集器):Parallel Scavenge, Parallel Old
并发收集器
并发收集器 停顿时间优先 垃圾收集线程和用户线程一起执行 适合对相应时间有要求的场景
停顿时间:垃圾收集器做垃圾回收中断应用执行的时间 -XX:MaxGCPauseMillis
Concurent(停顿时间优先):CMS , G1
如何选择垃圾收集器?
1. 优先调整堆的大小让服务器自己来选择
2. 如果内存小于100M,使用串行收集器
3。 如果是单核,并且没有停顿时间的要求,选择串行或者 JVM 自己选
4. 如果允许停顿时间超过1秒,选择并行或 JVM 自己选
5. 如果响应时间最重要,并且不能超过1秒,使用并发收集器
垃圾回收之新生代垃圾收集器
2.Serial收集器(复制算法-单线程-Client模式)
2.ParNew收集器(复制算法-多线程-Client模式)
3.Parallel Scavenge收集器(复制算法-多线程-Server模式下)
垃圾回收之老年代垃圾收集器
1.Serial Old收集器(标记整理算法-单线程-Client模式下)
2.Paraller Old收集器(标记整理算法-多线程-)
3.CMS收集器(标记清除算法)
4.G1收集器(新生代,老年代都可以用)
G1的几个概念
Region
SATB:Snapshot-At-The-Beginning,它是通过 Root Tracing 得到的,GC 开始时候存活对象的快照。
RSet:记录了其他 Region中的对象引用本 Region 中对象的关系,属于 points-into 结构
YoungGC
新对象进入 Eden 区
存活对象拷贝到Survivor 区
存活时间达到年龄阈值时,对象晋升到 Old 区
MixedGC
不是 FullGC,回收所有的 Young和所有的 Old
global concurrent marking
1. Initial marking phase: 标记 GC Root, STW
2. Root region scanning phase:标记存活 Region
3. Concurrent marking phase:标记存活的对象
4. Remark phase:重新标记,STW
5. Cleanup phase:部分 STW
MixedGC时机
InitiatingHeapOccupancyPercent
G1HeapWastePercent
G1MixedGCLiveThresholdPercent
G1MixedGCCountTarget
G1OldGCSetRegionThresholdPercent
-XX:+UseG1GC 开启 G1
-XX:G1HeapRegionSize=n, Region 的大小,1-32M,最多2048个
-XX:MaxGCPauseMillis=200 最大停顿时间
-XX:G1NewSizePercent、-XX:G1MaxNewSizePercent
-XX:G1ReservePercent=10 保留防止 to space溢出
-XX:ParallelGCThreads=n SWT线程数
-XX:ConcGCThreads=n 并发线程数=1/4*并行
最佳实践
年轻代大小:避免使用-Xmn, -XX:NewRatio 等显式 Young 区大小,会覆盖暂停时间目标
暂停时间目标:暂停时间不要太严苛,其吞吐量目标是90%的应用程序时间和10%的垃圾回收时间,太严苛会直接影响到吞吐量
需要切换到 G1的情况:
1. 50%以上的堆被存活对象占用
2. 对象分配和晋升的速度变化非常大
3. 垃圾回收时间特别长,超过了1秒
可视化 GC 日志分析工具
打印日志相关参数:
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log -XX:+PrintHeapAtGC -XX:+PrintTenuringDistribution
例(默认为 ParallelGC, 其它的添加-XX:+UseConcMarkSweepGC或-XX:+UseG1GC即可):
JAVA_OPTS="$JAVA_OPTS -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -Xloggc:$CATALINA_HOME/logs/gc.log"
在线工具:http://gceasy.io/
访问 GCeasy 官网导入日志即可获取可视化分析及优先建议
mvn clean package -Dmaven.test.skip 生成 jar包,双击执行,导入日志即可进入图形化分析页面