千家信息网

Java垃圾收集器的介绍以及JVM调优方法

发表于:2026-10-31 作者:千家信息网编辑
千家信息网最后更新 2026年10月31日,这篇文章主要讲解了"Java垃圾收集器的介绍以及JVM调优方法",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Java垃圾收集器的介绍以及JVM调优方法
千家信息网最后更新 2026年10月31日Java垃圾收集器的介绍以及JVM调优方法

这篇文章主要讲解了"Java垃圾收集器的介绍以及JVM调优方法",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Java垃圾收集器的介绍以及JVM调优方法"吧!

垃圾收集器

1.Serial

关注点:stw的时间 复制算法 STW

2.ParNew

关注点:stw的时间 Serial的多线程版本 复制算法 STW

参数 -XX:ParallelGCThreads 并行线程数

3.Parallel Scavenge

关注点:达到一个可控制的吞吐量(吞吐量优先) 复制算法 所谓吞吐量就是CPU运行用户代码的时间和CPU消耗的时间的比值,即吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)。

Paralled Scavenge 收集器提供了两个参数用于精确控制吞吐量。分别是控制最大垃圾收集停顿时间的-XX:MaxGCPauseMillis ,以及直接设置吞吐量大小的-XX:GCTimeRatio 参数。

参数

-XX:MaxGCPauseMillis -XX:GCTimeRatio-XX:+UseAdaptiveSizePolicy 自适应调节策略

old区

1.Serial Old

Serial的老年代版本 JDK1.5之前可搭配Parallel Scavenge使用在CMS发生Concurrent mode failure时使用Mark-Compact

2.Parallel Old

Parallel Scavenge的老年代版本JDK1.6出现 搭配Parallel Scavenge使用Mark-Compact

3.CMS

-XX:+UseConcMarkSweepGCMark-Sweep过程:初始标记 并发标记 重新标记 并发清除初始标记和重新标记会STW耗时最长是并发标记和并发清除默认回收线程数:CMS默认启动的回收线程数目是 (ParallelGCThreads + 3)/4),可以通过-XX:ParallelCMSThreads=20来设定优点:并发低停顿缺点:1、对CPU资源非常敏感 占用部分线程(CPU资源)导致应用程序变慢,吞吐量降低默认线程数=(CPU数量+3)/42、无法处理浮动垃圾,可能出现Concurrent mode failure而导致另一次fullGC的产生参数-XX:CMSInitiatingOccupancyFraction默认68% CMS触发百分比,CMS执行期间,预留空间无法满足,就会出现Concurrent mode failure失败,启动后备SerialOld的方案,停顿时间更长了。(所以CMSInitiatingOccupancyFraction不能设置的过大)3、内存碎片参数-XX:+UseCMSCompactAtFullCollection(停顿时间加长) 默认开启-XX:CMSFullGCsBeforeCompaction(执行多少次不进行碎片整理的FullGC后进行一次带压缩的)其他参数初始标记的并行化-XX:+CMSParallelInitialMarkEnabled为了减少第二次暂停的时间,开启并行remark: -XX:+CMSParallelRemarkEnabled,如果remark还是过长的话,可以开启-XX:+CMSScavengeBeforeRemark(在CMS GC前启动一次ygc,目的在于减少old gen对ygc gen的引用,降低remark时的开销-----一般CMS的GC耗时 80%都在remark阶段)为了避免Perm区满引起的full gc,建议开启CMS回收Perm区选项:+CMSPermGenSweepingEnabled -XX:+CMSClassUnloadingEnabled-XX:+UseConcMarkSweepGC:设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。-XX:+UseParNewGC:设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。

G1收集器

收集器组合开关选项

-XX:+UseSerialGC Serial+SerialOld-XX:+UseParNewGC ParNew+SerialOld-XX:+UseParallelGC ParallelScavenge+SerialOld -XX:+UseConcMarkSweepGC ParNew+CMS+SerialOld  FullGC算法:单线程的Mark Sweep Compact(MSC)-XX:+UseParallelOldGC ParallelScavenge+Parallel Old  FullGC算法:PS MarkSweep

JVM参数

