Skip to content
survior edited this page Jul 12, 2018 · 8 revisions

(1).G1收集器

1.1.G1收集器线上应用举例

java -Dspring.profiles.active=tw06 -Dlcp.adfacade.s_ab=detailDownload_000 -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -Dlogging.level.com.lcp=INFO -Dlogging.level.com.lcp.dbs.cache=INFO -Dexpression.workerId=3 -server -Xmx40g -Xms40g -XX:MaxGCPauseMillis=300 -XX:PermSize=128m -XX:MaxPermSize=256m -Xss256k -XX:+DisableExplicitGC -XX:+UseG1GC -XX:LargePageSizeInBytes=128m -verbose:gc -Xloggc:/data/logs/gc_lcp.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps -jar /data/apps/lcp/lib/lcp-1.0.0.jar

lcp.adfacade.s_ab=detailDownload_000:应用级别参数,程序使用。

spring.profiles.active=tw06:指定properties使用的版本。一般不同环境其值不同。

lcp.adfacade.s_ab=detailDownload_000:应用级别参数定义。

-Dlogging.level.com.lcp=INFO -Dlogging.level.com.lcp.dbs.cache=INFO:应用级别的不同组件的log日志路径不同。

-Dexpression.workerId=3:不同节点的uniq-num的高位区分值。

-Xms:sum(young+old)的初始大小。

-Xmx:sum(young+old)的最大大小。

-XX:MaxGCPauseMillis=300:设置最大GC停顿时间(GC pause time)指标(target). 这是一个软性指标(soft goal), JVM 会尽力去达成这个目标. 所以有时候这个目标并不能达成. 默认值为 200 毫秒.

-XX:DisableExplicitGC:这个参数作用是禁止代码中显示调用GC。代码如何显示调用GC呢,通过System.gc()函数调用。如果加上了这个JVM启动参数,那么代码中调用System.gc()没有任何效果,相当于是没有这行代码一样。

-XX:+UseG1GC:开启G1收集器的方式。

-XX:LargePageSizeInBytes:设置堆内存的内存页大小。调大后TLB(页表寄存器)可以将内存分页的页地址都存到这个寄存器中,速度大大提升。

-XX:PermSize=128m -XX:MaxPermSize=256m:前者是永久代初始内存,后者是永久代最大内存。注意LargePageSizeInBytes的配置最好要和这两个参数契合。

java.awt.headless=true:Headless模式虽然不是我们愿意见到的,但事实上我们却常常需要在该模式下工作,尤其是服务器端程序开发者。因为服务器(如提供Web服务的主机)往往可能缺少前述设备,但又需要使用他们提供的功能,生成相应的数据,以提供给客户端(如浏览器所在的配有相关的显示设备、键盘和鼠标的主机)。如果没有配置这个参数,启动可能会报错,因为服务器一般都是linux,没有显示屏,鼠标和键盘,所以需要设置为true,模拟有的行为。

-Xss256k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内 存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

java.net.preferIPv4Stack=true:用于限制优先使用IP4地址。

-XX:+PrintGCDetails:打印gc详细日志。

-XX:+PrintGCDateStamps:输出GC的时间戳。

1.2.G1 gc online log for example

2017-07-14T20:39:26.320+0800: 33455.950: [GC pause (young), 0.1019680 secs] [Parallel Time: 94.0 ms, GC Workers: 23] [GC Worker Start (ms): Min: 33455950.3, Avg: 33455950.5, Max: 33455950.6, Diff: 0.4] [Ext Root Scanning (ms): Min: 3.4, Avg: 4.0, Max: 6.5, Diff: 3.1, Sum: 91.6] [Update RS (ms): Min: 26.1, Avg: 28.4, Max: 29.0, Diff: 3.0, Sum: 653.4] [Processed Buffers: Min: 48, Avg: 57.3, Max: 65, Diff: 17, Sum: 1319] [Scan RS (ms): Min: 23.7, Avg: 24.1, Max: 24.6, Diff: 0.8, Sum: 554.6] [Code Root Scanning (ms): Min: 0.0, Avg: 0.0, Max: 0.0, Diff: 0.0, Sum: 0.0] [Object Copy (ms): Min: 36.5, Avg: 36.9, Max: 37.3, Diff: 0.8, Sum: 849.2] [Termination (ms): Min: 0.0, Avg: 0.1, Max: 0.1, Diff: 0.1, Sum: 2.2] [GC Worker Other (ms): Min: 0.0, Avg: 0.1, Max: 0.1, Diff: 0.1, Sum: 1.6] [GC Worker Total (ms): Min: 93.4, Avg: 93.6, Max: 93.8, Diff: 0.4, Sum: 2152.6] [GC Worker End (ms): Min: 33456044.0, Avg: 33456044.0, Max: 33456044.1, Diff: 0.1] [Code Root Fixup: 0.3 ms] [Code Root Migration: 0.0 ms] [Clear CT: 0.8 ms] [Other: 6.9 ms] [Choose CSet: 0.0 ms] [Ref Proc: 1.0 ms] [Ref Enq: 0.0 ms] [Free CSet: 1.1 ms] [Eden: 1760.0M(1760.0M)->0.0B(23.8G) Survivors: 288.0M->208.0M Heap: 11.0G(40.0G)->9560.0M(40.0G)] [Times: user=2.15 sys=0.01, real=0.10 secs]

