JVM之垃圾回收机制全解(GC)文章底部有思维导图,较为清晰,可参考

导读:垃圾回收是Java体系中最重要的组成部分之一,其提供了一套全自动的内存管理方案,要想掌握这套管理方案,就必须了解垃圾回收器的工作原理。本文介绍了垃圾回收的概念,算法,垃圾回收器及我在工作中遇到的一些关于GC的优化实例。

先来简单了解下JVM:

-------------------------------------------------------------

一、heap内存划分

-------------------------------------------------------------

1.年轻代:分三个区。一个Eden区,两个Survivor区(from Survivor(s0)区和to Survivor(s1)区)。

大部分对象在Eden区中生成。当Eden区满时,还存活的对象将被复制到Survivor区(两个中的一个),当这个Survivor区满时,此区的存活对象将被复制到另外一个Survivor区,当这个Survivor去也满了的时候,从第一个Survivor区复制过来的并且此时还存活的对象,将被复制“年老区(Tenured)”。

2.年老代

在年轻代中经历了N次((ParNew默认15))垃圾回收后仍然存活的对象,就会被放到年老代中。年轻代放不下的大对象直接进入老年代。

tip1:对象动态年龄计算规则

虚拟机并不是永远地要求对象的年龄必须达到了MaxTenuringThreshold(默认15次)才能晋升老年代,如果在Survivor空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象就可以直接进入老年代,无须等到MaxTenuringThreshold中要求的年龄。

3.持久代

用于存放静态文件,如今Java类、方法等

JDK1.8中,永久代已经从java堆中移除,String直接存放在堆中,类的元数据存储在meta space中,meta space占用外部内存,不占用堆内存。

-------------------------------------------------------------

二、GC回收算法

-------------------------------------------------------------

2.1 标记清除算法:标记清除分为两个阶段,标记阶段(标记从根节点开始的所有可达对象,未标记即未被引用)和清除阶段。缺点:两个阶段效率都很低;回收后内存空间不连续,产生碎片多,易导致提前GC。

2.2 复制算法:内存等分两块,相互复制存活的对象后清洗垃圾 缺点:内存利用率低

2.3 标记压缩法:先标记,然后存活的向一段移动,清理存活端标记以外的内存。(老年代使用,无需需要第二块相同的内存) 优缺点:无内存碎片,但是耗时。

2.4 分代算法:复制算法(新生代使用) ,标记压缩法和标记清除法(老年代使用)。卡表(数据结构,一个比特位的集合),用来表示老年代对象是否持有新生代对象的引用,新生代无需再花时间确认对象是否被持有,可以加快新生代回收的速度。

2.5 分区算法:将整个堆空间划分为连续不同的小的空间,独立管理,独立回收。

垃圾回收基本思想在于如何判断对象的可触及性。根据标记清除算法,可以扫描出root节点未触及持有的对象,但一个无法触及持有的对象有可能在某个时间下使自己复活。

对象的可触及性的三种状态:

可触及的

可复活的(finalize()函数)

不可触及的(finalize()函数只能调用一次)

2.6 引用和可触及的强度分为4个级别

强引用:任何时候都不会被系统回收,亦可能会引起OOM。 例:StringBuffer str = new StringBuffer("juejin");

软引用:GC不一定回收,但堆空间不足时会被回收。OOM之前一定会回收,所以软引用不会引起OOM。 使用SoftReference创建的对象。

弱引用:发现即回收。使用WeakReference创建的对象。使用PhantomReference创建的对象。

虚引用:随时可回收。

-------------------------------------------------------------

三、分代垃圾回收

-------------------------------------------------------------

3.1 young代采用复制算法

3.2 old代使用标记清除或者标记清理

3.3 Tip:对象优先在Eden去分配,大的对象直接进入老年代,长期存活对象进入老年代。

-------------------------------------------------------------

四、垃圾回收器

-------------------------------------------------------------

tip:Stop The World(STW)1、为了让垃圾回收器可以正常切高效执行。2、保证了系统某个瞬间的一致性。3、有益于垃圾回收器更好地标记垃圾对象。

4.1 串行回收器

单线程GC,启动时会停止应用,适用于配置小的服务器(1C2G),基本已弃用

4.2 并行回收器PS(吞吐量优先)

JDK1.6~1.8默认使用。垃圾线程并行,启动时应用会等待。

PS的新生代回收器有两个。

