java JVM参数设置以及垃圾回收的选择

tech2022-09-07  106

垃圾回收算法:

标记--清除算法 缺点 造成内存碎片

复制算法 空间利用不够 任何时候都只使用一般空间

标记整理算法 成本高,但是解决了内存碎片的缺点

垃圾收集器:

Serial收集器 是虚拟机新生代收集器的唯一选择

优点:简单而高效,对于限制单个CPU来说,由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率

ParNew收集器 是单线程收集器的多线程版本

ParNew收集器是许多运行在Server模式下的虚拟机中首选的新生代收集器。

有一个很重要的原因是除了Serial收集器外,目前只有它能与CMS收集器配合工作

Parallel收集器 吞吐量优先的收集器 是一个新生代收集器 他也是使用复制算法的收集器,又是并行的多线程收集器

优点:停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验, 在注重吞吐量以及CPU资源敏感的场合,都可以优先考虑Parallel Scavenge加Parallel Old收集器。

而高吞吐量则可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。

CMS收集器 是一种以获取最短回收停顿时间为目标的收集器,,这类回收器尤其注重服务的相应速度,希望系统停顿最短,给用户达到最好的体验,

cms 收集器是基于标记-清除算法实现的

G1收集器 是一款面向服务端应用的收集器,

特点:并行与并发 ,分代收集,空间整合,可预测的停顿,

JVM参数:

典型配置:

/usr/local/jdk/bin/java

-Dresin.home=/usr/local/resin

-server

-Xms1800M

-Xmx1800M

-Xmn300M

-Xss512K

-XX:PermSize=300M

-XX:MaxPermSize=300M

-XX:SurvivorRatio=8

-XX:MaxTenuringThreshold=5

-XX:GCTimeRatio=19

-Xnoclassgc

-XX:+DisableExplicitGC

-XX:+UseParNewGC

-XX:+UseConcMarkSweepGC

-XX:+UseCMSCompactAtFullCollection

-XX:CMSFullGCsBeforeCompaction=0

-XX:-CMSParallelRemarkEnabled

-XX:CMSInitiatingOccupancyFraction=70

-XX:SoftRefLRUPolicyMSPerMB=0

-XX:+PrintClassHistogram

-XX:+PrintGCDetails

-XX:+PrintGCTimeStamps

-XX:+PrintHeapAtGC

-Xloggc:log/gc.log

堆大小设置:

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k

-Xms2550m 初始化堆内存

-Xmx3350m 最大堆内存 一般初始化堆内存和最大内存一致,以避免每次垃圾回收完成之后JVM重新分配内存

-Xmn2g 设置年轻代大小为2G 整个堆大小=年轻代+老年代+持久代 持久代一般固定为64m,一般按照3/8 比例进行分配

-Xss128 设置每个线程的堆栈大小

java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m -XX:MaxTenuringThreshold=0

-XX:NewRatio=4 设置年轻代(包括eden区和servivor区)于年老代的比例,表示年轻代于年老代所占比值为1/4 ,年轻代占整个堆的1/5

-XX:SurvivorRatio=4 设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6

-XX:MaxPermSize=16m:设置持久代大小为16m。

-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论

回收器选择:

吞吐量优先的并行收集器

java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC

-XX:ParallelGCThreads=20

主要讲两个:

-XX:+UseParallelGC 垃圾收集器为并行收集器 ,此配置对年轻代有效,上述配置:年轻代使用并发收集,年老代用串行收集

-XX:ParallelGCThreads=20 配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收,此值最好配置于于处理器数目相等

Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20

-XX:+UseParallelOldGC

-XX:+UseParallelOldGC: 配置老年代垃圾收集方式为并行收集,jdk6 支持对老年代进行收集

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC

-XX:MaxGCPauseMillis=100

-XX:MaxGCPauseMillis=100 设置每次年轻代垃圾回收的最长时间,如果没法满足此时间, JVM会自动调节年轻代大小,以满足为止

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC

-XX:MaxGCPauseMillis=100-XX:+UseAdaptiveSizePolicy