垃圾回收参数-Xnoclassgc 是否对类进行回收-verbose:class -XX:+TraceClassUnloading 查看类加载和卸载信息-XX:SurvivorRatio Eden和其中一个survivor的比值-XX:PretenureSizeThreshold 大对象进入老年代的阈值,Serial和ParNew生效-XX:MaxTenuringThreshold 晋升老年代的对象年龄,默认15, CMS默认是4-XX:HandlePromotionFailure 老年代担保-XX:+UseAdaptiveSizePolicy动态调整Java堆中各个区域大小和进入老年代年龄-XX:ParallelGCThreads 并行回收的线程数-XX:MaxGCPauseMillis Parallel Scavenge参数,设置GC的最大停顿时间-XX:GCTimeRatio  Parallel Scavenge参数,GC时间占总时间的比率,默认99%,即1%的GC时间-XX:CMSInitiatingOccupancyFraction,old区触发cms阈值,默认68%-XX:+UseCMSCompactAtFullCollection(CMS完成后是否进行一次碎片整理,停顿时间加长)-XX:CMSFullGCsBeforeCompaction(执行多少次不进行碎片整理的FullGC后进行一次带压缩的)-XX:+ScavengeBeforeFullGC,在fullgc前触发一次minorGC垃圾回收统计信息-XX:+PrintGC 输出GC日志-verbose:gc等同于上面那个-XX:+PrintGCDetails 输出GC的详细日志堆大小设置-Xmx:最大堆大小-Xms:初始堆大小(最小内存值)-Xmn:年轻代大小-XX:NewSize和-XX:MaxNewSize 新生代大小-XX:SurvivorRatio:3 意思是年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5-Xss栈容量 默认256k-XX:PermSize永久代初始值-XX:MaxPermSize 永久代最大值

生产环境参数配置(CMS-GC)

Java < 8    -server    -Xms[g|m|k] -Xmx[g|m|k]    -XX:PermSize=[g|m|k] -XX:MaxPermSize=[g|m|k]    -Xmn[g|m|k]    -XX:+DisableExplicitGC    -XX:SurvivorRatio=    -XX:+UseConcMarkSweepGC     -XX:+CMSParallelRemarkEnabled    -XX:+CMSScavengeBeforeRemark    -XX:+UseCMSInitiatingOccupancyOnly     -XX:CMSInitiatingOccupancyFraction=    -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Xloggc:""    -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=`date`.hprof    -Dsun.net.inetaddr.ttl=    -Djava.rmi.server.hostname=    -Dcom.sun.management.jmxremote.port=     -Dcom.sun.management.jmxremote.authenticate=false     -Dcom.sun.management.jmxremote.ssl=falseJava >= 8    -server    -Xms[g|m|k] -Xmx[g|m|k]    -XX:MaxMetaspaceSize=[g|m|k]    -Xmn[g|m|k]    -XX:+DisableExplicitGC    -XX:SurvivorRatio=    -XX:+UseConcMarkSweepGC     -XX:+CMSParallelRemarkEnabled    -XX:+CMSScavengeBeforeRemark    -XX:+UseCMSInitiatingOccupancyOnly    -XX:CMSInitiatingOccupancyFraction=    -XX:+PrintGCDateStamps -verbose:gc -XX:+PrintGCDetails -Xloggc:""    -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=10 -XX:GCLogFileSize=100M    -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=`date`.hprof    -Dsun.net.inetaddr.ttl=    -Djava.rmi.server.hostname=    -Dcom.sun.management.jmxremote.port=     -Dcom.sun.management.jmxremote.authenticate=false     -Dcom.sun.management.jmxremote.ssl=false

理解GC日志

DefNew:Serial收集器新生代名称 - Tenured - PermParNew:ParNew收集器新生代名称 - PSYoungGen:Parallel Scavenge收集器新生代名称

JVM调优

  • GC的时间足够的小

  • GC的次数足够的少

  • 发生Full GC的周期足够的长

前两个目前是相悖的,要想GC时间小必须要一个更小的堆,要保证GC次数足够少,必须保证一个更大的堆,我们只能取其平衡。

(1)针对JVM堆的设置,一般可以通过-Xms -Xmx限定其最小、最大值,为了防止垃圾收集器在最小、最大之间收缩堆而产生额外的时间,我们通常把最大、最小设置为相同的值

(2)年轻代和年老代将根据默认的比例(1:2)分配堆内存,可以通过调整二者之间的比率NewRadio来调整二者之间的大小,也可以针对回收代,比如年轻代,通过 -XX:newSize -XX:MaxNewSize来设置其绝对大小。同样,为了防止年轻代的堆收缩,我们通常会把-XX:newSize -XX:MaxNewSize设置为同样大小。-XX:PermSize,-XX:MaxPermSize设置为一样防止老年代收缩。 -XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5 -XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。

