JVM参数调优整理

当系统报OOM(Out of Memory)异常时常见有以下几个原因

  • 老年代内存不足:java.lang.OutOfMemoryError:Javaheapspace;
  • 永久代内存不足:java.lang.OutOfMemoryError:PermGenspace;
  • 代码bug,占用内存无法及时回收。

前两种情况就可能需要优化JVM分配的内存容量。

一.参数配置

1.linux:Tomcat安装路径下 bin\catalina.sh 中添加如下参数:

CATALINA_OPTS="
-Xmx250m 
-Xms250m
-Xmn100m
-XX:SurvivorRatio=8
-XX:+UseG1GC
-XX:MaxTenuringThreshold=14
-XX:ParallelGCThreads=8
-XX:ConcGCThreads=8
-XX:+DisableExplicitGC
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=d:/a.dump
-XX:+PrintGCDetails
-XX:+PrintGCTimeStamps
-XX:+PrintHeapAtGC
-XX:+TraceClassLoading
-XX:+PrintClassHistogram"

参数添加到截图指定的位置:

2.windows:Tomcat安装路径下 bin\catalina.bat添加如下参数:

set “JAVA_OPTS=-Xmx800m -Xms800m -Xmn200m -XX:SurvivorRatio=8”

参数添加到截图指定的位置:

二.参数说明

在讲解参数时,请参考下面的图,对内存区域的大小有个形象的了解:

JVM启动参数共分为三类:

  • 标准参数(-),所有的JVM实现都必须实现这些参数的功能,而且向后兼容;
  • 非标准参数(-X),默认jvm实现这些参数的功能,但是并不保证所有jvm实现都满足,且不保证向后兼容;
  • 非Stable参数(-XX),此类参数各个jvm实现会有所不同,将来可能会随时取消,需要慎重使用;

常用参数详解:

