根据gc(垃圾回收器)的选择,进行参数优化
JVM给了三种选择:串行收集器、并行收集器、并发收集器,但是串行收集器只适用于小数据量的情况,所以这里的选择主要针对并行收集器和并发收集器。
-XX:+UseSerialGC:设置串行收集器 -XX:+UseParallelGC:设置并行收集器 -XX:+UseParalledlOldGC:设置并行年老代收集器 -XX:+UseConcMarkSweepGC:设置并发收集器
串行收集器
-XX:+UseSerialGC:设置串行收集器。
吞吐量优先的并行收集器
并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。
-XX:+UseParallelGC:设置为并行收集器。此配置仅对年轻代有效。即年轻代使用并行收集,而年老代仍使用串行收集。 -XX:ParallelGCThreads=20:配置并行收集器的线程数,即:同时有多少个线程一起进行垃圾回收。此值建议配置与CPU数目相等。 -XX:+UseParallelOldGC:配置年老代垃圾收集方式为并行收集。JDK6.0开始支持对年老代并行收集。 -XX:MaxGCPauseMillis=100:设置每次年轻代垃圾回收的最长时间(单位毫秒)。如果无法满足此时间,JVM会自动调整年轻代大小,以满足此时间。 -XX:+UseAdaptiveSizePolicy:设置此选项后,并行收集器会自动调整年轻代Eden区大小和Survivor区大小的比例,以达成目标系统规定的最低响应时间或者收集频率等指标。此参数建议在使用并行收集器时,一直打开。
响应时间优先的并发收集器
并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。
-XX:+UseConcMarkSweepGC:即CMS收集,设置年老代为并发收集。它的主要适合场景是对响应时间的重要性需求大于对吞吐量的需求,能够承受垃圾回收线程和应用线程共享CPU资源,并且应用中存在比较多的长生命周期对象。CMS收集的目标是尽量减少应用的暂停时间,减少Full GC发生的几率,利用和应用程序线程并发的垃圾回收线程来标记清除年老代内存。
-XX:CMSFullGCsBeforeCompaction=0:由于并发收集器不对内存空间进行压缩和整理,所以运行一段时间并行收集以后会产生内存碎片,内存使用效率降低。此参数设置运行0次Full GC后对内存空间进行压缩和整理,即每次Full GC后立刻开始压缩和整理内存。
-XX:+UseCMSCompactAtFullCollection:打开内存空间的压缩和整理,在Full GC后执行。可能会影响性能,但可以消除内存碎片。
-XX:+CMSIncrementalMode:设置为增量收集模式。一般适用于单CPU情况。
-XX:CMSInitiatingOccupancyFraction=70:表示年老代内存空间使用到70%时就开始执行CMS收集,以确保年老代有足够的空间接纳来自年轻代的对象,避免Full GC的发生。
其它垃圾回收参数
-XX:+ScavengeBeforeFullGC:年轻代GC优于Full GC执行。 -XX:-DisableExplicitGC:不响应 System.gc() 代码。 -XX:+UseThreadPriorities:启用本地线程优先级API。即使 java.lang.Thread.setPriority() 生效,不启用则无效。 -XX:SoftRefLRUPolicyMSPerMB=0:软引用对象在最后一次被访问后能存活0毫秒(JVM默认为1000毫秒)。 -XX:TargetSurvivorRatio=90:允许90%的Survivor区被占用(JVM默认为50%)。提高对于Survivor区的使用率。
推荐的JVM优化参数
CATALINA_OPTS=" -Xms2g #初始堆大小 -Xmx2g #最大堆大小 -Xss512k #线程栈大小,出现java.lang.StackOverflowError时增大此值 -Xmn1000m #年轻代大小,对半分 -XX:MetaspaceSize=128m #元数据类存储空间大小,存储与系统内存 -XX:MaxMetaspaceSize=512m #最大元数据类存储空间大小 -Xloggc:/usr/local/tomcat/logs/gc.log #记录gc日志路径 -XX:+PrintGCDetails #打印gc日志 -XX:+PrintGCDateStamps #打印gc发生的具体时间 -XX:+PrintPromotionFailure #打开了就知道是多大的新生代晋升到老生代引发的full gc -XX:-UseBiasedLocking #取消偏向锁 -XX:AutoBoxCacheMax=20000 #增大Integr cache -XX:+AlwaysPreTouch #启动时访问并置零内存页面 -Djava.security.egd=file:/dev/./urandom #此江湖偏方原因为Tomcat的SecureRandom显式使用SHA1PRNG算法时,初始因子默认从/dev/random读取会存在堵塞。额外效果是SecureRandom的默认算法也变成合适的SHA1了 -XX:+ExplicitGCInvokesConcurrent #full gc时,使用CMS算法,不是全程停顿,必选 -XX:+ParallelRefProcEnabled #并行的处理Reference对象,如WeakReference,默认为false,除非在GC log里出现Reference处理时间较长的日志,否则效果不会很明显,但我们总是要JVM尽量的并行,所以设了也就设了 -XX:+UseConcMarkSweepGC #老年代并发收集,CMS gc -XX:CMSInitiatingOccupancyFraction=75 #老年代内存空间使用到75%时执行CMS收集,以确保年老代有足够的空间接纳来自年轻代的对象,避免Full GC的发生。 -XX:+UseCMSInitiatingOccupancyOnly #在年老代使用了初始化的比例后并发收集器启动收集 -XX:-CMSClassUnloadingEnabled #在CMS中清理永久代中的过期的Class而不等到Full GC -XX:+CMSScavengeBeforeRemark -XX:+HeapDumpOnOutOfMemoryError #发生OOM时创建堆内存转储文件 -XX:HeapDumpPath=/usr/local/tomcat/logs #发生OOM时创建堆内存转储文件路径,在容器环境中,输出4G的HeapDump在普通硬盘会造成20秒以上的硬盘IO跑满,容易影响同一宿主机上所有其它容器 -XX:OnOutOfMemoryError=/usr/local/tomcat/bin/stop.sh #发生OOM记录内存转储文件后。执行的操作,可以重启或者停止服务 -XX:ErrorFile=/usr/local/tomcat/logs/hs_err_%p.log #jvm crash时hotspot会生成一个error文件,提供jvm状态信息 -XX:+PrintCommandLineFlags #将启动参数输出到catalina.out -XX:+PrintFlagsFinal #启动后打印默认和更改过的参数值到catalina.out -XX:-OmitStackTraceInFastThrow -Duser.timezone=Asia/Shanghai #用户所在时区 -Djavax.servlet.request.encoding=UTF-8 -Djavax.servlet.response.encoding=UTF-8 -Dfile.encoding=UTF-8 -Duser.country=CN -Duser.language=zh -Djava.awt.headless=true -Dcom.sun.management.jmxremote.port=1099 #以下参数为开启jmx远程给监控系统连接 -Dcom.sun.management.jmxremote.ssl=false -Dcom.sun.management.jmxremote.authenticate=false -Djava.rmi.server.hostname=127.0.0.1"
参数解释
-XX:CMSInitiatingOccupancyFraction设置技巧:(Xmx-Xmn)*(100-CMSInitiatingOccupancyFraction)/100 >= Xmn
例如在应用中 Xmx 是6000,Xmn 是 512,那么 Xmx-Xmn 是 5488M,也就是老年代有 5488M,CMSInitiatingOccupancyFraction=90 说明老年代到 90% 满的时候开始执行对老年代的并发垃圾回收(CMS),这时还 剩 10% 的空间是 5488*10% = 548M,所以即使 Xmn(也就是新生代共512M)里所有对象都搬到老年代里,548M 的空间也足够了,所以只要满足上面的公式,就不会出现垃圾回收时的 promotion failed,因此这个参数的设置必须与 Xmn 关联在一起。
-Xmn:新生代的内存空间大小,注意:此处的大小是(eden+ 2 survivor space),整个堆大小 = 新生代大小 + 老生代大小 + 永久代大小。在保证堆大小不变的情况下,增大新生代后,将会减小老生代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的 3/8。
PrintFlagsFinal
-XX:MaxTenuringThreshold:设置垃圾最大年龄。如果设置为0的话,则新生代对象不经过 Survivor 区,直接进入老年代。对于老年代比较多的应用(需要大量常驻内存的应用),可以提高效率。如果将此值设置为一 个较大值,则新生代对象会在 Survivor 区进行多次复制,这样可以增加对象在新生代的存活时间,增加在新生代即被回收的概率,减少Full GC的频率,这样做可以在某种程度上提高服务稳定性。该参数只有在串行 GC 时才有效,这个值的设置是根据本地的 jprofiler 监控后得到的一个理想的值,不能一概而论原搬照抄。
-XX:+UseCMSCompactAtFullCollection #打开内存空间的压缩和整理,在Full GC后执行。可能会影响性能,但可以消除内存碎片,配合CMS,默认为true,不需要显式设置
-XX:CMSFullGCsBeforeCompaction=0 #上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩,配合CMS,默认应该是开启
查看本机默认参数命令
java -XX:+PrintFlagsInitial
查看配置堆情况和使用情况
jmap -heap 2975
配置思路需要考虑的是 Java 提供的垃圾回收机制。虚拟机的堆大小决定了虚拟机花费在收集垃圾上的时间和频度。收集垃圾能够接受的速度和应用有关,应该通过分析实际的垃圾收集的时间和频率来调整。假如堆的大小很大,那么完全垃圾收集就会很慢,但是频度会降低。假如您把堆的大小和内存的需要一致,完全收集就很快,但是会更加频繁。调整堆大小的的目的是最小化垃圾收集的时间,以在特定的时间内最大化处理客户的请求。在基准测试的时候,为确保最好的性能,要把堆的大小设大,确保垃圾收集不在整个基准测试的过程中出现。
假如系统花费很多的时间收集垃圾,请减小堆大小。一次完全的垃圾收集应该不超过 3-5 秒。假如垃圾收集成为瓶颈,那么需要指定代的大小,检查垃圾收集的周详输出,研究垃圾收集参数对性能的影响。当增加处理器时,记得增加内存,因为分配能够并行进行,而垃圾收集不是并行的。
调优总结
年轻代大小选择
1.响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时,减少到达年老代的对象。
2.吞吐量优先的应用:尽可能的设置大,可能到达Gbit的程度。因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8CPU以上的应用。
年老代大小选择
1.响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可以会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:
1.并发垃圾收集信息
2.持久代并发收集次数
3.传统GC信息
4.花在年轻代和年老代回收上的时间比例
减少年轻代和年老代花费的时间,一般会提高应用的效率
2.吞吐量优先的应用:一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期的对象,而年老代尽存放长期存活对象。
3.较小堆引起的碎片问题
因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是,当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:
-XX:+UseCMSCompactAtFullCollection:使用并发收集器时,开启对年老代的压缩。
-XX:CMSFullGCsBeforeCompaction=0:上面配置开启的情况下,这里设置多少次Full GC后,对年老代进行压缩
常见的 Java 内存溢出有以下三种
(1) java.lang.OutOfMemoryError: Java heap space —-JVM Heap(堆)溢出
JVM 在启动的时候会自动设置 JVM Heap 的值,其初始空间(即-Xms)是物理内存的1/64,最大空间(-Xmx)不可超过物理内存。可以利用 JVM提供的 -Xmn -Xms -Xmx 等选项可进行设置。Heap 的大小是 Young Generation 和 Tenured Generaion 之和。在 JVM 中如果 98% 的时间是用于 GC,且可用的 Heap size 不足 2% 的时候将抛出此异常信息。
解决方法:手动设置 JVM Heap(堆)的大小。
(2) java.lang.OutOfMemoryError: PermGen space —- PermGen space溢出。
PermGen space 的全称是 Permanent Generation space,是指内存的永久保存区域。为什么会内存溢出,这是由于这块内存主要是被 JVM 存放Class 和 Meta 信息的,Class 在被 Load 的时候被放入 PermGen space 区域,它和存放 Instance 的 Heap 区域不同,sun 的 GC 不会在主程序运行期对 PermGen space 进行清理,所以如果你的 APP 会载入很多 CLASS 的话,就很可能出现 PermGen space 溢出。
解决方法: 手动设置 MaxPermSize 大小
(3) java.lang.StackOverflowError —- 栈溢出
栈溢出了,JVM 依然是采用栈式的虚拟机,这个和 C 与 Pascal 都是一样的。函数的调用过程都体现在堆栈和退栈上了。调用构造函数的 “层”太多了,以致于把栈区溢出了。通常来讲,一般栈区远远小于堆区的,因为函数调用过程往往不会多于上千层,而即便每个函数调用需要 1K 的空间(这个大约相当于在一个 C 函数内声明了 256 个 int 类型的变量),那么栈区也不过是需要 1MB 的空间。通常栈的大小是 1-2MB 的。
通常递归也不要递归的层次过多,很容易溢出。
解决方法:修改程序。
参考博客:
https://blog.csdn.net/kthq/article/details/8618052
http://blog.chopmoon.com/favorites/231.html
http://www.importnew.com/13954.html
https://blog.csdn.net/yjl33/article/details/78890363
报错解决:
1、java.lang.StackOverflowError异常
原因:Java中,栈的大小通过-Xss来设置,当栈中存储数据比较多时,需要适当调大这个值
堆和栈的概念:http://pengjiaheng.iteye.com/blog/518623 https://www.cnblogs.com/sxdcgaq8080/p/7150568.html