JVM
2021-08-01 21:12:54 0 举报
AI智能生成
JVM完整知识思维导图
作者其他创作
大纲/内容
思考题汇总
ParNew+CMS下的GC,如何只保证YGC,JVM参数如何设置(22)
为什么老年代Full GC要比新生代的Minor GC慢很多倍(24)
什么时候会触发Young GC
当新生代的堆内存中Eden区已经无法再容纳新的对象进行存储的时候,就会触发Young GC
什么时候会触发Full GC
永久代的可用内存空间不足
老年代的内存已经到达了92%
-XX:CMSInitiatingOccupanyFaction
空间担保机制
如果老年代的可用的连续内存空间小于历代Young GC后存活对象进入老年代的平均大小
新生代Young GC后,存活对象进入老年代,没有足够的连续内存空间存放存活对象
开发流程中是怎么做好压测环节(52)
JVM优化规范流程(55)
线上生产环境的JVM监控方案(55)
为什么有时候Young GC处理时间会比Full GC的处理时间长(54)
如果每次Full GC都是由于Young GC触发的,此时Young GC需要等待Full GC处理完毕才能将Young GC处理完成
Young GC过后有很多的存活对象要放入老年代,老年代内存不够触发了Full GC
必须等待Full GC执行完毕,Young GC才能把存活对象放入老年代,Young GC才能算处理结束
如何优化CMS垃圾回收下每次Full GC的性能
在CMS的初始标记阶段开启多线程并发执行
-XX:+CMSParallelInitialMarkEnabled
在CMS重新标记阶段之前先执行一次Young GC
-XX:+CMSScavengeBeforeRemark
Java反射机制带来的永久代的频繁Full GC(59)
使用反射机制频繁加载类的原因
使用反射可能会让JVM动态去生成一些类放入到Metaspace区域
xxx.getClass()
xxx.class
使用反射创建出来的类都是SoftReference软引用类型
正常情况下不会被回收,除非是内存空间紧张才会回收软引用对象
是否要回收的公式判断
clock - timestamp <= freespace * SoftRefLRUPolicyMSPerMB
colock - timestamp
代表一个软引用对象多久没被访问过
freespace
代表JVM的空闲内存空间
SoftRefLRUPolicyMSPerMB
代表美1MB空闲内存空间可以允许软引用对象存活多久
-XX:SoftRefLRUPolicyMSPerMB
设置为0,会导致软引用对象在创建后,在使用完毕之后就会立即变为垃圾对象
会导致每一次执行相同的反射代码,都会重复创建一个类对象
最终导致永久代内存被重复类占用,导致频繁GC
频繁Full GC的场景(64)
Java反射机制和-XX:SoftRefLRUPolicyMSPerMB设置不合理导致永久代内存频繁占满
将-XX:SoftRefLRUPolicyMSPerMB数值调大一些
使用jstat发现使用内存不多,则可能是永久代使用内存不足导致GC频繁的原因
代码内部使用System.gc()的操作
使用-XX:+DisableExplicitGC禁止在代码中执行GC
使用jstat发现使用内存不多,则可能是调用System.gc()导致GC频繁的原因
新生代内存分配不合理,导致存活对象频繁进入老年代,进而引发频繁的GC
可通过jsat命令分析存活对象是否频繁进入老年代
存在内存泄漏问题
内存中驻留了大量的对象塞满了老年代,导致稍有一些存活对象进入老年代都会引发Full GC
分析内存泄漏原因,可以使用MAT工具(65)
MAT
Memory Analysis Tool
步骤
jmap导出一份内存快照
jmap-dump:live,format=b,file=dump.hprof PID
MAT工具内部选择 open a Heap Dump(打开内存快照)
选择Leak Suspects(泄漏嫌疑)
进行内存泄漏的分析
系统一次性加载过多数据到内存,搞出很多大对象
大对象频繁进入老年代,必然会频繁触发Full GC
通过jstat、jmap命令,还有MAT工具可以分析出是否为该原因
CPU使用率飙升的主要原因
系统中创建了大量线程,线程同时并发运行,而且工作负载很重
导致CPU负载过高
如果不是频繁Full GC,必然就是多线程并发执行导致CPU负载变高
JVM在频繁的执行Full GC,极大耗费CPU资源
可通过jstat命令去观测是否频繁产生Full GC
JVM性能优化到底怎么做
第一步,在系统开发完毕,合理估算核心的业务流程使用内存情况
估算每秒的创建多少对象,每个对象的大小,每秒使用多少内存空间
估算出新生代的Eden区大概多少时间会被占满
估算出多长时间会发生一次Young GC
估算会有多少对象存活
估算会有多少对象进入老年代
估算老年代对象的增长速率是多少
估算多长时间会发生一次Full GC
第二步,针对合理的估算,合理分配新生代(Eden区,Survivor区)和老年代的内存空间
原则是尽可能让每次Young GC后存活的对象远小于Survivor区,避免存活对象频繁进入老年代
参照启动命令模板编写JVM启动命令参数
第三步,根据实际压测情况,调整JVM启动命令参数
通过模拟压测,并使用jstat命令进行观察
Young GC的频率
Full GC的频率
Young GC的耗时
Full GC的耗时
第四步,使用工具进行线上系统的监控和报警
一旦发现频繁Full GC就对JVM启动命令进行合理优化
设置JVM启动命令模板的原则
合理分配新生代的内存空间大小
Eden区和Survivor区的比例
避免对象频繁进入老年代
合理分配老年代的内存空间大小
根据堆内存总大小和新生代内存大小计算得出
新生代和老年代的垃圾回收器的指定
CMS性能优化参数
内存碎片整理,存活对象压缩
-XX:CompactAtFullCollection
-XX:CMSFullGCsBeforeCompaction
初始标记阶段使用多线程并发标记存活对象
-XX:+CMSParallelInitialMarkEnabled
重新标记阶段前,先执行一次Young GC
-XX:+CMSScavengeBeforeRemark
禁用System.gc()
打印GC日志,导出日志
MAT工具定位OOM步骤(89)
1. 检查内存中到底什么对象占用内存过多
Histogram(直方图)界面
2. 深入查看占用内存过多的对象被谁引用,哪个线程引用
Dominator_Tree界面
3.定位创建过多对象的代码行
Thread_Overview界面
结合自己的公司系统的业务进行分析,如果遇到OOM应该怎么处理
首先根据服务日志查看具体是什么类型的内存溢出
方法区内存溢出
栈内存溢出
堆内存溢出
生成OOM异常快照dump.hprof
使用MAT工具打开OOM异常快照
从MAT工具去定位OOM原因
定位具体什么对象占用内存多
定位对象是被哪个线程引用,哪些变量引用
定位创建过多对象的位置
最后优化有问题的代码块或者调整相关配置
分析源码,调整配置信息
业务代码问题则进行修复
生产环境的JVM参数怎么设置,为什么这么设置
生产环境的JVM优化经验
JVM日志详解
儒猿43~47讲
JVM检查命令(监控)
jps
查看java进程的PID
jstat
jstat -gc PID(查看指定java进程的内存和GC情况)
S0C
From Survivor区内存大小
S0U
From Survivor区当前使用的内存大小
S1C
To Survivor区内存大小
S1U
To Survivor区当前使用的内存大小
EC
Eden区的内存大小
EU
Eden区当前使用的内存大小
OC
老年代的内存大小
OU
老年代当前使用的内存大小
MC
方法区(永久代、元数据区)的大小
MU
方法区(永久代、元数据区)当前使用的内存大小
YGC
系统运行目前为止的Young GC次数
YGCT
所有Young GC的总耗时
FCG
系统运行目前为止的FullGC的次数
FGCT
所有Full GC的总耗时
GCT
所有GC的总耗时
jstat -gccapacity PID
堆内存分析
jstat -gcnew PID
年轻代GC分析
TT:对象在年轻代存活的年龄
MTT:对象在年轻代存活的最大年龄
jstat -gcnewcapacity PID
年轻代内存分析
jstat- gcold PID
老年代GC分析
jstat -gcoldcapacity PID
老年代内存分析
jstat -gcmetacapacity PID
元数据区内存分析
jstat -gc PID 1000 10
每隔1秒钟更新一行jstat统计信息,一共执行10次jstat统计
统计新生代的对象的增长速率
Young GC的触发频率
Young GC的耗时
每次Young GC后会有多少对象存活
每次Young GC过后有多少对象进入老年代
老年代的对象的增长速率
Full GC的触发频率
Full GC的耗时
jmap
jmap -heap PID
Eden区总容量
Eden区已使用容量
Eden区剩余空间容量
两个Survivor区总容量
两个Survivor区已使用容量
两个Survivor区剩余的空间容量
老年代的总容量
老年代已经使用的容量
老年代剩余的空间容量
jmap -histo PID
打印对象占用的内存空间大小,按照从大到小排列
jmap -dump:live,format=b,file=dump.hprof PID
生成堆内存转储快照,生成dump.hrpof的二进制文件
jhat
jhat dump.hprof -port 7000
在浏览器中分析堆内存转储的快照文件dump.hprof
发生OOM的原因
方法区内存溢出
CGLIB动态代理生成新类
造成方法区内存占满,频繁GC,无法回收Class对象,最终造成内存溢出
栈内存内存溢出
递归调用无限制导致栈内存溢出
堆内存内存溢出
系统承载高并发请求,请求量过大,大量对象存活
新的对象无法放入堆内存中,导致内存溢出
系统存在内存泄漏问题,导致很多存活对象一直驻留在堆内存中
没有及时取消存活对象的引用,在触发GC后还是没有多余的空间容纳新的对象导致内存溢出
JVM分代模型
年轻代
大部分的对象存活时间都很短
方法局部变量引用的对象
方法栈帧出栈,引用结束,对象成为垃圾对象
老年代
少数的对象是长期存活的
被静态变量长期引用的对象
长期停留在Java堆内存中
超过一定年龄段进入老年代的对象
永久代
方法区
存放类的信息:类静态变量,类
触发垃圾回收的三个条件
该类的所有实例对象都已经从Java堆内存里被回收
加载这个类的ClassLoader已经被回收
对该类的Class对象没有任何引用
为什么分年轻代和老年代
大部分的对象的存活时间很短,垃圾回收要迅速,标记存活的对象,然后复制到年轻代的Survivor区,最后全部清除原有的内存中的所有对象
少部分的对象存活时间很长,垃圾回收没办法处理迅速,需要标记需要垃圾回收的对象,通过标记整理对垃圾对象进行清除,然后整理内存区域
对象在JVM中的分配和流转
大部分正常的对象,都是优先在新生代中分配内存的
静态变量引用的对象,一开始也是分配到新生代的
等经历了15次(默认)垃圾回收后,对象如果还存活,才会进入老年代
什么时候触发新生代的垃圾回收
Minor/Young GC
短暂存活的对象在没有被引用的时候,会在内存中标记为垃圾对象,但不会立马被回收
当新生代的使用的内存占满之后,无法再容纳新的对象存储,此时就会触发一个 Minor GC
长期存活的对象会躲过多次的新生代垃圾回收
当经历了15次垃圾回收后,对象仍然存活,就会进入老年代
什么时候触发老年代的垃圾回收
老年代的内存区域占满,无法再容纳长期存活的对象进入老年代
Full GC
会同时清理新生代和老年代的垃圾对象
JVM核心参数配置
核心参数
-Xmx
Java堆内存的最大大小
-Xms
Java堆内存的大小
-Xmn
Java堆内存中的新生代内存大小
堆内存大小-新生代内存大小=老年代内存大小
-XX:MaxPermSize
方法区/永久代的内存最大大小
-XX:MaxMetaspaceSize(1.8)
-XX:PermSize
方法区/永久代的内存大小
-XX:MetaspaceSize(1.8)
-Xss
每个线程的栈内存大小
JVM垃圾回收算法
什么时候一个对象会成为垃圾对象
可达性分析算法
GC roots
静态变量
局部变量
当一个对象没有被局部变量或者静态变量直接引用或间接引用的话,这个对象就是垃圾对象
Java对象中不同的引用类型
强引用
一个变量引用一个对象
只要是强引用的类型,那垃圾回收的时候是绝对不会去回收这个对象
软引用
实例对象用【SoftReference】软引用类型包裹起来
正常情况下,垃圾回收不会回收软引用对象
只有在垃圾回收后,发现内存空间不够存放新的对象,此时才会将软引用对象给回收了,即使对象被变量引用
弱引用
实例对象用【WeakReference】弱引用类型包裹起来
只要发生垃圾回收,就会将弱引用的对象回收掉
虚引用
垃圾回收算法
复制算法
新生代使用的垃圾回收算法
先复制后清除
内存区域分配
Eden区(8)+ Survivor1区(1)+Survivor2区(1)
-XX:SurvivorRatio=8
为什么要设置为8:1:1的内存分配
每一次的垃圾回收,只有少量的对象存活,占比大概在10%左右
eden区80%内存+S1区10%内存用于存储新的对象,内存利用率更大
始终保持一块S区是空着的,新对象会分配在Eden区和另一块被使用的S区
只有10%的内存被闲置,90%的内存能被利用
为什么使用复制算法
新生代的对象大部分都是存活时间较短的对象,通常在方法执行完毕之后,就没有变量引用成为垃圾对象
少量存活的对象,就从内存中标记出来,然后统一复制到Survivor区
最终可以把原有的对象全部从Eden区和另一个Survivor区中一并清除
其他回收算法问题
可能在清除大量垃圾对象时,会产生大量的内存碎片
造成内存浪费
清除效率低,因为要标记大部分的垃圾对象
标记整理算法
老年代使用的垃圾回收算法
先整理后清除
垃圾回收步骤
首先标记出老年代中存活的对象
然后统一将存活的对象进行移动,让存活对象紧靠在一起,避免垃圾回收后产生过多的内存碎片
最后再一次性把垃圾对象给清除掉
老年代的垃圾回收算法的速度是比新生代的垃圾回收算法速度慢10倍,应当尽量避免频繁触发Full GC垃圾回收
新生代的对象什么情况下会进入老年代
新生代里的对象,经历15次Young GC后仍然存活,则进入老年代
-XX:MaxTenuringThreshold
动态年龄判断
一批对象的总大小大于Survivor区内存大小的一半,则后续大于这批对象年龄的对象,直接进入老年代
年龄1+年龄2+...+年龄n的多个对象总内存超过Survivor区的50%,则会把年龄n以上的对象直接放入老年代
大对象直接进入老年代
-XX: PretenureSizeThreshold
大对象的阈值,超过这个字节数,则直接进入老年代
Young GC后存活对象太多无法放入Survivor区
老年代的空间担保规则
在每一次执行Young GC之前,JVM会先检查老年代的可用内存空间,是否大于新生代的所有对象的总大小
大于
可以直接Young GC
小于
-XX:HandlePromotionFailure
设置
继续判断
没有设置
Full GC + Minor GC
设置了 -XX:HandlePromotionFailure,则会进一步判断,老年代的可用内存大小,是否大于历代Young GC后进入老年代对象的平均大小
大于
进行Minor GC
Minor GC后的存活对象小于Survivor区大小,存活对象直接进入Survivor区
Minor GC后的存活对象大于Survivor区大小,小于老年代可用内存大小,直接进入老年代
Minor GC后的存活对象大于Survivor区大小,大于老年代可用内存大小,触发Full GC
小于
直接进行Full GC
Full GC后,仍然没有足够的内存存放Minor GC后的存活对象,就会OOM
触发老年代的垃圾回收时机
在Minor GC之前,发现可能Minor GC后要进入老年代的存活对象大于老年代的可用内存大小
提前预测,进行Full GC
在Minor GC之后,发现剩余的存活对象大于老年代的可用内存大小
最终结果,进行Full GC
老年代的内存空间占用达到-XX:CMSInitiatingOccupanyFaction指定的比例,就会自动执行老年代的GC
GC流程图例
思考题
到底什么时候会尝试触发Minor GC
新生代的内存空间无法再容纳新的对象存储的时候,就会尝试触发Minor GC
新生代现有存活对象小于老年代剩余内存
新生代现有存活对象大于老年代剩余内存,查看是否设置空间担保并担保成功
触发Minor GC之前会如何检查老年代大小,设计的步骤和条件
新生代对象总大小与老年代可用内存大小的判断
空间担保失败下,新生代GC后的平均大小与老年代的可用内存大小的判断
什么时候在Minor GC之前就会提前触发一次Full GC
未设置空间担保失败(-XX:HandlePromotionFailure), 新生代对象的总大小大于老年代可用内存大小
设置了空间担保失败,历代Minor GC后的存活对象的平均大小大于老年代的可用内存大小
Full GC的算法是什么
标记整理算法
先标记存活对象,整理到一块,减少内存碎片,然后统一清除垃圾对象
Minor GC过后会有几种情况
存活对象存储在Survivor区
存活对象存储在老年代
老年代先发生Full GC,然后存活对象存储在老年代
老年代先发生Full GC,可用内存无法存储存活对象,OOM
那些情况下Minor GC后的对象会进入老年代
Survivor区存放不下
躲过15次的Minor GC
动态年龄判断下,超过Survior区大小50%后,大于最大年龄的对象
Stop the World
这个过程中,需要jvm专门负责对垃圾对象进行回收,此时系统无法创建新的存活对象,会有短暂的停止运行
JVM垃圾回收器
简介
Serial和Serial Old垃圾回收器
单线程运行,分别用来回收新生代和老年代的垃圾对象
ParNew和CMS垃圾回收器
多线程并发运行,ParNew用在新生代的垃圾回收器,CMS用在老年代的垃圾回收器
G1垃圾回收器
统一收集新生代和老年代
ParNew垃圾回收器
命令行指定
-XX:UseParNewGC
应用在新生代的垃圾回收器
在要执行Minor GC时,会把系统程序的工作线程停掉,禁止程序继续运行创建新的对象
然后用自己的多个垃圾回收线程去进行垃圾对象的回收
复制算法到一个Survivor区,然后统一清除Eden区和另一个Survivor区里的对象
默认垃圾回收的线程数与CPU的核数保持一致
-XX:ParallelGCThreads
调整ParNew的垃圾回收线程数量
对比Serial垃圾回收器
Serial垃圾回收器是单线程的,适用于指定为-client的客户端程序
多线程反而会带来线程上下文切换的开销
ParNew垃圾回收器是多线程的,适用于指定为-server的服务端程序
CMS垃圾回收器
使用的是标记清理算法
不是标记整理算法
标记清理,只会将标记为垃圾的对象进行清除,并不会将存活的对象整理放在一块,会产生内存碎片
垃圾对象
没有被GC Roots引用的对象
命令行指定
-XX:UseConcMarkSweepGC
工作模式
垃圾回收线程和系统工作线程尽量同时执行的模式
垃圾回收过程的4个阶段
初始标记
停止系统线程创建对象的过程,进入 Stop the World
垃圾回收线程在这一阶段,会标记出当前在老年代堆内存中的没有被GC Roots引用的对象
并发标记
系统线程可以随意创建各种新对象,继续运行
运行期间,可以创建新的存活对象,也可能部分存活对象失去引用,成为垃圾对象
垃圾回收线程会尽可能对已有的对象进行GC Roots追踪
标记出新的垃圾对象
过程耗时,但是因为垃圾线程和系统运行线程并发运行,所以不会影响性能
重新标记
第二阶段是没办法完全标出所有的垃圾对象,所以会有重新标记的阶段
停止系统线程创建对象的过程,进入Stop the World
重新标记在并发标记阶段下创建出来后失去引用的垃圾对象
并发清理
垃圾回收线程和系统线程并发运行
系统线程在这一阶段中可以继续创建新的存活对象
垃圾回收线程负责对所有标记为垃圾对象的对象进行清理
过程耗时,但是因为垃圾线程和系统运行线程并发运行,所以不会影响性能
并发模式下的影响
并发回收垃圾导致的CPU紧张
在并发标记和并发清理两个耗时的阶段,系统线程和垃圾回收线程会并发运行
耗费CPU资源
默认启动的垃圾回收线程数量
(CPU核数 + 3) / 4
Concurrent Mode Failure问题
并发清理阶段,垃圾回收线程只会回收标记好的垃圾对象
但是在这一阶段,系统线程会一直运行,可能会有新的对象进入老年代后变为了垃圾对象
这种垃圾对象称为:浮动垃圾
浮动垃圾在这次的GC不会被清理,需要等待下一次的GC
老年代需要预留一部分空间
负责保证在CMS垃圾回收期间,还有一定的内存空间让一些对象进入老年代
CMS垃圾回收触发时机
当老年代内存占用达一定比例,就自动执行GC
-XX:CMSInitiatingOccupancyFaction
设置老年代占用多少比例时触发CMS垃圾回收
JDK1.6默认92%
预留8%的空间交给并发清理阶段,系统将一批新的对象放入老年代中
在并发清理阶段,新的存活对象要放入老年代的大小大于老年代的可用内存大小
发生 Concurrent Mode Failure问题
并发垃圾回收失败
使用Serial Old垃圾回收器进行单线程的垃圾回收
强行把系统程序 Stop the World
将所有垃圾对象都回收后,才会恢复系统线程
内存碎片问题
CMS是标记清理算法
标记出所有垃圾对象,然后一次性回收,会产生大量内存碎片
内存碎片太多,导致后续对象进入老年代没有可用的连续的内存空间,频繁触发Full GC
解决
-XX:CMSCompactAtFullCollection
在执行完Full GC后,进行内存碎片整理
-XX:CMSFullGCsBeforeCompaction
在Full GC后执行"Stop the World",停止工作线程,进行碎片整理
将存活对象挪到一起,空出大片连续内存空间
执行多少次Full GC之后就执行一次内存碎片整理工作
默认是0,每次Full GC之后就进行一次内存碎片整理
为什么老年代垃圾回收慢
两个阶段执行慢
并发标记
需要追踪所有存活对象,老年代存活对象多,这个过程很慢
并发清理
需要找到所有零散在个地方的垃圾对象,这个过程很慢
内存碎片整理
进行内存碎片整理,需要进入Stop the World
出现 Concurrent Mode Failure
切换为单线程的"Serial Old"垃圾回收器,然后Stop the World进行垃圾回收
G1垃圾回收器
ParNew+CMS组合的痛点
可能会存在长时间的Stop the World,造成系统线程的长时间等待不可用
G1垃圾回收器的特点
可以同时回收新生代和老年代的里的垃圾对象
新生代和老年代在回收器中只是逻辑上的概念
将Java堆内存拆分为大小相等的Regin
新生代占用一部分Regin
老年代占用一部分Regin
大对象占用一部分Regin
Regin会被动态分配
可以指定垃圾回收的系统停顿(Stop the World)的时间,最长不会超过1分钟
追踪每个Regin里的回收价值
最少的时间,回收最多的垃圾对象
G1可以让开发者设定垃圾回收对系统的影响
内存拆分为等量的大小的Regin
追踪每个R而该in中可以回收的对象大小和预估时间
最后再垃圾回收时,精良把垃圾回收对系统的影响控制在指定的时间范围
同时在有限的时间内尽量回收尽可能多的垃圾对象
同一个Regin,可能属于新生代,也可能属于老年代
新生代和老年代的内存区域会根据分配的Regin的数量动态变化
G1内存大小的设定
-XX:UseG1GC
指定垃圾回收器为G1垃圾回收器
通过-Xmx和-Xms设置堆内存的大小,则每一个Regin的大小就为 总堆内存/2048
Regin大小必须为2的倍数
手动指定Regin大小
-XX:G1HeapReginSize
G1新生代
指定新生代初始内存大小占比
-XX:G1NewSizePercent
指定新胜达最大内存大小占比
-XX:G1MaxNewSizePercent
最大占比不超过60%
同样有Eden区和Survivor区的逻辑概念
-XX:SurivorRatio
如何垃圾回收
在新生代对应的Regin中存放对象,直到新生代占据堆内存大小的最大比例60%
对应的Eden区下的所有Regin已经占满了对象,此时就会触发新生代的垃圾回收
Stop the World,复制算法,将存活对象放入S1对应的Regin,然后回收Eden对应的Regin中的垃圾对象
垃圾回收过程可以限制执行GC的时间
-XX:MaxGCPauseMills
默认200ms
G1老年代
什么时候对象进入老年代
避开多次新生代的回收,次数:-XX:MaxTenuingThreshold
新生代存活对象太多,Survior区放不下
动态年龄判断下,S区存活对象占用空间超过50%,下一次垃圾回收,大于最大年龄的存活对象进入老年代
大对象的Regin区域
大对象的回收,会在新生代、老年代在回收的时候,一起被回收
混合回收
新生代和老年代、大对象的混合回收
-XX:InitiatingHeapOccupanyPercent
默认45%
当老年代占据堆内存的45%的Regin,就会触发新生代和老年代一起回收的混合回收
混合回收下,年轻代和老年代都是基于复制算法进行回收
将Regin的存活对象拷贝到空闲的Regin中去
回收过程
初始标记
进入Stop the Wrold,标记GC Roots直接引用的对象
并发标记
允许系统运行,同时进行GC Roots追踪所有存活对象
包含间接引用的存活对象
最终标记
进入Stop the World,最终标记出那些是存活对象,哪些是垃圾对象
混合回收
计算老年代中每个Regin的存活对象数量、占比;执行垃圾回收的预期性能和效率
进入Stop the World,选择部分Regin进行回收,保证系统的停顿时间控制在指定的时间范围内
在指定时间范围内,尽可能多地回收垃圾对象
可以指定多次的混合回收
停止系统运行,先混合回收一部分Regin,然后恢复系统运行。接着再进行系统运行,再回收一部分Regin,反复循环
默认循环次数8次
-XX:G1MixedGCCountTarget
通过该命令指定混合回收次数
将回收造成的长时间系统停顿,分摊到不同次的回收过程中
避免一次性造成的长时间的系统停顿
-XX:G1HeapWastePercent
默认5%
混合回收下,对Regin回收都是基于复制算法进行
先把要回收的Regin里的存活对象放入其他Regin,然后将这个Regin中的对象全部清除
此时,空闲出来的Regin数量达到总数量的5%,就会立即停止混合回收
-XX:G1MixedGCLiveThresholdPercent
默认值85%
一个Regin里的存活对象低于85%才会去回收这个Regin
回收失败的Full GC
将各个Regin的存活对象拷贝到空闲的Regin中去
空闲的Regin对象无法承载复制的存活对象
混合回收失败
混合回收失败,就会切换为停止系统程序,然后用单线程的方式进行标记清理和压缩整理
过程耗时长
JVM启动命令整合
jvm堆内存最大大小
-Xmx
-XX:MaxHeapSize(1.8)
jvm堆内存初始大小
-Xms
-XX:InitialHeapSize(1.8)
新生代内存大小
-Xmn
-XX:NewSize(1.8)
-XX:MaxNewSize(1.8)
永久代最大大小
-XX:MaxPermSize
永久代初始大小
-XX:PermSize
元数据区最大大小(1.8)
-XX:MaxMetaspaceSize
元数据区初始大小(1.8)
-XX:MetaspaceSize
设置新生代Eden与Survivor比例
-XX:SurvivorRatio
设置大对象的阈值
-XX:PretenureSizeThread
设置躲过多少次Young GC的存活对象进入老年代
-XX:MaxTenuringThreshold
15
设置开启空间担保机制
-XX:HandlePromotionFailure
设置ParNew垃圾回收器
-XX:+UseParNewGC
设置新生代使用ParNew垃圾回收器进行垃圾回收的线程数
-XX:ParallelGCThread
CMS垃圾回收器相关
设置CMS垃圾回收器
-XX:+UseConcMarkSweepGC
设置老年代存活对象占用多少空间后就会触发Full GC
-XX:CMSInitiatingOccupanyFaction
92%
设置使用CMS垃圾回收器,是否进行内存碎片整理
-XX:+CMSCompactAtFullCollection
设置使用CMS垃圾回收器,经过多少次垃圾回收后会进行内存碎片整理
-XX:CMSFullGCsBeforeCompaction
在CMS垃圾回收器的初始标记阶段开启多线程并发执行
-XX:+CMSParallelInitialMarkEnabled
在CMS垃圾回收器的重新标记阶段之前,先执行一次Young GC
-XX:+CMSScavengeBeforeRemark
G1垃圾回收器相关
设置G1垃圾回收器
-XX:UseG1GC
设置G1内存中每个Region的内存大小
-XX:G1HeapReginSize
指定新生代的内存大小占比
-XX:G1NewSizePercent
指定新生代最大内存大小占比
-XX:G1MaxNewSizePercent
设置每次GC的系统暂停的最大时间
-XX:MaxGCPauseMills
200ms
老年代占用达到指定比例后,触发混合回收
-XX:InitiatingHeapOccupanyPercent
45%
指定混合回收的次数
-XX:G1MaxedGCCountTarget
指定空闲出Regin的数量达到总Regin数量的比例后,就会停止使用混合回收
-XX:G1HeapWastePercent
5%
一个Regin里的存活对象低于多少才会去回收这个Regin
-XX:G1MixedGCLiveThresholdPercent
85%
日志打印相关
打印详细的gc日志
-XX:+PrintGCDetails
打印每次GC发生的日期
-XX:PrintGCTimeStamps
将GC日志写入一个磁盘文件
-Xloggc:gc.log
永久代相关
追踪类加载的情况
-XX:TraceClassLoading
追踪类卸载的情况
-XX:TraceClassUnloading
每1MB软引用对象存活时间设置
-XX:+SoftRefLRUPolicyMSPerMB
禁止调用System.gc()进行GC操作
-XX:+DisableExplicitGC
在系统内存溢出的时候导出一份内存快照到指定的位置
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath
-XX:HeapDumpPath=./
0 条评论
下一页