(3)年轻代和年老代设置多大才算合理?这个我问题毫无疑问是没有答案的,否则也就不会有调优。我们观察一下二者大小变化有哪些影响 年轻代老年代设置:整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐年轻代配置为整个堆的3/8。

  • 更大的年轻代必然导致更小的年老代,大的年轻代会延长普通GC的周期,但会增加每次GC的时间;小的年老代会导致更频繁的Full GC

  • 更小的年轻代必然导致更大年老代,小的年轻代会导致普通GC很频繁,但每次的GC时间会更短;大的年老代会减少Full GC的频率

  • 如何选择应该依赖应用程序对象生命周期的分布情况:如果应用存在大量的临时对象,应该选择更大的年轻代;如果存在相对较多的持久对象,年老代应该适当增大。但很多应用都没有这样明显的特性,在抉择时应该根据以下两点:(A)本着Full GC尽量少的原则,让年老代尽量缓存常用对象,JVM的默认比例1:2也是这个道理 (B)通过观察应用一段时间,看其他在峰值时年老代会占多少内存,在不影响Full GC的前提下,根据实际情况加大年轻代,比如可以把比例控制在1:1。但应该给年老代至少预留1/3的增长空间

(4)在配置较好的机器上(比如多核、大内存),可以为年老代选择并行收集算法: -XX:+UseParallelOldGC ,默认为Serial收集

(5)线程堆栈的设置:每个线程默认会开启1M的堆栈,用于存放栈帧、调用参数、局部变量等,对大多数应用而言这个默认值太了,一般256K就足用。理论上,在内存不变的情况下,减少每个线程的堆栈,可以产生更多的线程,但这实际上还受限于操作系统。 -Xss256k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

(6)可以通过下面的参数打Heap Dump信息

  -XX:HeapDumpPath  -XX:+PrintGCDetails  -XX:+PrintGCTimeStamps  -Xloggc:/usr/aaa/dump/heap_trace.txt  通过下面参数可以控制OutOfMemoryError时打印堆的信息  -XX:+HeapDumpOnOutOfMemoryError

注:通过分析dump文件可以发现,每个1小时都会发生一次Full GC,经过多方求证,只要在JVM中开启了JMX服务,JMX将会1小时执行一次Full GC以清除引用.

性能分析工具

jps

-m 主类的参数 -l 主类的全名,如果执行的是jar包,输出jar路径-v 虚拟机参数

jstat

监视虚拟机运行状态信息,包括类装载、GC、运行期编译(JIT)用于输出java程序内存使用情况,包括新生代、老年代、元数据区容量、垃圾回收情况 jstat -gcutil 52670 2000 5 进程号 2s输出一次一共5次S0:幸存1区当前使用比例S1:幸存2区当前使用比例E:伊甸园区使用比例O:老年代使用比例M:元数据区使用比例CCS:压缩使用比例YGC:年轻代垃圾回收次数YGCT:年轻代垃圾回收消耗时间FGC:老年代垃圾回收次数FGCT:老年代垃圾回收消耗时间GCT:垃圾回收消耗总时间

jmap

jmap:用于生成堆转储快照。一般称为dump或heapdump文件。jmap -histo 3618上述命令打印出进程ID为3618的内存情况,包括有哪些对象,对象的数量。但我们常用的方式是将指定进程的内存heap输出到外              部文件,再由专门的heap分析工具进行分析,例如mat(Memory Analysis Tool),所以我们常用的命令是:jmap -dump:live,format=b,file=heap.hprof 3618-F 强制生成dump快照

jstack

jstack:用户输出虚拟机当前时刻的线程快照,常用于定位因为某些线程问题造成的故障或性能问题。一般称为threaddump文件。参数-F当正常输出没有响应的时候强制打印栈信息,一般情况不需要使用-l长列表. 打印关于锁的附加信息,一般情况不需要使用-m 如果调用本地方法的话,可打印c/c++的堆栈

jinfo

查看和修改虚拟机参数

可视化工具 JConsole Java监视与管理控制台 VisualVM 多合一故障处理工具

Java Class文件类型前缀

