JVM调优笔记 您所在的位置:网站首页 1G多少个字符 JVM调优笔记

JVM调优笔记

2023-02-26 11:41| 来源: 网络整理| 查看: 265

JVM调优笔记-JVM调优攻略

JVM

在Java虚拟机的参数中,有3种表示方法(出自:http://www.cnblogs.com/wenfeng762/archive/2011/08/14/2137810.html),用ps -ef |grep “java”命令,可以得到当前Java进程的所有启动参数和配置参数:

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

(额外的,-DpropertyName=“value”的形式定义了一些全局属性值,下面有介绍。) 本文只重点介绍一些重要和常用的参数,如果想了解全部参数,可以参考下面的文章: 《Java HotSpot VM Options》 《JVM启动参数大全》

标准参数

其实标准参数是用过Java的人都最熟悉的,就是你在运行java命令时后面加上的参数,如java -version, java -jar 等,输入命令java -help或java -?就能获得当前机器所有java的标准参数列表。

-client

设置jvm使用client模式,这是一般在pc机器上使用的模式,启动很快,但性能和内存管理效率并不高;多用于桌面应用;

-server 启动模式 新生代GC方式 旧生代和持久代GC的方式 client 串行 串行 server 并行 并发 -classpath / -cp

JVM加载和搜索文件的目录路径,多个路径用;分隔。注意,如果使用了-classpath,JVM就不会再搜索环境变量中定义的CLASSPATH路径。 JVM搜索路径的顺序为:

先搜索JVM自带的jar或zip包(Bootstrat,搜索路径可以用System.getProperty(“sun.boot.class.path”)获得); 搜索JRE_HOME/lib/ext下的jar包(Extension,搜索路径可以用System.getProperty(“java.ext.dirs”)获得); 搜索用户自定义目录,顺序为:当前目录(.),CLASSPATH,-cp;(搜索路径用System.getProperty(“java.class.path”)获得) -DpropertyName=value

定义系统的全局属性值,如配置文件地址等,如果value有空格,可以用-Dname=”space string”这样的形式来定义,用System.getProperty(“propertyName”)可以获得这些定义的属性值,在代码中也可以用System.setProperty(“propertyName”,”value”)的形式来定义属性。

-verbose-verbose:class

输出jvm载入类的相关信息,当jvm报告说找不到类或者类冲突时可此进行诊断。

-verbose:gc

输出每次GC的相关情况,后面会有更详细的介绍。

-verbose:jni

输出native方法调用的相关情况,一般用于诊断jni调用错误信息。

非标准参数

非标准参数,是在标准参数的基础上进行扩展的参数,输入“java -X”命令,能够获得当前JVM支持的所有非标准参数列表(你会发现,其实并不多哦)。 image_1b4fh891111hb1paab93dgv1hts9.png-229.2kB

-Xmn

新生代内存大小的最大值,包括E区和两个S区的总和,大小建议为整个堆栈的3/8,使用方法如:-Xmn65535,-Xmn1024k,-Xmn512m,-Xmn1g (-Xms,-Xmx也是种写法) -Xmn只能使用在JDK1.4或之后的版本中,(之前的1.3/1.4版本中,可使用-XX:NewSize设置年轻代大小,用-XX:MaxNewSize设置年轻代最大值); 如果同时设置了-Xmn和-XX:NewSize,-XX:MaxNewSize,则谁设置在后面,谁就生效; 如果同时设置了-XX:NewSize -XX:MaxNewSize与-XX:NewRatio则实际生效的值是: min(MaxNewSize,max(NewSize, heap/(NewRatio+1)))(参考:http://www.open-open.com/home/space.php?uid=71669&do=blog&id=8891) 建议: 在开发、测试环境,可以-XX:NewSize 和 -XX:MaxNewSize来设置新生代大小。 在线上生产环境,使用-Xmn一个即可(推荐),或者将-XX:NewSize 和 -XX:MaxNewSize设置为同一个值,这样能够防止在每次GC之后都要调整堆的大小(即:抖动,抖动会严重影响性能)

-Xms

初始堆的大小,也是堆大小的最小值,默认值是总共的物理内存/64(且小于1G),默认情况下,当堆中可用内存小于40%(这个值可以用-XX: MinHeapFreeRatio 调整,如-XX:MinHeapFreeRatio=30时,堆内存会开始增加,一直增加到-Xmx的大小;

-Xmx

堆的最大值,默认值是总共的物理内存/64(且小于1G),如果Xms和Xmx都不设置,则两者大小会相同,默认情况下,当堆中可用内存大于70%(这个值可以用-XX: MaxHeapFreeRatio 调整,如-XX:MaxHeapFreeRatio=60)时,堆内存会开始减少,一直减小到-Xms的大小; 整个堆的大小=年轻代大小+年老代大小,堆的大小不包含持久代大小,如果增大了年轻代,年老代相应就会减小,官方默认的配置为年老代大小/年轻代大小=2/1左右(使用-XX:NewRatio可以设置-XX:NewRatio=5,表示年老代/年轻代=5/1); 建议: 在开发测试环境可以用Xms和Xmx分别设置最小值最大值 在线上生产环境,Xms和Xmx设置的值必须一样,原因与年轻代一样——防止抖动;

-Xss

这个参数用于设置每个线程的栈内存,默认1M,一般来说是不需要改的。除非代码不多,可以设置的小点,另外一个相似的参数是-XX:ThreadStackSize,这两个参数在1.6以前,都是谁设置在后面,谁就生效;1.6版本以后,-Xss设置在后面,则以-Xss为准,-XXThreadStackSize设置在后面,则主线程以-Xss为准,其它线程以-XX:ThreadStackSize为准。

-Xrs

减少JVM对操作系统信号(OS Signals)的使用(JDK1.3.1之后才有效),当此参数被设置之后,jvm将不接收控制台的控制handler,以防止与在后台以服务形式运行的JVM冲突(这个用的比较少,参考:http://www.blogjava.net/midstr/archive/2008/09/21/230265.html)。

-Xprof

跟踪正运行的程序,并将跟踪数据在标准输出输出;适合于开发环境调试。

-Xnoclassgc

关闭针对class的gc功能;因为其阻止内存回收,所以可能会导致OutOfMemoryError错误,慎用;

-Xincgc

开启增量gc(默认为关闭);这有助于减少长时间GC时应用程序出现的停顿;但由于可能和应用程序并发执行,所以会降低CPU对应用的处理能力。

-Xloggc:file

与-verbose:gc功能类似,只是将每次GC事件的相关情况记录到一个文件中,文件的位置最好在本地,以避免网络的潜在问题。 若与verbose命令同时出现在命令行中,则以-Xloggc为准。

非Stable参数(非静态参数)

以-XX表示的非Stable参数,虽然在官方文档中是不确定的,不健壮的,各个公司的实现也各有不同,但往往非常实用,所以这部分参数对于GC非常重要。JVM(Hotspot)中主要的参数可以大致分为3类(参考http://blog.csdn.net/sfdev/article/details/2063928):

性能参数( Performance Options):用于JVM的性能调优和内存分配控制,如初始化内存大小的设置; 行为参数(Behavioral Options):用于改变JVM的基础行为,如GC的方式和算法的选择; 调试参数(Debugging Options):用于监控、打印、输出等jvm参数,用于显示jvm更加详细的信息;

对于非Stable参数,使用方法有4种:

-XX:+ 启用选项 -XX:- 不启用选项 -XX:= 给选项设置一个数字类型值,可跟单位,例如 32k, 1024m, 2g -XX:= 给选项设置一个字符串值,例如-XX:HeapDumpPath=./dump.core 行为参数 参数及其默认值 描述 -XX:-UseConcMarkSweepGC 使用`ParNew+CMS -XX:-UseSerialGC 启用串行GC,即采用Serial+Serial Old模式 -XX:-UseParallelGC 启用并行GC,即采用Parallel Scavenge+Serial Old收集器组合(-Server模式下的默认组合) -XX:-UseParallelOldGC 使用Parallel Scavenge +Parallel Old组合收集器 -XX:+UseParNewGC 使用ParNew+Serial Old收集器组合 -XX:GCTimeRatio=99 设置用户执行时间占总时间的比例(默认值99,即1%的时间用于GC) -XX:-DisableExplicitGC 禁止调用System.gc();但jvm的gc仍然有效 -XX:+ScavengeBeforeFullGC 新生代GC优先于Full GC执行,Full GC前调用YGC,默认true -XX:+MaxFDLimit 最大化文件描述符的数量限制 -XX:+UseGCOverheadLimit 在抛出OOM之前限制jvm耗费在GC上的时间比例 -XX:+UseTLAB 启用线程本地缓存区(Thread Local)。默认开启 -XX:+UseThreadPriorities 启用本地线程优先级,默认开启 -XX:+UseAltSigs 为了防止与其他发送信号的应用程序冲突,允许使用候补信号替代 SIGUSR1和SIGUSR2。限于Solaris,默认启用 -XX:+UseBoundThreads 限于Solaris, 默认启用绑定所有的用户线程到内核线程。减少线程进入饥饿状态(得不到任何cpu time)的次数。 -XX:+UseLWPSynchronization 限于solaris,默认启用。使用轻量级进程(内核线程)替换线程同步。 -XX:+MaxFDLimit 限于Solaris,默认启用,设置java进程可用文件描述符为操作系统允许的最大值。 -XX:+UseVMInterruptibleIO 限于solaris,默认启用。在solaris中,允许运行时中断线程 。 -XX:+CMSParallelRemarkEnabled 在使用 UseParNewGC 的情况下 , 尽量减少 mark 的时间 -XX:CMSInitiatingOccupancyFraction 指示在 old generation 在使用了 n% 的比例后 , 启动 concurrent collector, 默认值是 68, 如 :-XX:CMSInitiatingOccupancyFraction=70 -XX:+UseCMSInitiatingOccupancyOnly 指示只有在 old generation 在使用了初始化的比例后 concurrent collector 启动收集 -XX:TLABWasteTargetPercent TLAB占eden区的百分比,默认1% -XX:+CollectGen0First FullGC时是否先YGC,默认false -XX:+AggressiveHeap 试图使用大量的物理内存 -XX:CMSInitiatingPermOccupancyFraction 设置Perm Gen使用到达多少比率时触发 -XX:-AllowUserSignalHandlers 允许为java进程安装信号处理器。限于Linux和Solaris,默认不启用 -XX:AltStackSize=16384 备用信号堆栈大小 -XX:-RelaxAccessControlCheck 在Class校验器里,放松对访问控制的检查,默认不启用。作用与reflection里的setAccessible类似。 -XX:+UseSplitVerifier 使用新Class类型校验器, java5之前默认不启用,java6之后默认启用,详情参见附言:什么是新class类型校验器 -XX:+FailOverToOldVerifier 如果新的Class校验器检查失败,则使用老的校验器,默认启用 -XX:+HandlePromotionFailure 关闭新生代收集担保,默认开启,详情参见附言:什么是新生代担保 -XX:+UseSpinning 启用多线程自旋锁优化。默认开启,详情参见附言:自旋锁优化原理 -XX:PreBlockSpin=10 控制多线程自旋锁优化的自旋次数。默认10 垃圾收集(G1)选项 参数 描述 -XX:+UseG1GC 使用G1收集器 -XX:MaxGCPauseMillis=time 设置GC的最大停顿时间(软目标) -XX:InitiatingHeapOccupancyPercent=45 堆占用百分比达到目标后启动并发GC循环 -XX:NewRatio=2 新生代内存容量与老生代内存容量的比例(Old/New=NewRatio) -XX:SurvivorRatio=8 Eden区域Survivor区的容量比值,默认值为8,如Eden:Survivor1:Survivor2=8:1:1 -XX:MaxTenuringThreshold=15 对象在新生代存活区切换的次数(坚持过MinorGC的次数,每坚持过一次,该值就增加1),大于该值会进入老年代,默认15 -XX:ParallelGCThreads=n 并行垃圾收集器使用的线程数 -XX:ConcGCThreads=n 并发垃圾收集器使用的线程数。 -XX:G1ReservePercent=n 设置保留为false上限的堆的数量,以减少promotion失败的可能性. -XX:G1HeapRegionSize=n 讲G1的堆分为大小均匀的区域。这将设置每个子分区的大小。此参数的默认值是符合人体工程学的基于堆的大小确定。最小值为1MB,最大值是32mb。 性能参数 参数及其默认值 描述 -XX:NewSize=2.125m 新生代对象生成时占用内存的默认值 -XX:MaxNewSize=size 新生代对象能占用堆内存的最大值 -XX:MaxPermSize=64m 永久代最大值 -XX:PermSize=64m 永久代初始内存 -XX:NewRatio=2 新生代内存容量与老生代内存容量的比例(Old/New=NewRatio) -XX:SurvivorRatio=8 Eden区域Survivor区的容量比值,默认值为8,如Eden:Survivor1:Survivor2=8:1:1 -XX:TargetSurvivorRatio=50 清除后期望的幸存空间剩余比例 -XX:MaxHeapFreeRatio=70 GC后,如果发现空闲堆内存占到整个预估堆内存的70%,则收缩堆内存预估最大值。参见详情附言:什么是预估堆内存 -XX:MinHeapFreeRatio=40 GC后,如果发现空闲堆内存占到整个预估堆内存的40%,则放大堆内存的预估最大值,但不超过固定最大值。 -XX:ReservedCodeCacheSize= 32m 保留代码占用的内存容量 -XX:ThreadStackSize=512 设置线程栈大小,若为0则使用系统默认值 -XX:LargePageSizeInBytes=4m 设置堆的内存页面大小 -XX:PretenureSizeThreshold= size 大于该值的对象直接晋升入老年代(这种对象少用为好) -XX:+UseLargePages 使用大页面内存,详情Java Support for Large Memory Pages -XX:+UseFastAccessorMethods get,set 方法转成本地代码,使用优化的get原始字段 -XX:-UseISM 使用亲密共享内存,详情参见Intimate Shared Memory -XX:+UseMPSS 启用多页面支持的w/4mb堆内存,不能ISM同用 -XX:+UseStringCache 启用字符串缓存,缓存常用的字符串 -XX:AllocatePrefetchLines=1 高速缓存线后使用预取指令生成JIT编译的代码的最后一个对象分配负载数量。默认值是1,如果最后分配的对象是实例,如果数组是3。 -XX:AllocatePrefetchStyle=1 生成的预读指令的代码风格。0没有预读指令生成,1每次分配后执行预读指令,2使用TLAB分配水印指针时,预取指令执行。 -XX:+AggressiveOpts 启用JVM开发团队最新的调优成果。例如编译优化,偏向锁,并行年老代收集等,默认启用 -XX:+UseBiasedLocking 启用偏向锁,详情参见tuning example -XX:SoftRefLRUPolicyMSPerMB 每兆堆空闲空间中SoftReference的存活时间,默认1s -XX:CompileThreshold=10000 JIT将方法编译成机器码的触发阀值,可以理解为调用方法的次数,例如调1000次,这编译为机器码。默认1000 -XX:+UseCompressedStrings 使用byte[]可以代表纯ASCII -XX:+OptimizeStringConcat 优化字符串连接操作 -XX:CMSMaxAbortablePrecleanTime 当abortable-preclean阶段执行达到这个时间时才会结束 -XX:CMSScheduleRemarkEdenSizeThreshold(默认2m) 控制abortable-preclean阶段什么时候开始执行,即当eden使用达到此值时,才会开始abortable-preclean阶段 -XX:CMSScheduleRemarkEdenPenetratio(默认50%) 控制abortable-preclean阶段什么时候结束执行 调试参数 参数及其默认值 描述 -XX:-CITime 打印消耗在JIT编译的时间 -XX:ErrorFile=./hs_err_pid.log 保存错误日志或者数据到文件中 -XX:-ExtendedDTraceProbes 开启solaris特有的dtrace探针 -XX:HeapDumpPath=./java_pid.hprof 指定导出堆信息时的路径或文件名 -XX:-HeapDumpOnOutOfMemoryError 当遭遇OOM时导出此时堆中相关信息 -XX:OnError=”;” 出现致命ERROR之后运行自定义命令 -XX:OnOutOfMemoryError=”;” 当首次遭遇OOM时执行自定义命令 -XX:-PrintClassHistogram 遇到Ctrl-Break后打印类实例的柱状信息,与jmap -histo功能相同 -XX:-PrintConcurrentLocks 遇到Ctrl-Break后打印并发锁的相关信息,与jstack -l功能相同 -XX:-PrintCommandLineFlags 打印出JVM初始化完毕后所有跟初始化的默认之不同的参数及他们的值 -XX:-PrintCompilation 当一个方法被编译时打印相关信息 -XX:-PrintGC 每次GC时打印相关信息,输出形式:[GC 118250K->113543K(130112K), 0.0094143 secs][Full GC 121376K->10414K(130112K), 0.0650971 secs] -XX:-PrintGCDetails 每次GC时打印详细信息,输出形式:[GC [DefNew: 8614K->781K(9088K), 0.0123035 secs] 118250K->113543K(130112K), 0.0124633 secs][GC [DefNew: 8614K->8614K(9088K), 0.0000665 secs][Tenured: 112761K->10414K(121024K), 0.0433488 secs] 121376K->10414K(130112K), 0.0436268 secs] -XX:-PrintGCTimeStamps 打印每次GC的时间戳,输出形式:11.851: [GC 98328K->93620K(130112K), 0.0082960 secs] -XX:+PrintGCApplicationConcurrentTime 打印每次垃圾回收前,程序未中断的执行时间,输出形式:Application time: 0.5291524 seconds -XX:-PrintAdaptiveSizePolicy 打印有关自适应代的大小信息。 -XX:+PrintGCApplicationStoppedTime 打印垃圾回收期间程序暂停的时间,输出形式:Total time for which application threads were stopped: 0.0468229 seconds -XX:PrintHeapAtGC 打印GC前后的详细堆栈信息,参见附言 -XX:-PrintTenuringDistribution 打印对象的存活期限信息。 -XX:+PrintFlagsFinal 显示所有可设置的参数及”参数处理”后的默认值,可是查看不同版本默认值,以及是否设置成功.输出的信息中”=”表示使用的是初始默认值,而”:=”表示使用的不是初始默认值 -XX:+PrintFlagsInitial 在”参数处理”之前所有可设置的参数及它们的值,然后直接退出程序 -XX:-TraceClassLoading 跟踪类的加载信息 -XX:-TraceClassLoadingPreorder 跟踪被引用到的所有类的加载信息 -XX:-TraceClassResolution 跟踪常量池 -XX:-TraceClassUnloading 跟踪类的卸载信息 -XX:-TraceLoaderConstraints 跟踪类加载器约束的相关信息 -XX:+PerfDataSaveToFile 退出时保存jvmstat二进制文件 -XX:ParallelGCThreads=n 并行垃圾收集器使用的线程数 -XX:+UseCompressedOops 优化64位堆内存小于32G,使用压缩指针 -XX:+AlwaysPreTouch java堆在JVM初始化预处理。堆的每一页都是这样要求零初始化而不是增量在应用程序执行期间。 -XX:AllocatePrefetchDistance=n 设置对象分配预取距离。预读取缓存大小(以字节为单位)超过新分配的大小,则新对象被写入内存。每个java线程都有自己的配置点。默认值不同平台上的JVM运行。 -XX:InlineSmallCode=n 仅在其生成的本机代码大小小于此值时,将先前编译的方法内联。默认值不同平台上的JVM运行。 -XX:MaxInlineSize=35 方法内联的最大字节大小。 -XX:FreqInlineSize=n 频繁执行的是内联方法的最大字节大小。 -XX:LoopUnrollLimit=n 循环体展开服务器编译器的中间节点数小于这个值。服务器编译器使用的限制是这个值的函数,而不是实际值. -XX:InitialTenuringThreshold=7 自适应并行新生代的初始阀值。该阈值是对象在年轻被确认到年老带或终身存活次数。 -XX:MaxTenuringThreshold=n 设置自适应GC的最大阀值。当前最大的值为15。默认值是并行是15,CMS是4。 -XX:-UseGCLogFileRotation 启用GC日志文件的自动转储,要求Xloggc参数。 -XX:NumberOfGClogFiles=1 设置循环日志时使用的文件数,必须为> 1。循环的日志文件将使用以下命名方案,。0,。1,…,。1。 -XX:GCLogFileSize=8K 日志文件的大小,日志会循环,此时必须> = 8 k。。

主要参考了博客: http://blog.csdn.net/sfdev/article/details/2063928和http://kenwublog.com/docs/java6-jvm-options-chinese-edition.htm,后一个比较全面,有兴趣的可以仔细研读。

收集器搭配

image_1b4fjeid71q9p6v11g6n1t3q1ov6m.png-141kB 图中两个收集器之间有连线,说明它们可以配合使用

Serial收集器

Serial收集器是在client模式下默认的新生代收集器,其收集效率大约是100M左右的内存需要几十到100多毫秒; 在client模式下,收集桌面应用的内存垃圾,基本上不影响用户体验。所以,一般的Java桌面应用中,直接使用Serial收集器(不需要配置参数,用默认即可)。

ParNew收集器

Serial收集器的多线程版本,这种收集器默认开通的线程数与CPU数量相同,-XX:ParallelGCThreads可以用来设置开通的线程数。 可以与CMS收集器配合使用,事实上用-XX:+UseConcMarkSweepGC选择使用CMS收集器时,默认使用的就是ParNew收集器,所以不需要额外设置-XX:+UseParNewGC,设置了也不会冲突,因为会将ParNew+Serial Old作为一个备选方案; 如果单独使用-XX:+UseParNewGC参数,则选择的是ParNew+Serial Old收集器组合收集器。 一般情况下,在server模式下,如果选择CMS收集器,则优先选择ParNew收集器。

Parallel Scavenge收集器

关注的是吞吐量(关于吞吐量的含义见上一篇博客),可以这么理解,关注吞吐量,意味着强调任务更快的完成,而如CMS等关注停顿时间短的收集器,强调的是用户交互体验。 在需要关注吞吐量的场合,比如数据运算服务器等,就可以使用Parallel Scavenge收集器。

Serial Old收集器

在1.5版本及以前可以与 Parallel Scavenge结合使用(事实上,也是当时Parallel Scavenge唯一能用的版本),另外就是在使用CMS收集器时的备用方案,发生 Concurrent Mode Failure时使用。

如果是单独使用,Serial Old一般用在client模式中。

Parallel Old收集器

在1.6版本之后,与 Parallel Scavenge结合使用,以更好的贯彻吞吐量优先的思想,如果是关注吞吐量的服务器,建议使用Parallel Scavenge + Parallel Old 收集器。

CMS收集器

这是当前阶段使用很广的一种收集器,国内很多大的互联网公司线上服务器都使用这种垃圾收集器(http://blog.csdn.net/wisgood/article/details/17067203),笔者公司的收集器也是这种,CMS收集器以获取最短回收停顿时间为目标,非常适合对用户响应比较高的B/S架构服务器。

CMSIncrementalMode

CMS收集器变种,属增量式垃圾收集器,在并发标记和并发清理时交替运行垃圾收集器和用户线程。适用于单CPU

G1 收集器

面向服务器端应用的垃圾收集器,计划未来替代CMS收集器。

建议Java桌面应用

建议采用Serial+Serial Old收集器组合,即:-XX:+UseSerialGC(-client下的默认参数)

开发/测试环境

可以采用默认参数,即采用Parallel Scavenge+Serial Old收集器组合,即:-XX:+UseParallelGC(-server下的默认参数)

线上运算优先的环境

建议采用Parallel Scavenge+Serial Old收集器组合,即:-XX:+UseParallelGC

线上服务响应优先的环境

建议采用ParNew+CMS+Serial Old收集器组合,即:-XX:+UseConcMarkSweepGC

吞吐量优先的并行收集器

如上文所述,并行收集器主要以到达一定的吞吐量为目标,适用于科学技术和后台处理等。 典型配置 : java -Xmx3800m -Xms3800m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelGC :选择垃圾收集器为并行收集器。 此配置仅对年轻代有效。即上述配置下,年轻代使用并发收集,而年老代仍旧使用串行收集。 -XX:ParallelGCThreads=20 :配置并行收集器的线程数,即:同时多少个线程一起进行垃圾回收。此值最好配置与处理器数目相等。 java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:ParallelGCThreads=20 -XX:+UseParallelOldGC -XX:+UseParallelOldGC :配置年老代垃圾收集方式为并行收集。JDK6.0支持对年老代并行收集。 java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:MaxGCPauseMillis=100 : 设置每次年轻代垃圾回收的最长时间,如果无法满足此时间,JVM会自动调整年轻代大小,以满足此值。 java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseParallelGC -XX:MaxGCPauseMillis=100 -XX:+UseAdaptiveSizePolicy -XX:+UseAdaptiveSizePolicy :设置此选项后,并行收集器会自动选择年轻代区大小和相应的Survivor区比例,以达到目标系统规定的最低相应时间或者收集频率等,此值建议使用并行收集器时,一直打开。

响应时间优先 的并发收集器

如上文所述,并发收集器主要是保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。 典型配置 : java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:ParallelGCThreads=20 -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:+UseConcMarkSweepGC :设置年老代为并发收集。测试中配置这个以后,-XX:NewRatio=4的配置失效了,原因不明。所以,此时年轻代大小最好用-Xmn设置。 -XX:+UseParNewGC :设置年轻代为并行收集。可与CMS收集同时使用。JDK5.0以上,JVM会根据系统配置自行设置,所以无需再设置此值。 java -Xmx3550m -Xms3550m -Xmn2g -Xss128k -XX:+UseConcMarkSweepGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection -XX:CMSFullGCsBeforeCompaction :由于并发收集器不对内存空间进行压缩、整理,所以运行一段时间以后会产生“碎片”,使得运行效率降低。此值设置运行多少次GC以后对内存空间进行压缩、整理。 -XX:+UseCMSCompactAtFullCollection :打开对年老代的压缩。可能会影响性能,但是可以消除碎片

另外在选择了垃圾收集器组合之后,还要配置一些辅助参数,以保证收集器可以更好的工作。关于这些参数,请在http://kenwublog.com/docs/java6-jvm-options-chinese-edition.htm中查询其意义和用法,如:

ParNew收集器

你可能需要配置4个参数: -XX:SurvivorRatio, -XX:PretenureSizeThreshold, -XX:+HandlePromotionFailure,-XX:MaxTenuringThreshold;

Parallel Scavenge收集器(并行收集器)

你可能需要配置的参数: -XX:MaxGCPauseMillis,-XX:GCTimeRatio, -XX:+UseAdaptiveSizePolicy -XX:ParallelGCThreads=n;

CMS收集器(并发收集器)

你可能需要配置的参数: -XX:CMSInitiatingOccupancyFraction, -XX:+CMSIncrementalMode, -XX:ParallelGCThreads=n, -XX:+UseCMSCompactAtFullCollection, -XX:CMSFullGCsBeforeCompaction;

启动内存分配

用-Xmn,-Xmx,-Xms,-Xss,-XX:NewSize,-XX:MaxNewSize,-XX:MaxPermSize,-XX:PermSize,-XX:SurvivorRatio,-XX:PretenureSizeThreshold,-XX:MaxTenuringThreshold就基本可以配置内存启动时的分配情况 建议 1. -XX:PermSize尽量比-XX:MaxPermSize小,-XX:MaxPermSize>= 2 * -XX:PermSize, -XX:PermSize> 64m,一般对于4G内存的机器,-XX:MaxPermSize不会超过256m; 2. -Xms = -Xmx(线上Server模式),以防止抖动,大小受操作系统和内存大小限制,如果是32位系统,则一般-Xms设置为1g-2g(假设有4g内存),在64位系统上,没有限制,不过一般为机器最大内存的一半左右; 3. -Xmn,在开发环境下,可以用-XX:NewSize和-XX:MaxNewSize来设置新生代的大小(-XX:NewSize (total occupancy1), secs] -> (total occupancy3), secs]

image_1b4fncn4n1eer1kglv4lvuiuk22q.png-7.5kB 选项的意义是: time:执行GC的时间,需要添加-XX:+PrintGCDateStamps参数才有; collector:minor gc使用的收集器的名字。 starting occupancy1:GC执行前新生代空间大小。 ending occupancy1:GC执行后新生代空间大小。 total occupancy1:新生代总大小 pause time1:因为执行minor GC,Java应用暂停的时间。 starting occupancy3:GC执行前堆区域总大小 ending occupancy3:GC执行后堆区域总大小 total occupancy3:堆区总大小 pause time3:Java应用由于执行堆空间GC(包括full GC)而停止的时间。

可视化工具

监控和分析GC也有一些可视化工具,比较常见的有JConsole和VisualVM,有兴趣的可以看看下面的文章,在此不再赘述: http://blog.csdn.net/java2000_wl/article/details/8049707

调优方法

原则: 1. 多数的Java应用不需要在服务器上进行GC优化; 2. 多数导致GC问题的Java应用,都不是因为我们参数设置错误,而是代码问题; 3. 在应用上线之前,先考虑将机器的JVM参数设置到最优(最适合); 4. 减少创建对象的数量; 5. 减少使用全局变量和大对象; 6. GC优化是到最后不得已才采用的手段; 7. 在实际使用中,分析GC情况优化代码比优化GC参数要多得多;

GC优化的目的有两个(http://www.360doc.com/content/13/0305/10/15643_269388816.shtml): 1. 将转移到老年代的对象数量降低到最小; 2. 减少full GC的执行时间;

需要做的事情有:

减少使用全局变量和大对象; 调整新生代的大小到最合适; 设置老年代的大小为最合适; 选择合适的GC收集器;

调优般步骤为:

监控GC的状态

使用各种JVM工具,查看当前日志,分析当前JVM参数设置,并且分析当前堆内存快照和gc日志,根据实际的各区域内存划分和GC执行时间,觉得是否进行优化;

分析结果,判断是否需要优化

如果各项参数设置合理,系统没有超时日志出现,GC频率不高,GC耗时不高,那么没有必要进行GC优化;如果GC时间超过1-3秒,或者频繁GC,则必须优化; 注:如果满足下面的指标,则一般不需要进行GC:

Minor GC执行时间不到50ms; Minor GC执行不频繁,约10秒一次; Full GC执行时间不到1s; Full GC执行频率不算频繁,不低于10分钟1次; 调整GC类型和内存分配

如果内存分配过大或过小,或者采用的GC收集器比较慢,则应该优先调整这些参数,并且先找1台或几台机器进行beta,然后比较优化过的机器和没有优化的机器的性能对比,并有针对性的做出最后选择;

不断的分析和调整

通过不断的试验和试错,分析并找到最合适的参数

全面应用参数

如果找到了最合适的参数,则将这些参数应用到所有服务器,并进行后续跟踪。

调优实例实例1:

笔者昨日发现部分开发测试机器出现异常:java.lang.OutOfMemoryError: GC overhead limit exceeded,这个异常代表:GC为了释放很小的空间却耗费了太多的时间,其原因一般有两个: 1,堆太小, 2,有死循环或大对象; 笔者首先排除了第2个原因,因为这个应用同时是在线上运行的,如果有问题,早就挂了。所以怀疑是这台机器中堆设置太小; 使用ps -ef |grep “java”查看,发现: image_1b4fnplct1gkd14opg23ndgsqd37.png-30.6kB 该应用的堆区设置只有768m,而机器内存有2g,机器上只跑这一个java应用,没有其他需要占用内存的地方。另外,这个应用比较大,需要占用的内存也比较多; 笔者通过上面的情况判断,只需要改变堆中各区域的大小设置即可,于是改成下面的情况: image_1b4fnq8k01pct1notkgf16ej9tb3k.png-39.1kB 跟踪运行情况发现,相关异常没有再出现;

实例2:

(http://www.360doc.com/content/13/0305/10/15643_269388816.shtml) 一个服务系统,经常出现卡顿,分析原因,发现Full GC时间太长:

jstat -gcutil:S0 S1 E O P YGC YGCT FGC FGCT GCT12.16 0.00 5.18 63.78 20.32 54 2.047 5 6.946 8.993

分析上面的数据,发现Young GC执行了54次,耗时2.047秒,每次Young GC耗时37ms,在正常范围,而Full GC执行了5次,耗时6.946秒,每次平均1.389s,数据显示出来的问题是:Full GC耗时较长,分析该系统的是指发现,NewRatio=9,也就是说,新生代和老生代大小之比为1:9,这就是问题的原因: 1,新生代太小,导致对象提前进入老年代,触发老年代发生Full GC; 2,老年代较大,进行Full GC时耗时较大; 优化的方法是调整NewRatio的值,调整到4,发现Full GC没有再发生,只有Young GC在执行。这就是把对象控制在新生代就清理掉,没有进入老年代(这种做法对一些应用是很有用的,但并不是对所有应用都要这么做)

实例3:

一应用在性能测试过程中,发现内存占用率很高,Full GC频繁,使用sudo -u admin -H jmap -dump:format=b,file=文件名.hprof pid 来dump内存,生成dump文件,并使用Eclipse下的mat差距进行分析,发现: image_1b4fofdo775cnrr1c5s1ebpfob41.png-115.8kB 从图中可以看出,这个线程存在问题,队列LinkedBlockingQueue所引用的大量对象并未释放,导致整个线程占用内存高达378m,此时通知开发人员进行代码优化,将相关对象释放掉即可。

实例4:CMSInitiatingOccupancyFraction值与Xmn的关系公式

上面介绍了promontion faild产生的原因是EDEN空间不足的情况下将EDEN与From survivor中的存活对象存入To survivor区时,To survivor区的空间不足,再次晋升到old gen区,而old gen区内存也不够的情况下产生了promontion faild从而导致full gc.那可以推断出:eden+from survivor < old gen区剩余内存时,不会出现promontion faild的情况,即: (Xmx-Xmn)*(1-CMSInitiatingOccupancyFraction/100)>=(Xmn-Xmn/(SurvivorRatior+2)) 进而推断出:

CMSInitiatingOccupancyFraction verify -> prepare -> resove -> init

什么是新生代收集担保?

在一次理想化的minor gc中,活跃对象会从Eden和First Survivor中被复制到Second Survivor。 然而,Second Survivor不一定能容纳所有的活跃对象。

为了确保minor gc能够顺利完成,需要在年老代中保留一块足以容纳所有活跃对象的内存空间。 这个预留的操作,被称之为新生代收集担保(New Generation Guarantee)。当预留操作无法完成时,就会触发major gc(full gc)。

为什么要关闭新生代收集担保? 因为在年老代中预留的空间大小,是无法精确计算的。

为了确保极端情况的发生,GC参考了最坏情况下的新生代内存占用,即Eden+First Survivor。

这种策略无疑是在浪费年老代内存,并从时序角度看,可能提前触发Full GC。

为了避免如上情况的发生,JVM允许开发者关闭新生代收集担保。

在开启本选项后,minotr gc将不再提供新生代收集担保,而是在出现survior或年老代不够用时,抛出promotion failed异常。

自旋锁优化原理

大家知道,Java的多线程安全是基于Lock机制实现的,而Lock的性能往往不如人意。 原因是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖操作系统互斥(mutex)来实现的。 互斥是一种会导致线程挂起,并在较短的时间内又需要重新调度回原线程的,较为消耗资源的操作。

为了避免进入OS互斥,Java6的开发者们提出了自旋锁优化方法。

自旋锁优化的原理是在线程进入OS互斥前,通过CAS自旋一定的次数来检测锁的释放。

如果在自旋次数未达到预订值前,发现锁已被释放,则会立即持有该锁。 CAS检测锁的原理详见: http://www.bt285.cn/sejishikong/ 关联选项: -XX:PreBlockSpin=10

什么是预估堆内存?

预估堆内存是堆大小动态调控的重要选项之一。 堆内存预估最大值一定小于或等于固定最大值(-Xmx指定的数值)。 前者会根据使用情况动态调大或缩小,以提高GC回收的效率。



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有