(2).CMS收集器线上应用举例(这个例子是有写问题的,有几个配置不合适)

java -Djava.awt.headless=true -Djava.net.preferIPv4Stack=true -server -Xmx2g -Xms2g -Xmn256m -XX:PermSize=128m -Xss256k -XX:+DisableExplicitGC -XX:+UseConcMarkSweepGC -XX:+CMSParallelRemarkEnabled -XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m -XX:+UseFastAccessorMethods -XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70 -classpath xxx

-Xmn256m:-Xmn用来设置堆内新生代的大小。通过这个值我们也可以得到老生代的大小:-Xmx减去-Xmn。这个设小了。至少应该设置为官方3/8堆区大小。

-XX:+UseConcMarkSweepGC:CMS收集器也被称为短暂停顿并发收集器。它是对年老代进行垃圾收集的。CMS收集器通过多线程并发进行垃圾回收,尽量减少垃圾收集造成的停顿。CMS收集器对年轻代进行垃圾回收使用的算法和Parallel收集器一样。这个垃圾收集器适用于不能忍受长时间停顿要求快速响应的应用。可使用 -XX:ParallelCMSThreads=n JVM选项来限制CMS收集器的线程数量。CMS默认启动的回收线程数目是 (ParallelGCThreads + 3)/4),其中ParallelGCThreads是年轻代的并行收集线程数。

-XX:+CMSParallelRemarkEnabled:为了减少第二次暂停的时间,开启并行remark: -XX:+CMSParallelRemarkEnabled,如果remark还是过长的话,可以开启-XX:+CMSScavengeBeforeRemark选项,强制remark之前开始一次minor gc,减少remark的暂停时间,但是在remark之后也将立即开始又一次minor gc。之所以强制remark之前开始一次minor gc可以减少remark时间,是因为young区的对象很多都是死对象,minor清除之后可以大大减少remark时间,这也是为什么YGC时的stw不会有什么影响的原因。

-XX:+UseCMSCompactAtFullCollection:CMS是不会整理堆碎片的,因此为了防止堆碎片引起full gc,通过这个配置会开启CMS阶段进行合并碎片选项。

-XX:+UseFastAccessorMethods:设置关闭快速调用成员方法,这里表述可能不是太准确。首先说明一下什么方法叫做AccessorMethods,①必须是成员方法,静态方法不行,②返回值类型必须是引用类型或者int,其它都不算,③方法体的代码必须满足aload_0; getfield #index; areturn或ireturn这样的模式,方法名是什么都没关系,是不是get、is、has开头都不重要。 因为此类方法方法体很简单,而且没有方法计数器,开启此设置后可以跳过对该类方法的编译,详细参见http://mail.openjdk.java.net/pipermail/hotspot-compiler-dev/2011-March/005057.html该设置默认在jdk6中是开启的,但由于jdk7的server模式默认开启了多层编译,此时在这种多台方法调用时甚至会导致性能下降,所以设置关闭(jdk7是否默认关闭不太清楚),但仍需要根据自己项目测试。这个参数没有必要开启。

-XX:+UseCMSInitiatingOccupancyOnly -XX:CMSInitiatingOccupancyFraction=70:前者:使用手动定义初始化定义开始CMS收集;后者:使用cms作为垃圾回收,使用70%后开始CMS收集。为了保证不出现promotion failed(见下面介绍)错误,该值的设置需要满足一些条件,见后边。

(3).gc的一些问题:

3.1.OutOfMemoryError出现场景:

顺着jvm内存结构梳理的记,jvm内存分几块?堆内内存,永久代,直接内存,这几处都有可能出现。会完全用尽再报么?肯定不可能,完全用尽就报不出来了,连存异常栈的信息都没有地方存了,更不可能打印出来了。当内存达到阈值或者gc后可用内存小于阈值后会报outofmemoryerror(至于阈值多少没有查到)。

3.1.1.java.lang.OutOfMemoryError: Direct buffer memory

发生原因:用来nio,但是direct buffer不够。

解决办法:1)检查是否直接或间接使用了nio,例如手动调用生成buffer的方法或者使用了nio容器如netty,jetty,tomcat等等;nio是在底层是调用了native方法进行allocate分配内存,而这部分堆外内存的回收在java层面是用system.gc触发回收的。见3);2)-XX:MaxDirectMemorySize加大,该参数默认是64M,可以根据需求调大试试;3)检查JVM参数里面有无:-XX:+DisableExplicitGC,如果有就去掉(因为这个参数会将system.gc调用忽略)。

代码跟踪:生成ByteBuffer的时候调用了allication方法,按下面一步接一步的调用,可以看到System.gc()。

(2).java -Xms2048m -Xmx2048m -XX:NewRatio=3 -XX:SurvivorRatio=4 -XX:MaxTenuringThreshold=4 -XX:TargetSurvivorRatio=90 -XX:+PrintTenuringDistribution -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -cp aa.jar com.server.Server