-XX:+UseAdapativeSizePolicy:并行收集器会自动选择年轻代区大小和相应的survivor区的比例,以达到目标系统规定的最低相应时间或者收集频率,此值建议使用并行收集器时,一直打开

响应时间优先的收集器

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20

-XX:+UseConcMarkSweepGC -XX:+UseParNewGC

-XX:+UseConcMarkSweepGC:设置老年代为并发收集,配置这个之后,-XX:NewRatio=4的配置就失效了,原因不明,所以此时年轻代大小最好用-Xmn设置

-XX:+UseParNewGC :年轻代为并行收集,可与CMS收集器同时使用,jdk1.5以上JVM会更具系统配置自动设置,所以无需设置此值

java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC

-XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection

-XX:CMSFullGCBeforeCompaction=5:由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间后会产生“碎片”,使的运行效率降低,此值设置运行多少次GC以后对内存空间进行压缩,整理

-XX:+UseCMSCompactAtFullCollectio:打开对老年代的压缩,可能会影响性能,但是可以消除碎片

辅助信息

-XX:+PrintGC

输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs]

[Full GC 121376K->10414K(130112K), 0.0650971 secs]

-XX:+PrintGCDetails

输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs]

[GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs]

-XX:+PrintGCTimeStamps -XX:+PrintGC:PrintGCTimeStamps可与上面两个混合使用

输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs]

-XX:+PrintGCApplicationConcurrentTime:打印每次垃圾回收前,程序未中断的执行时间。可与上面混合使用

输出形式:Application time: 0.5291524 seconds

-XX:+PrintGCApplicationStoppedTime:打印垃圾回收期间程序暂停的时间。可与上面混合使用

输出形式:Total time for which application threads were stopped: 0.0468229 seconds

-XX:PrintHeapAtGC:打印GC前后的详细堆栈信息

-Xloggc:filename:与上面几个配合使用,把相关日志信息记录到文件以便分析。

并行收集器相关参数

参数名称

含义

默认值

说明

-XX:+UseParallelGC

Full GC采用parallel MSC(此项待验证)

 

选择垃圾收集器为并行收集器.此配置仅对年轻代有效.即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集.(此项待验证)

-XX:+UseParNewGC

设置年轻代为并行收集

 

可与CMS收集同时使用,JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值

-XX:ParallelGCThreads

并行收集器的线程数

 

此值最好配置与处理器数目相等 同样适用于CMS

-XX:+UseParallelOldGC

年老代垃圾收集方式为并行收集(Parallel Compacting)

 

这个是JAVA 6出现的参数选项

-XX:MaxGCPauseMillis

每次年轻代垃圾回收的最长时间(最大暂停时间)

 

如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值.

-XX:+UseAdaptiveSizePolicy

自动选择年轻代区大小和相应的Survivor区比例

 

设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开.

-XX:GCTimeRatio

设置垃圾回收时间占程序运行时间的百分比

 

公式为1/(1+n)

-XX:+ScavengeBeforeFullGC

Full GC前调用YGC

true

Do young generation GC prior to a full GC. (Introduced in 1.4.1.)

CMS相关参数

参数名称

含义

默认值

说明

-XX:+UseConcMarkSweepGC

使用CMS内存收集

 

测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明.所以,此时年轻代大小最好用-Xmn设置

-XX:+AggressiveHeap

  

试图是使用大量的物理内存长时间大内存。使用的优化,能检查计算资源(内存, 处理器数量)至少需要256MB内存,大量的CPU/内存, (在1.4.1在4CPU的机器上已经显示有提升)

-XX:CMSFullGCsBeforeCompaction

多少次后进行内存压缩

 

由于并发收集器不对内存空间进行压缩,整理,所以运行一段时间以后会产生"碎片",使得运行效率降低.此值设置运行多少次GC以后对内存空间进行压缩,整理.

-XX:+CMSParallelRemarkEnabled

降低标记停顿

  

-XX+UseCMSCompactAtFullCollection

在FULL GC的时候, 对年老代的压缩

 

CMS是不会移动内存的, 因此, 这个非常容易产生碎片, 导致内存不够用, 因此, 内存的压缩这个时候就会被启用。 增加这个参数是个好习惯。可能会影响性能,但是可以消除碎片

