JVM调优分享

52次阅读

共计 3484 个字符,预计需要花费 9 分钟才能阅读完成。

面试精选集,快快前往领取吧!offer.liangsonghua.me/。关注微信公众号:松花皮蛋的黑板报,获取更多精彩!

一. 默认配置

配置及说明:

-Djava.library.path=/usr/local/lib-server -Xms6144m-Xmx6144m-XX:MaxPermSize=256m-Dsun.net.client.defaultConnectTimeout=60000-Dsun.net.client.defaultReadTimeout=60000-Dnetworkaddress.cache.ttl=300-Dsun.net.inetaddr.ttl=300

-Djava.library.path

指定非 java 类包的位置(如:dll,so)。

-server

如果 tomcat 是运行在生产环境中的,这个参数必须加上,-server 参数可以使 tomcat 以 server 模式运行, 这个模式下将拥有:更大、更高的并发处理能力,更快更强捷的 JVM 垃圾回收机制,可以有更大的负载与吞吐量。

-Xms<size> 和 -Xmx<size>

前者表示 JVM 初始化堆的大小,后者表示 JVM 堆的最大值。一般把 Xms 与 Xmx 两个值设成一样是最优的做法,否则会导致 jvm 有较为频繁的 GC,影响系统性能。

-XX:MaxPermSize=256m

初始化 JVM 非堆(持久代、永久代、方法区)最大值。

-Dsun.net.client.defaultConnectTimeout=60000

连接建立超时设置。

-Dsun.net.client.defaultReadTimeout=60000

内容获取超时设置。

-Dnetworkaddress.cache.ttl=300

jvm dns 缓存超时的相关设置。

-Dsun.net.inetaddr.ttl=300

jvm dns 缓存超时的相关设置。

二. 调整 GC 策略

背景:

线上频繁发生报警(堆内存占用超过 80%),创建 dump 文件并分析发现,大量数据为 char[]、String 等类型,主要为业务模块产生的临时数据,以 mybatis 查询缓存字符串为主,无大对象,过段时间 full gc 会自行回收(但回收量有时较大,有时较少)。考虑调整 GC 策略。

配置及说明:

-XX:+UseParNewGC-XX:+UseConcMarkSweepGC-XX:+CMSParallelRemarkEnabled

-XX:+UseParNewGC

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

-XX:+UseConcMarkSweepGC

设置年老代为 CMS 并发收集。CMS 流程:初始标记 (CMS-initial-mark) -> 并发标记 (CMS-concurrent-mark) -> 重新标记 (CMS-remark) -> 并发清除 (CMS-concurrent-sweep) -> 并发重设状态等待下次 CMS 的触发 (CMS-concurrent-reset)。

-XX:+CMSParallelRemarkEnabled

CMS 开启并行 remark。

-XX:+CMSScavengeBeforeRemark

强制 remark 之前开始一次 minor gc,减少 remark 的暂停时间。

-Xss

设置每个线程的堆栈大小。JDK5.0 以后每个线程堆 栈大小为 1M,以前每个线程堆栈大小为 256K。根据应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一 个进程内的线程数还是有限制的,不能无限生成,经验值在 3000~5000 左右。线程栈的大小是个双刃剑,如果设置过小,可能会出现栈溢出,特别是在该线程内有递归、大的循环时出现溢出的可能性更大,如果该值设置过大,就有影响到创建栈的数量,如果是多线程的应用,就会出现内存溢出的错误。线出现过栈溢出。

三. 保底配置

背景:

线上频繁发生报警(堆内存占用超过 80%),调大堆内存到 6144m、调整 GC 策略后依然存在问题,分析 dump 文件发现主要数据为 char[]、String 等类型的临时数据,暂增加保底策略,堆内存达到 70% 后强制 CMS GC。

配置及说明

-XX:CMSInitiatingOccupancyFraction=70-XX:+UseCMSInitiatingOccupancyOnly

XX:CMSInitiatingOccupancyFraction=70

堆内存使用达到 70%后强制开始 CMS 收集。