(1)、ParNew回收器:多线程执行垃圾回收。PS的线程数量可以用-XX:ParallelGCThreads指定。当CPU<8时,ParallelGCThreads的值=CPU,CPU>8时,ParallelGCThreads的值=3+((5*CPU_count)/8)。适用于交互较弱的场景。JDK1.8以上已经被删除。

(2)、Parallel回收器:与ParNew一样是多线程独占式。但其特点是关注系统的吞吐量(吞吐量:花费在垃圾收集时间和花费在应用时间的占比)。

使用方法:-XX:+UseParallelGC(设置老年代-XX:+UseParallelOidGC)

4.3 并发回收器(响应时间优先)(并行GC前会额外触发新生代的GC)

与并行回收器不相同的是,并发收集器是非独占式,在进行垃圾回收的时候应用程序也可以运行。

主要有Concurrent Mask Sweep(CMS)和G1

JDK1.9默认使用G1。适用于对响应时间有要求的场景。响应时间:花费在应用时间和花费在垃圾收集时间的占比。

CMS(以获取最短回收停顿时间为目标的收集器,基于并发“标记清理”实现)

过程:1)初始标记(标记root对象) 2)并发标记 3)预清理(准备及控制停顿时间) 4)重新标记 5)并发清除 6)并发重置

优点:并发收集、低停顿。

缺点:

1)CMS对CPU资源敏感。在并发阶段,它虽然不会导致用户线程停顿,但是会因为占用了一部分线程而导致应用程序变慢,总吞吐量会降低。

2)CMS无法处理浮动垃圾,可能会出现“Concurrent Mode Failure(并发模式故障)”失败而导致Full GC产生。

3)CMS容易出现大量空间碎片。当空间碎片过多,将会给大对象分配带来很大的麻烦,往往会出现老年代还有很大空间剩余,但是无法找到足够大的连续空间来分配当前对象,不得不提前触发一次Full GC。

4)老年代垃圾回收过程中,如果出现资源不够用,则会强制进行老年代串行回收,应用暂停时间更长,影响更大。

G1(面向服务端应用的垃圾收集器)

1.7正式使用,且使用了全新的算法,看起来有取代CMS的趋势。

保留了分代的概念,但是从堆结构上看,分代内存并不是连续的。如图:

在并行性和并发性的基础上,可以同时兼顾年轻代和年老代,还可以进行空间整理,每次GC之后会自动进行碎片整理,减少碎片空间。最后还有可预见性,G1可以选取部分区域进行内存回收。

过程:1)初始标记(标记root对象)(eden区会被清空) 2)根区域扫描 3)并发标记 4)重新标记 5)独占清理 (计算各个区域存活对象和GC回收比例)6)并发清理

混合回收:在年轻代满时,触发年轻代收集;随着老年代内存增长,当到达IHOP阈值-XX:InitiatingHeapOccupancyPercent(老年代占整堆比,默认45%)时,G1开始准备收集老年代空间。首先经历并发标记周期,识别出垃圾占比较高的老年代分区。但随后G1并不会马上开始一次混合收集,而是让应用线程先运行一段时间,等待触发一次年轻代收集。在这次STW中,G1将保准整理混合收集周期。接着再次让应用线程运行,当接下来的几次年轻代收集时,将会有老年代分区加入到CSet中,即触发混合收集,这些连续多次的混合收集称为混合收集周期(Mixed Collection Cycle)

特点:

1、并行于并发:G1能充分利用CPU多核,使用多个CPU来缩短stop-The-World停顿时间。

2、分代收集:虽然G1可以不需要其他收集器配合就能独立管理整个GC堆,但是还是保留了分代的概念。

3、空间整合:与CMS的“标记--清理”算法不同,G1从整体来看是基于“标记整理”算法实现的收集器;从局部上来看是基于“复制”算法实现的。

4、可预测的停顿:这是G1相对于CMS的另一个大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内。

-------------------------------------------------------------

五、调优思路

-------------------------------------------------------------

5.1 前瞻

1、尝试多种垃圾回收器,G1并不是最好的

2、并发不等于并行。垃圾回收的过程实际上有两步,启动GC周期和GC自身运行,这是不同的两件事。并发针对的是GC周期,而并行针对GC算法自身。

3、平均事务时间不是最需要被关注的指标,有可能用户正好经历了那个长时间GC的场景,那将是毁灭性的。