-server一定要作为第一个参数,在多个 CPU 时性能佳,还有一种叫 -client 的模式,特点是启动速度比较快,但运行时性能和内存管理效率不高,通常用于客户端应用程序或开发调试,在 32 位环境下直接运行 Java 程序默认启用该模式。Server 模式的特点是启动速度比较慢,但运行时性能和内存管理效率很高,适用于生产环境,在具有 64 位能力的 JDK 环境下默认启用该模式,可以不配置该参数。
-Xms表示 Java 初始化堆的大小,-Xms 与-Xmx 设成一样的值,避免 JVM 反复重新申请内存,导致性能大起大落,默认值为物理内存的 1/64,默认(MinHeapFreeRatio参数可以调整)空余堆内存小于 40% 时,JVM 就会增大堆直到 -Xmx 的最大限制。
-Xmx表示最大 Java 堆大小,当应用程序需要的内存超出堆的最大值时虚拟机就会提示内存溢出,并且导致应用服务崩溃,因此一般建议堆的最大值设置为可用内存的最大值的80%。如何知道我的 JVM 能够使用最大值,使用 java -Xmx512M -version 命令来进行测试,然后逐渐的增大 512 的值,如果执行正常就表示指定的内存大小可用,否则会打印错误信息,默认值为物理内存的 1/4,默认(MinHeapFreeRatio参数可以调整)空余堆内存大于 70% 时,JVM 会减少堆直到-Xms 的最小限制。
-Xss表示每个 Java 线程堆栈大小,JDK 5.0 以后每个线程堆栈大小为 1M,以前每个线程堆栈大小为 256K。根据应用的线程所需内存大小进行调整,在相同物理内存下,减小这个值能生成更多的线程,但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在 3000~5000 左右。一般小的应用, 如果栈不是很深, 应该是128k 够用的,大的应用建议使用 256k 或 512K,一般不易设置超过 1M,要不然容易出现out ofmemory。这个选项对性能影响比较大,需要严格的测试。
-Xmn新生代的内存空间大小,注意此处的大小是(eden+ 2 survivor space)。与 jmap -heap 中显示的 New gen 是不同的。整个堆大小 = 新生代大小 + 老生代大小 + 永久代大小。在保证堆大小不变的情况下,增大新生代后,将会减小老生代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的 3/8。
-XX:NewSize设置新生代内存大小。
-XX:MaxNewSize设置最大新生代新生代内存大小
-XX:PermSize设置持久代内存大小
-XX:MaxPermSize设置最大值持久代内存大小,永久代不属于堆内存,堆内存只包含新生代和老年代。
-XX:+AggressiveOpts作用如其名(aggressive),启用这个参数,则每当 JDK 版本升级时,你的 JVM 都会使用最新加入的优化技术(如果有的话)。
-XX:+UseBiasedLocking启用一个优化了的线程锁,我们知道在我们的appserver,每个http请求就是一个线程,有的请求短有的请求长,就会有请求排队的现象,甚至还会出现线程阻塞,这个优化了的线程锁使得你的appserver内对线程处理自动进行最优调配。
-XX:+DisableExplicitGC在 程序代码中不允许有显示的调用“System.gc()”。每次在到操作结束时手动调用 System.gc() 一下,付出的代价就是系统响应时间严重降低,就和关于 Xms,Xmx 里的解释的原理一样,这样去调用 GC 导致系统的 JVM 大起大落。
-XX:+UseConcMarkSweepGC设置年老代为并发收集,即 CMS gc,这一特性只有 jdk1.5 后续版本才具有的功能,它使用的是 gc 估算触发和 heap 占用触发。我们知道频频繁的 GC 会造面 JVM的大起大落从而影响到系统的效率,因此使用了 CMS GC 后可以在 GC 次数增多的情况下,每次 GC 的响应时间却很短,比如说使用了 CMS GC 后经过 jprofiler 的观察,GC 被触发次数非常多,而每次 GC 耗时仅为几毫秒。
-XX:+UseParNewGC对新生代采用多线程并行回收,这样收得快,注意最新的 JVM 版本,当使用 -XX:+UseConcMarkSweepGC 时,-XX:UseParNewGC 会自动开启。因此,如果年轻代的并行 GC 不想开启,可以通过设置 -XX
-XX:MaxTenuringThreshold设置垃圾最大年龄。如果设置为0的话,则新生代对象不经过 Survivor 区,直接进入老年代。对于老年代比较多的应用(需要大量常驻内存的应用),可以提高效率。如果将此值设置为一 个较大值,则新生代对象会在 Survivor 区进行多次复制,这样可以增加对象在新生代的存活时间,增加在新生代即被回收的概率,减少Full GC的频率,这样做可以在某种程度上提高服务稳定性。该参数只有在串行 GC 时才有效,这个值的设置是根据本地的 jprofiler 监控后得到的一个理想的值,不能一概而论原搬照抄。
-XX:+CMSParallelRemarkEnabled在使用 UseParNewGC 的情况下,尽量减少 mark 的时间。
-XX:+UseCMSCompactAtFullCollection在使用 concurrent gc 的情况下,防止 memoryfragmention,对 live object 进行整理,使 memory 碎片减少。
-XX:LargePageSizeInBytes指定 Java heap 的分页页面大小,内存页的大小不可设置过大, 会影响 Perm 的大小。
-XX:+UseFastAccessorMethods使用 get,set 方法转成本地代码,原始类型的快速优化。
-XX:+UseCMSInitiatingOccupancyOnly只有在 oldgeneration 在使用了初始化的比例后 concurrent collector 启动收集。
-Duser.timezone=Asia/Shanghai设置用户所在时区。
-Djava.awt.headless=TRUE这个参数一般我们都是放在最后使用的,这全参数的作用是这样的,有时我们会在我们的 J2EE 工程中使用一些图表工具如jfreechart,用于在 web 网页输出 GIF/JPG 等流,在 winodws 环境下,一般我们的 app server 在输出图形时不会碰到什么问题,但是在linux/unix 环境下经常会碰到一个 exception 导致你在 winodws 开发环境下图片显示的好好可是在 linux/unix 下却显示不出来,因此加上这个参数以免避这样的情况出现。
-XX:CMSInitiatingOccupancyFraction当堆满之后,并行收集器便开始进行垃圾收集,例如,当没有足够的空间来容纳新分配或提升的对象。对于 CMS 收集器,长时间等待是不可取的,因为在并发垃圾收集期间应用持续在运行(并且分配对象)。因此,为了在应用程序使用完内存之前完成垃圾收集周期,CMS 收集器要比并行收集器更先启动。因为不同的应用会有不同对象分配模式,JVM 会收集实际的对象分配(和释放)的运行时数据,并且分析这些数据,来决定什么时候启动一次 CMS 垃圾收集周期。这个参数设置有很大技巧,基本上满足(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100 >= Xmn 就不会出现 promotion failed。例如在应用中 Xmx 是6000,Xmn 是 512,那么 Xmx-Xmn 是 5488M,也就是老年代有 5488M,CMSInitiatingOccupancyFraction=90 说明老年代到 90% 满的时候开始执行对老年代的并发垃圾回收(CMS),这时还 剩 10% 的空间是 5488*10% = 548M,所以即使 Xmn(也就是新生代共512M)里所有对象都搬到老年代里,548M 的空间也足够了,所以只要满足上面的公式,就不会出现垃圾回收时的 promotion failed,因此这个参数的设置必须与 Xmn 关联在一起。
-XX:+CMSIncrementalMode该标志将开启 CMS 收集器的增量模式。增量模式经常暂停 CMS 过程,以便对应用程序线程作出完全的让步。因此,收集器将花更长的时间完成整个收集周期。因此,只有通过测试后发现正常 CMS 周期对应用程序线程干扰太大时,才应该使用增量模式。由于现代服务器有足够的处理器来适应并发的垃圾收集,所以这种情况发生得很少,用于但 CPU情况。
-XX:NewRatio年轻代(包括 Eden 和两个 Survivor 区)与年老代的比值(除去持久代),-XX:NewRatio=4 表示年轻代与年老代所占比值为 1:4,年轻代占整个堆栈的 1/5,Xms=Xmx 并且设置了 Xmn 的情况下,该参数不需要进行设置。
-XX:SurvivorRatioEden 区与 Survivor 区的大小比值,设置为 8,表示 2 个 Survivor 区(JVM 堆内存年轻代中默认有 2 个大小相等的 Survivor 区)与 1 个 Eden 区的比值为 2:8,即 1 个 Survivor 区占整个年轻代大小的 1/10。
-XX:+UseSerialGC设置串行收集器。
-XX:+UseParallelGC设置为并行收集器。此配置仅对年轻代有效。即年轻代使用并行收集,而年老代仍使用串行收集。
-XX:+UseParallelOldGC配置年老代垃圾收集方式为并行收集,JDK6.0 开始支持对年老代并行收集。
-XX:ConcGCThreads早期 JVM 版本也叫-XX:ParallelCMSThreads,定义并发 CMS 过程运行时的线程数。比如 value=4 意味着 CMS 周期的所有阶段都以 4 个线程来执行。尽管更多的线程会加快并发 CMS 过程,但其也会带来额外的同步开销。因此,对于特定的应用程序,应该通过测试来判断增加 CMS 线程数是否真的能够带来性能的提升。如果还标志未设置,JVM 会根据并行收集器中的 -XX:ParallelGCThreads 参数的值来计算出默认的并行 CMS 线程数。
-XX:ParallelGCThreads配置并行收集器的线程数,即同时有多少个线程一起进行垃圾回收,此值建议配置与 CPU 数目相等。
-XX:OldSize设置 JVM 启动分配的老年代内存大小,类似于新生代内存的初始大小 -XX:NewSize

三.性能调优的总结

在CPU负载不足的同时,会有用户反映请求的时间过长,就应该意识到必须对程序及JVM进行调优,从以下几个方面进行:

  • 线程池:解决用户响应时间长的问题;
  • 连接池:链接时长和连接数等;
  • JVM启动参数:调整各代的内存比例和垃圾回收算法,提高吞吐量;
  • 程序算法:改进程序逻辑算法提高性能;

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

  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收集器;

进行监控和调优

  1. 监控GC的状态;
  2. GC频率不高,GC耗时不高,那么没有必要进行GC优化;如果GC时间超过1-3秒,或者频繁GC,则必须优化;
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值