Element Type        Encodingboolean             Zbyte                Bchar                Cdouble              Dfloat               Fint                 Ilong                Jshort               S class or interface  Lclassname;[L代表了相应类型数组嵌套的层数

内存泄漏及解决方法

1.系统崩溃前的一些现象:

每次垃圾回收的时间越来越长,由之前的10ms延长到50ms左右,FullGC的时间也有之前的0.5s延长到4、5s

FullGC的次数越来越多,最频繁时隔不到1分钟就进行一次FullGC

年老代的内存越来越大并且每次FullGC后年老代没有内存被释放

之后系统会无法响应新的请求,逐渐到达OutOfMemoryError的临界值。

2.生成堆的dump文件 通过JMX的MBean生成当前的Heap信息,大小为一个3G(整个堆的大小)的hprof文件,如果没有启动JMX可以通过Java的jmap命令来生成该文件。

3.分析dump文件

下面要考虑的是如何打开这个3G的堆信息文件,显然一般的Window系统没有这么大的内存,必须借助高配置的Linux。当然我们可以借助X-Window把Linux上的图形导入到Window。我们考虑用下面几种工具打开该文件:

1.Visual VM 2.IBM HeapAnalyzer 3.JDK 自带的Hprof工具

使用这些工具时为了确保加载速度,建议设置最大内存为6G。使用后发现,这些工具都无法直观地观察到内存泄漏,Visual VM虽能观察到对象大小,但看不到调用堆栈;HeapAnalyzer虽然能看到调用堆栈,却无法正确打开一个3G的文件。因此,我们又选用了Eclipse专门的静态内存分析工具:Mat。

4.分析内存泄漏

通过Mat我们能清楚地看到,哪些对象被怀疑为内存泄漏,哪些对象占的空间最大及对象的调用关系。针对本案,在ThreadLocal中有很多的JbpmContext实例,经过调查是JBPM的Context没有关闭所致。

另,通过Mat或JMX我们还可以分析线程状态,可以观察到线程被阻塞在哪个对象上,从而判断系统的瓶颈。

5.回归问题

Q:为什么崩溃前垃圾回收的时间越来越长?

A:根据内存模型和垃圾回收算法,垃圾回收分两部分:内存标记、清除(复制),标记部分只要内存大小固定,时间是不变的,变的是复制部分,因为每次垃圾回收都有一些回收不掉的内存,所以增加了复制量,导致时间延长。所以,垃圾回收的时间也可以作为判断内存泄漏的依据

Q:为什么Full GC的次数越来越多?

A:因此内存的积累,逐渐耗尽了年老代的内存,导致新对象分配没有更多的空间,从而导致频繁的垃圾回收

Q:为什么年老代占用的内存越来越大?

A:因为年轻代的内存无法被回收,越来越多地被Copy到年老代

调优方法

一切都是为了这一步,调优,在调优之前,我们需要记住下面的原则:

1、多数的Java应用不需要在服务器上进行GC优化;

2、多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题;

3、在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合)

4、减少创建对象的数量;

5、减少使用全局变量和大对象;

6、GC优化是到最后不得已才采用的手段;

7、在实际使用中,分析GC情况优化代码比优化GC参数要多得多;

GC优化的目的有两个

1、将转移到老年代的对象数量降低到最小;

2、减少full GC的执行时间;

为了达到上面的目的,一般地,你需要做的事情有:

1、减少使用全局变量和大对象;

2、调整新生代的大小到最合适;

3、设置老年代的大小为最合适;

4、选择合适的GC收集器

感谢各位的阅读,以上就是"Java垃圾收集器的介绍以及JVM调优方法"的内容了,经过本文的学习后,相信大家对Java垃圾收集器的介绍以及JVM调优方法这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

时间 年轻 内存 大小 垃圾 参数 线程 对象 年代 收集器 文件 堆栈 应用 情况 最大 信息 工具 标记 比例 分析 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 华服互联网科技怎么样 科技互联网项目 伴你行互联网科技有限公司 互联网科技公司的市场部门 农村开互联网科技讲堂 新华互联网科技2019 昭通云南众福互联网科技有限公司 全球顶尖互联网科技公司 互联网科技和信息 互联网科技如何创业 工业互联网展湾科技股份有限公司 山东子淼互联网科技有限公司 工业互联网科技成果转化 图游互联网科技 互联网科技包装股份有限公司上市 普陀区互联网教育科技管理办法 兰石爱特工业互联网科技分公司 求路客互联网科技电话 互联网科技的前景 互联网企业和网络科技有限公司 深圳互联网络科技有限公司 深圳齐家互联网科技 中正互联网科技招聘 移动互联网科技股 互联网和黑科技的看法 互联网科技产业特征 本届互联网博览会上的黑科技 中科明德北京互联网科技 区块链结合互联网科技 现代互联网科技公司
0