-XX:+UseCMSInitiatingOccupancyOnly

使用手动定义初始化定义开始CMS收集

 

禁止hostspot自行触发CMS GC

-XX:CMSInitiatingOccupancyFraction=70

使用cms作为垃圾回收,使用70%后开始CMS收集

92

为了保证不出现promotion failed(见下面介绍)错误,该值的设置需要满足以下公式CMSInitiatingOccupancyFraction计算公式

-XX:CMSInitiatingPermOccupancyFraction

设置Perm Gen使用到达多少比率时触发

92

 

-XX:+CMSIncrementalMode

设置为增量模式

 

用于单CPU情况

-XX:+CMSClassUnloadingEnabled

相对于并行收集器,CMS收集器默认不会对永久代进行垃圾回收。如果希望对永久代进行垃圾回收,可用设置标志-XX:+CMSClassUnloadingEnabled。在早期JVM版本中,要求设置额外的标志-XX:+CMSPermGenSweepingEnabled。注意,即使没有设置这个标志,一旦永久代耗尽空间也会尝试进行垃圾回收,但是收集不会是并行的,而再一次进行Full GC。

  

常见问题

内存泄漏及解决办法

1系统崩溃的一些现象:

1、每次垃圾回收的时间越来越长,由之前的10ms延长到50ms,Full GC 的时间也由原来的0.5s延长到4、5s

2、Full GC 的次数越来越多,最频繁时隔不到1分钟就进行Full GC

3、年老代的内存越来越大,并且Full GC 后年老代内存没有释放

2生成堆的dump文件

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

3分析dump文件

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

我们考虑用下面几种工具打开该文件:

Visual VMIBM HeapAnalyzerJDK 自带的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到年老代

年老代堆空间被占满

这种方式解决起来也比较容易,一般就是根据垃圾回收前后情况对比,同时根据对象引用情况(常见的集合对象引用)分析,基本都可以找到泄漏点。

持久代被占满

Perm空间被占满。无法为新的class分配存储空间而引发的异常。这个异常以前是没有的,但是在Java反射大量使用的今天这个异常比较常见了。主要原因就是大量动态反射生成的类不断被加载,最终导致Perm区被占满。

更可怕的是,不同的classLoader即便使用了相同的类,但是都会对其进行加载,相当于同一个东西,如果有N个classLoader那么他将会被加载N次。因此,某些情况下,这个问题基本视为无解。当然,存在大量classLoader和大量反射类的情况其实也不多。

解决:

1.-XX:MaxPermSize=16m2.换用JDK。比如JRocket

堆栈溢出

异常:java.lang.StackOverflowError说明:这个就不多说了,一般就是递归没返回,或者循环调用造成

线程堆栈满了

异常:Fatal: Stack size too small

说明:java中一个线程的空间大小是有限制的。JDK5.0以后这个值是1M。与这个线程相关的数据将会保存在其中。但是当线程空间满了以后,将会出现上面异常。

解决:增加线程栈大小。-Xss2m。但这个配置无法解决根本问题,还要看代码部分是否有造成泄漏的部分。

系统内存被占满

异常:java.lang.OutOfMemoryError: unable to create new native thread

说明:

这个异常是由于操作系统没有足够的资源来产生这个线程造成的。系统创建线程时,除了要在Java堆中分配内存外,操作系统本身也需要分配资源来创建线程。因此,当线程数量大到一定程度以后,堆中或许还有空间,但是操作系统分配不出资源来了,就出现这个异常了。

分配给Java虚拟机的内存愈多,系统剩余的资源就越少,因此,当系统内存固定时,分配给Java虚拟机的内存越多,那么,系统总共能够产生的线程也就越少,两者成反比的关系。同时,可以通过修改-Xss来减少分配给单个线程的空间,也可以增加系统总共内生产的线程数。

解决:

1.重新设计系统减少线程数量。2.线程数量不能减少的情况下,通过-Xss减小单个线程大小。以便能生产更多的线程。

 

 

最新回复(0)