-XX:+UseCMSInitiatingOccupancyOnly

只是用设定的回收阈值 (上面指定的 70%), 如果不指定,JVM 仅在第一次使用设定值, 后续则自动调整。

四. 记录 GC 日志

背景: 增加 gc 日志方便后续的 JVM 优化分析和问题排查。

配置及说明:

-XX:+PrintGCDetails-XX:+PrintGCDateStamps-Xloggc:/export/Logs/gc.log-XX:+UseGCLogFileRotation-XX:NumberOfGCLogFiles=10-XX:GCLogFileSize=1m

-XX:+PrintGCDetails

输出 GC 的详细日志。

-XX:+PrintGCDateStamps

输出 GC 的时间戳(以日期的形式)。

-Xloggc:/export/Logs/gc.log

gc 日志文件的输出路径。

-XX:+UseGCLogFileRotation

打开或关闭 GC 日志滚动记录功能,要求必须设置 -Xloggc 参数。

-XX:NumberOfGCLogFiles=3

设置滚动日志文件的个数,必须大于 1。

-XX:GCLogFileSize=512k

设置滚动日志文件的大小,必须大于 8k。

-XX:+PrintHeapAtGC

在进行 GC 的前后打印出堆的信息,该日志输出量较大,可以不开启。

五. 进一步优化

背景:

增加 70% 强制 CMS GC 配置后不再触发报警,但依然会在某特殊场景频繁 full gc。通过 gc 分析,怀疑在这种特殊场景下:内存分配过快导致很多数据在年轻代待的时间太短就进入老年代,致使老年代中不断堆积稍后就无效的对象,最终触发 full gc。考虑增大年轻代内存、eden 与 survivor 分配策略。

系统分析:

主要的内存消耗是业务产生的临时性数据,这些数据业务结束后即无效,增大年轻代有助于让这些临时性数据减少进入老年代进而触发 full gc 的概率,但是也不能一味增加年轻代,年轻代过大会影响 minor gc 过慢,系统吞吐量降低。

配置及说明:

-XX:NewRatio=3-XX:SurvivorRatio=4

-XX:NewRatio=3

设置老年代与新生代的比例。指定老年代 OC:新生代 YC 为 3:1。老年代占堆大小的 3/4,新生代占 1/4。

-XX:SurvivorRatio=4

年轻代中 Eden 区与 Survivor 区的大小比值。设置为 4,则表示 S0C:S1C:EC=1:1:4。该配置默认为 8。增大 Survivor 区可以容纳更多的存活对象。这样就会防止因为 Survivor 区太小导致很对存活对象还没有达到 MaxTenuringThreshold 阈值就直接进入老年代,潜在增大 old gc 的触发频率。

-XX:ParallelGCThreads=8

设置并行垃圾收集的线程数量。8 表示每次并行垃圾收集将有 8 个线程执行。如果不明确设置该标志,虚拟机将使用基于可用 (虚拟) 处理器数量计算的默认值。决定因素是由 Java Runtime。availableProcessors() 方法的返回值 N,如果 N<=8,并行垃圾收集器数 =N;如果 N>8,JVM 会调整算法,每超出 5 / 8 个 CPU 启动一个新的线程,并行垃圾收集器数 = 8 + ((N – 8) * 5/8) = 3+5*N/8。如 16 核对应 13 线程,32 核对应 23 线程。当 JVM 独占地使用系统和处理器时使用默认设置更有意义。但是,如果有多个 JVM(或其他耗 CPU 的系统) 在同一台机器上运行,我们应该使用 – XX:ParallelGCThreads 来减少垃圾收集线程数到一个适当的值。例如,如果 4 个以服务器方式运行的 JVM 同时跑在在一个具有 16 核处理器的机器上, 设置 – XX:ParallelGCThreads=4 是明智的,它能使不同 JVM 的垃圾收集器不会相互干扰。

文章来源:www.liangsonghua.me

作者介绍:京东资深工程师 - 梁松华,在稳定性保障、敏捷开发、JAVA 高级、微服务架构方面有深入的理解

正文完
 0