本文目录一览:
jvm - 常用调优启动参数配置
可以看到堆内存为2G,新生代为768M老年代为1280M,新生代采用ParNew收集器
-XX:+UseConcMarkSweepGC:新生代使用ParNew回收器,老年代使用CMS
线程栈为512k(默认1024k调小可以增加创建线程数,增加并发量)
同时打印 GC 详细信息和打印 GC 发生时间,当发生OOM时,Dump文件到指定路径
栈空间参数设置
-Xss: 设置线程的最大栈空间,栈空间越大,方法的递归深度越大
方法区参数设置(方法区大小的参数设置跟jdk版本相关)
jdk1.6,jdk1.7设置方法区永久代的大小
-XX:PermSize=5M
-XX:MaxPermSize=5M 最大永久代的大小默认是64M
jdk1.8及以上,永久代被移除,取而代之的是元数据区,元数据区是一块堆外的直接内存
如果不指定大小,那么会耗尽所有可用的系统内存
-XX:MaxMetaspaceSize=60M 设置最大元数据的大小
堆设置
-Xms:初始堆大小
-Xmx:最大堆大小
-Xmn:设置新生代大小,设置较大的新生代大小会减小老年代大小,新生代的大小一般为堆空间的1/3
-XX:NewSize=n: 设置年轻代大小
-XX:NewRatio=n:设置年轻代和年老代的比值(老年代/新生代)。如:为3,表示年轻代与年老代比值为1:3
年轻代占整个年轻代年老代和的1/4
-XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。
-Xmn,-XX:NewSize/-XX:MaxNewSize,-XX:NewRatio 3组参数都可以影响年轻代的大小,混合使用的情况下,优先级是什么?
1.高优先级:-XX:NewSize/-XX:MaxNewSize
2.中优先级:-Xmn(默认等效 -Xmn=-XX:NewSize=-XX:MaxNewSize=?)
3.低优先级:-XX:NewRatio
推荐使用-Xmn参数,原因是这个参数简洁,相当于一次设定 NewSize/MaxNewSIze而且两者相等,适用于生产环境。
-Xmn 配合 -Xms/-Xmx,即可将堆内存布局完成 -Xmn参数是在JDK 1.4 开始支持
下面两个参数配合使用,当系统发生堆空间不足时,会导出整个堆的信息,且导出到指定的文件中去,后面用MAT工具分析
-XX:HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=d:/a.dump
直接内存配置
-XX:MaxDirectMemorySize
设置直接内存大小,如果不设置,默认值为最大堆空间,即-Xmx指定的大小,当达到指定值时,
会触发垃圾回收,如果回收后也无法释放空间,那么将会抛出OOM
-XX:+UseSerialGC:新生代,老年代都使用串行收集器
-XX:+UseParNewGC:新生代使用ParNew收集器,老年代使用串行收集器Serial
(jdk9,10已经废除,因为ParNew只能和CMS收集器配合使用,而jdk9,10使用的默认收集器是G1)
-XX:+UseParallelGC:新生代使用ParallelGC,老年代使用串行收集器Serial
-XX:+UseParallelGC:新生代使用ParallelGC回收器,老年代使用串行回收器Serial
-XX:+UseParallelOldGC:新生代使用ParallelGC回收器,老年代使用ParallelOldGc回收器
两个重要参数
-XX:MaxGCPasuseMillis:设置最大垃圾回收停顿时间,设置的过小,可能导致垃圾回收频率加大
-XX:GCTimeRatio:设置吞吐量大小,取值范围为0-100,系统回收垃圾的停顿时间花费不超过1/(1+n)%
设置线程数量
-XX:ParallelGCThreads
-XX:+UseConcMarkSweepGC:新生代使用ParNew回收器,老年代使用CMS
CMS默认启动的并发线程数量为(parallelGCTheads+3)/4
设置并发线程数
-XX:ConcGCThreads=n
-XX:ParallelGCThreads=n
设置老年代空间使用率达到多少时执行CMS垃圾回收
-XX:CMSInitiatingOccupancyFraction 默认值为68
碎片整理参数,如果碎片不整理,可能造成没达到阈值就会触发老年代垃圾回收
-XX:+UseCMSCompactAtFullCollection :在CMS垃圾收集完成之后,进行一次内存碎片整理
-XX:+CMSFullGCsBeforeCompaction=n :在n次CMS回收后进行一次内存碎片整理
使用CMS回收方法去的perm区,默认情况下,还需要触发一次FullGC
-XX:+CMSClassUnloadingEnabled
XX:UseG1GC 开启G1垃圾收集器
两个重要的参数
-XX:MaxGcPasuseMillis :指定目标最大停顿时间,如果停顿的时间过小,一次收集的区域数量也会变小
就会增加FullGC的可能
-XX:parallelGCThreads :设置并行回收的GC线程数量
-XX:InitiatingHeapOccupancyPercent :设置整个堆使用率达到多少时,触发并发标记的执行,默认是45
-XX:+PrintGC 在程序运行期间,只要遇到GC,就会打印GC情况
占用大小-gc后大小 GC消耗时间
jdk9,jdk10默认使用G1收集器,所以打印GC参数不同
-Xlog:gc
-XX:+PrintGCDetails 打印GC详细信息(JDK8,9,10建议使用-Xlog:gc*)
-XX:+PrintGCTimeStamps 打印分析GC发生的时间
-XX:+PrintGCApplicationConcurrentTime 打印应用程序的执行时间
-XX:+PrintGCApplicationStoppedTime 打印GC产生停顿的时间
-Xloggc:/usr/local/log/gc.log 让gc日志打印在log文件夹下的gc文件中,因为默认情况下gc日志在控制台输出
栈上分配,逃逸分析(方法内的变量被外部引用)
允许对象直接在栈上进行分配,随线程停止而销毁,这样做可以加快分配速度,减少GC次数
栈空间较小,所以不适合大对象的栈上分配
-XX:+DoEscapeAnalysis 启用逃逸分析
-XX:+EliminateAllocations 开启标量替换(默认打开),允许对象打散分配在栈上
比如对象拥有id和name两个字段,那么这两个字段将会被视为两个独立的变量进行分配。
对象晋升
-MaxTenuringThreshold=n ,当对象经历了多少次GC次数后进入老年代
注意:大对象直接晋升到老年代
-PretenureSizeThreshold=n 这里的单位是字节,新生对象大于这个值的时候,会直接分配到老年代
1、对存活对象标注时间过长:比如重载了Object类的Finalize方法,导致标注Final Reference耗时过长;或者String.intern方法使用不当,导致YGC扫描StringTable时间过长。
2、长周期对象积累过多:比如本地缓存使用不当,积累了太多存活对象;或者锁竞争严重导致线程阻塞,局部变量的生命周期变长
当Eden区空间不足时,就会触发YGC。结合新生代对象的内存分配看下详细过程:
1、新对象会先尝试在栈上分配,如果不行则尝试在TLAB分配,否则再看是否满足大对象条件要在老年代分配,最后才考虑在Eden区申请空间。
2、如果Eden区没有合适的空间,则触发YGC。
3、YGC时,对Eden区和From Survivor区的存活对象进行处理,如果满足动态年龄判断的条件或者To Survivor区空间不够则直接进入老年代,如果老年代空间也不够了,则会发生promotion failed,触发老年代的回收。否则将存活对象复制到To Survivor区。
4、此时Eden区和From Survivor区的剩余对象均为垃圾对象,可直接抹掉回收。
此外,老年代如果采用的是CMS回收器,为了减少CMS Remark阶段的耗时,也有可能会触发一次YGC,这里不作展开。
大多数情况下,对象直接在年轻代中的Eden区进行分配,如果Eden区域没有足够的空间,那么就会触发YGC(Minor GC),YGC处理的区域只有新生代。因为大部分对象在短时间内都是可收回掉的,因此YGC后只有极少数的对象能存活下来,而被移动到S0区(采用的是复制算法)。当触发下一次YGC时,会将Eden区和S0区的存活对象移动到S1区,同时清空Eden区和S0区。当再次触发YGC时,这时候处理的区域就变成了Eden区和S1区(即S0和S1进行角色交换)。每经过一次YGC,存活对象的年龄就会加1。
下面4种情况,对象会进入到老年代中:
当晋升到老年代的对象大于了老年代的剩余空间时,就会触发FGC(Major GC),FGC处理的区域同时包括新生代和老年代。除此之外,还有以下4种情况也会触发FGC:
如何设置JVM参数
设置eclipse jvm参数
打开Eclipse 或者 MyEclipse
打开 Windows - Preferences - Java - Installed JREs
选中你所使用的 JDK,然后点击 Edit,会出现如下图:
在 Default VM Arguments输入框内输入: -Xms512m -Xmx512m
解释:
-Xms是设置java虚拟机的最小分配内存;-Xmx则是最大分配内存;512m为内存空间
一般-Xmx设置为你电脑物理内存的1/4,而把-Xms和 -Xmx设置为一样,
其实你可以设置得更大一些,只要系统能分配足够的内存就可以了,如果设置过大系统会提示你的。
JVM调优常用参数配置
说明:
1、一般初始堆和最大堆设置一样,因为:现在内存不是什么稀缺的资源,但是如果不一样,从初始堆到最大堆的过程会有一定的性能开销,所以一般设置为初始堆和最大堆一样。64位系统理论上可以设置为无限大,但是一般设置为 4G ,因为如果再大,JVM进行垃圾回收出现的暂停时间会比较长,这样全GC过长,影响JVM对外提供服务,所以不能太大。一般设置为4G。
2、-XX:NewRaio和-XX:SurvivorRatio这两个参数,都是设置年轻代和年老代的大小的,设置一个即可,第一是设置年轻代的大小,第二个是设置比值,理论上设置一个既可以满足需求
打印GC回收的过程日志信息
以下配置主要针对分代收集回收算法而言
年轻代的设置很关键
JVM中最大堆大小有三方面限制:相关操作系统的数据模型(32bit还是64bit)限制:系统的可用虚拟内存限制;系统的可用物理内存限制。32位系统下,一般限制在1.5G-2G;64位操作系统对内存没有限制。在Windows Server 2003系统,3.5G物理内存,JDK5.0下测试,最大设置为1478m。
典型设置:
JVM给了三种选择:串行收集器,并行收集器,并发收集器,但是串行收集器只适用于小数据量的情况,一般不考虑使用了,所以这里只针对并行收集器和并发收集器。默认情况下,JDK5.0以前是使用的串行收集器,如果想使用其他收集器需要在启动时加入相应的参数, JDK5.0以后,JVM会根据系统当前的配置进行判断
吞吐量优先的并行收集器
并行收集器主要以到达一定的吞吐量为目标,适用于后台处理
响应时间优先的并发收集器
并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。
6.1年轻代大小选择
响应时间优先的应用:尽可能设置大,直到接近系统的最低响应时间限制(根据实际情况选择)。在此种情况下,年轻代收集发生的频率也是最小的。同时减少到达年老代的对象。
吞吐量优先的应用:尽可能的设置大,可能到达Gbit的成都,因为对响应时间没有要求,垃圾收集可以并行进行,一般适合8核CPU以上应用。
6.2年老代大小选择
响应时间优先的应用:年老代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数。如果堆设置小了,可能会造成内存碎片、高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考一下数据获得:
1、并发垃圾收集信息
2、持久代并发收集次数
3、传统GC信息
4、花在年轻代和年老代回收上的时间比例减少年轻代和年老代花费的时间,一般会提高应用的效率
6.3吞吐量优先的应用
一般吞吐量优先的应用都有一个很大的年轻代和一个较小的年老代。原因是,这样可以尽可能回收掉大部分短期对象,减少中期对象,而年老代尽存放长期存活的对象
6.4较小堆引起的碎片问题
因为年老代的并发收集器使用标记、清除算法,所以不会对堆进行压缩。当收集器回收时,他会把相邻的空间进行合并,这样可以分配给较大的对象。但是当堆空间较小时,运行一段时间以后,就会出现“碎片”,如果并发收集器找不到足够的空间,那么并发收集器将会停止,然后使用传统的标记、清除方式进行回收。如果出现“碎片”,可能需要进行如下配置:
Jconsole,jProfile,VisualVM
Jconsole:jdk自带, 功能简单,但是可以再系统有一定负荷的情况下使用,对垃圾回收算法有很详细的跟踪。
JProfiler:商业软件,需要付费,但是功能强大
VisualVM:JDK自带,功能强大,与Jprofiler类似,推荐
观察内存释放情况、集合类检查,对象树
上面这些调优工具都提供了强大的功能,但是总的来说一般分为以下几类功能:
一般就是根据垃圾回收前后情况对比,同时根据对象引用情况( 常见的集合对象引用 )分析,基本都可以找到泄漏点。
持久代沾满处理:
1、-XX:MaxPermSize=16m
2、换JDK比如:JRocket
系统内存被沾满:
一般是因为没有足够的资源产生线程造成的,系统创建线程时,除了要在Java堆中分配内存外,操作系统本身也需要分配资源来创建线程。因此,当线程数量大的一定程度以后,堆中或许还有空间,但是操作系统分配不出资源来了,出现异常。
分配给Java虚拟机的内存越多,系统剩余的资源就越少,因此,当系统内存固定时,分配给Java虚拟机的内存越多,那么,系统总共能够产生的线程也就越少,两者成反比。同事,可以通过修改-Xss来减少分配给单个线程的空间,也可以增加系统总共生产的线程数。
java程序内存问题的诊断方法:
查看jmap的命令参数,帮助查看堆信息