4、GC调优并不能解决所有的事。如果程序修改程度大,那应该优先优化架构及代码。

5、GC日志并不会对性能造成太大的影响,在GC未被优化之前,开启GC日志是有必要的。

6、降低新对象的分配率可以改善GC的运行状况。粗略地把系统中的对象分为三种:长命(long-lived)对象,对它们我们一般做不了什么;中等寿命(mid-lived)对象,最大的问题可能出现在这;短命(short-lived)对象,它们的释放和回收通常都很快,在下个GC周期来临时就会消失

5.2 思路

1、理解应用需求和问题。

2、掌握GC的状态。

3、思考选择的GC是否符合我们的应用特征。

4、分析确认需要调整的参数。

5、验证调优。

5.3 查看设置参数

java -XX:+PrintFlagsInitial 查看初始值

-XX:+PrintFlagsFinal 查看最终值(初始值可能被修改掉)

-Xms 默认情况下堆内存的64分之一

-Xmx 默认情况下对内存的4分之一

-Xmn 默认情况下堆内存的3分之一

-XX:NewRatio 默认为2

-XX:SurvivorRatio 默认为8

-XX:+PrintGCDetails 开启GC详细日志-Xloggc:/cpic/cpicapp/perfma/xowl/../logs/xowl/gc.log -XX:+PrintGCDetails

5.4 GC一般合理表现

分析结果显示GC耗时在0.1-0.3秒以内的话,一般不需要花费额外的时间做GC调优。然而, 如果GC耗时达到1-3秒甚至10秒以上,就需要立即对系统进行GC调优 。

Minor GC执行迅速(50毫秒以内)

Minor GC执行不频繁(间隔10秒左右一次)

Full GC执行迅速(1秒以内)

Full GC执行不频繁(间隔10分钟左右一次)

-------------------------------------------------------------

六、参数调优

-------------------------------------------------------------

6.1 PS

6.1.1 吞吐量:-XX:GCTimeRatio=垃圾收集时间与应用程序时间的比率设置为1/(1+),默认值是99%(垃圾收集时间的1%)。

-Xmx:指定最大堆占用空间。

优先级保证:暂停时间>吞吐量>堆空间。如果不设置初始堆内存和最大堆内存,则初始堆大小为物理内存的1/64,最大内存为1/4,年轻代大小为堆内存的1/3。

-Xms (初始堆内存) and -Xmx (最大堆内存):

如果知道应用程序需要多少堆才能正常工作,那么可以将-Xms和-Xmx设置为相同的值。如果不知道,那么JVM将首先使用初始堆大小,然后自动增长,直到它找到堆使用和性能之间的平衡。

三个重要参数:1、-XX:MaxGCPauseTimeMillis:设置最大垃圾回收时间停顿时间。

2、-XX:GCTimeRatio:设置吞吐量大小。

3、-XX:UseAdaptiveSizePolicy:自适应模式。新生代大小,eden区与survivor区的比例,晋升老年代的对象年龄等参数会被自动调整。

CMS

使用方法:-XX:+UseConcMarkSweepGC

并发线程数:(ParallelGCThreads+3)/4。也可用通过-XX:ConcGCThreads或者-XX:ParallelCMSThreads手工设置。

因为并发性质,所以CMS不会等到堆饱和时才进行垃圾回收。默认值为老年代占用率68%,通过-XX:CMSInitiatingOccupancyFraction设置。

内存压缩:设定多少次之后GC回收之后对内存进行一次压缩。-XX:CMSFullGCsBeforeCompaction。默认0

开启-XX:CMSClassUnloadingEnable,可以在需要时候Perm区的还会触发一次FullGC。

6.2 G1

6.2.1 启用G1(常用):-XX:+UseG1GC

堆内存(常用):-XX:InitialHeapSize(初始堆内存)-XX:MaxHeapSize(最大堆内存)

年轻代设置(常用):-XX:NewSize(最小) -XX:MaxNewSize(最大)

暂停时间(常用):-XX:MaxGCPauseTimeMillis=(默认200ms)

空闲堆占比:-XX:MinHeapFreeRatio=40(GC后,如果发现空闲堆内存占到整个预估堆内存的40%,则放大堆内存的预估最大值,但不超过固定最大值。)-XX:MaxHeapFreeRatio=70

最大暂停间隔时间:-XX:PauseTimeIntervalMillis

GC停顿时候的并行的GC收集线程数:-XX:ParallelGCThreads=< ergo>根据虚拟机所在的主机的可用CPU线程数来计算的:如果CPU少于8个这个值就是cpu的数量,否则,就等于cpu数量*5/8。每个停顿开始的时候,最大的GC线程数还受限于最大的堆内存,G1的内个线程能使用的最大堆内存是由-XX:HeapSizePerGCThread来设置的。

与应用并发执行的GC线程数:-XX:ConcGCThreads=< ergo>:默认是-XX:ParallelGCThreads/4

region的大小:-XX:G1HeapRegionSize=< ergo>整个堆大概有2048个region,region的大小可以在1-32M之间,必须是2的次方。调整之后会影响分配对象的大小及停顿时间。

可分配的最大对象的大小: -XX:G1HeapRegionSize-XX:G1MaxNewSizePercent

-------------------------------------------------------------

七、针对项目经验集

-------------------------------------------------------------

7.1 这几种GC收集器相比之下,只要JDK版本在1.7u4及以上,推荐使用G1收集器。JDK1.7,1.8都默认使用PS(并行收集器)

7.2 尤其注意容器项目,容器设置的JVM配置内存大小不能大于容器内存大小,否则参数配置无效。

7.3 调优实例

7.3.1 实例一(集团2018版XXXXXXXX系统):

压测表现:稳定性压测时结果不稳定且内存消耗一直80%左右,影响时间3天。

内存分析表现:堆内存很大(7G)但年轻代内存非常小,年轻代minGC频繁,老年代内存一直增加直至触发majorGC,且GC暂停时间长,平均200~300ms

调优:调整年轻代内存为3G

export JAVA_OPTS="$JAVA_OPTS -Xmx7g -Xms7g -XX:NewSize=3g -XX:MaxNewSize=3g -XX:+UseG1GC"

优化结果:

复压,内存稳定在60%左右,年轻代GC频繁度减小,GC耗时100ms左右,老年代稳定无majorGC

7.3.2 实例二(寿险2013版XXXXXXXX系统):

压测表现:压测时压不上去,服务器消耗未满载。且压的时间长了TPS会有断崖式下降,TPS和相应时间非常不稳定。影响时间2天。

内存分析表现:heap内存只设置了2G,容器是3.5C7G,老年代一直增长直至触发majorGC,GC频繁且GC暂停时间不稳定。

调优:调整heap内存大小7G,新生代3G。

JAVA_OPTS="-Xmx7000m -Xms7000m -Xmn3072m -XX:PermSize=256M -XX:MaxPermSize=256M

调优结果:

TPS增长40~50且稳定。老年代稳定无majorGC。minorGC频繁度减小,GC暂停时间降低且稳定在几十ms以内。

7.3.3 Tip1:年轻代内存并不是越大越好,虽然会减小GC的频率,但是在GC时会增加回收时间造成GC暂停时间长。

Tip2:docker系统,如果容器内存4G,堆内存设置6G,进程可以启动且显示为6G,但实际只能使用到4G。

Tip3:如果开发和测试都不清楚如何设置堆大小及年轻代大小,可以参考perfma产品 http://xxfox.perfma.com/ ,填写相关参数会给出调优建议

java_opts gc回收器_JVM之垃圾回收机制(GC)相关推荐

  1. c++ 多线程 垃圾回收器_JVM的垃圾回收机制 总结(垃圾收集、回收算法、垃圾回收器)...

    一. 技术背景你要了解吧 按照套路是要先装装X,谈谈JVM垃圾回收的前世今生的.说起垃圾回收(GC),大部分人都把这项技术当做Java语言的伴生产物.事实上,GC的历史比Java久远,早在1960年L ...

  2. 垃圾回收机制GC知识再总结兼谈如何用好GC(转)

    作者:Jeff Wong  出处:http://jeffwongishandsome.cnblogs.com/  本文版权归作者和博客园共有,欢迎围观转载.转载时请您务必在文章明显位置给出原文链接,谢 ...

  3. JAVA垃圾回收机制GC之我姐是明星

    JAVA垃圾回收机制GC(Garbage Collection) 工作面试老伙伴之java垃圾回收机制 什么是GC,为什么要GC(我的明星老姐) 判断垃圾(找到不常穿的衣服) A 引用计数算法 B 可 ...

  4. Java-JVM虚拟机内存垃圾回收机制gc入门:引用类型,对象标记算法,回收算法,常见的 garbage collector

    文章目录 GC的优缺点 引用的四种类型 对象标记算法 引用计数法 可达性分析法 回收算法 标记-清除算法(Mark-Sweep) 复制算法 标记-整理算法(Mark-Compact) 分代收集算法 常 ...

  5. java垃圾回收机制_JVM的垃圾回收机制——垃圾回收算法

    一.Java垃圾回收机制 在java中,程序员是不需要显示的去释放一个对象的内存的,而是由虚拟机自行执行.在JVM中,有一个垃圾回收线程,它是低优先级的,在正常情况下是不会执行的,只有在虚拟机空闲或者 ...

  6. 判断是否存在此对象_JVM的垃圾回收机制,判断对象是否死亡

    这节我们主要讲垃圾收集的一些基本概念,先了解垃圾收集是什么.然后触发条件是什么.最后虚拟机如何判断对象是否死亡. 一.前言 我们都知道Java和C++有一个非常大的区别就是Java有自动的垃圾回收机制 ...

  7. JVM垃圾回收机制GC详解

    作为 Java 语言最重要的特性之一的自动垃圾回收机制,也是基于 JVM 实现的.那么,自动垃圾回收机制到底是如何实现的呢? 1.GC是干啥的? 进行资源的回收 1.1.对于 C/C++ 而言 对于C ...

  8. 【JVM】JVM垃圾回收机制GC

    文章目录 JVM垃圾回收机制 一.堆内存区域划分 1.1内存分配策略 1.2永久代(Permanent Generation) 1.3元空间(MetaSpace) 二.标记算法 2.1引用计数算法 2 ...

  9. java对于垃圾回收机制[GC垃圾回收机制] 为什么有GC还会有内存溢出呢?

    java垃圾回收机制 来源于书本和工作中的总结. 内存泄露 如果分配出去的内存得不到释放,及时回收,就会引起系统运行速度下降,甚至导致程序瘫痪,这就是内存泄露 GC机制 java内存分配和回收 都是j ...

最新文章

  1. 【对讲机的那点事】关于对讲机锂电池你了解多少?
  2. GitHub热榜第一,标星近万:这个用Python做交互式图形的项目火了
  3. python开发中文软件-Python 3程序开发指南(第二版)
  4. MYSQL5.7 忘记ROOT密码/初始化ROOT密码
  5. 自监督学习新思路!基于蒸馏(distillation loss)的自监督学习算法
  6. 云访问安全代理(CASB)行业调研报告 - 市场现状分析与发展前景预测(2021-2027年)
  7. Bailian4017 爬楼梯(POJ NOI0202-3089)【递推】
  8. HDU5667 - Sequence 矩阵快速幂 + 费马小定理
  9. jdbc中excute,excuteUpdate,excuteQuery函数解释
  10. netty 原理分析
  11. 主打产品“火力不足”致使发行人持续盈利能力下降,这公司创业板IPO被终止
  12. CAN总线的EMC设计方案
  13. [Squirrel基础]-- squirrel安装(通过Phoenix连接 HBase)
  14. html 鼠标悬停显示文字
  15. 22、Android之 使用手机的 GPS 功能
  16. 搭建机器人电控系统——PID算法——什么是PID?
  17. 时差 频差 双星定位 matlab,双星时差频差联合定位方法及其误差分析
  18. SpringMVC几个核心类(控制器核心类,加载配置文件核心类,处理url影射核心类,处理视图资源核心类,方法动态调用核心类)
  19. 第一次面试,面完,直接拒了!
  20. 数据工程学建设思考与实践

热门文章

  1. 【WEB安全】Xstream最新反序列化poc执行报错问题
  2. 【web安全】Web应用隔离防护之Web弱口令爆破
  3. Windows RDP协议 Fuzzing 漏洞挖掘研究
  4. Windows保护模式学习笔记(二)—— 代码跨段跳转
  5. 【Laravel】There is no existing directory at “…/storage/logs” and its not buildable: Permission denied
  6. 1.7 元注解作用及使用
  7. 1113 Integer Set Partition (25 分)【难度: 一般 / 知识点: 思维 贪心】
  8. 【PAT乙级】1005 继续(3n+1)猜想 (25 分)
  9. Keepalived设置开机自启
  10. linux防火墙 限制端口,Linux开启防火墙并限制开放端口