顺晟科技
2021-06-16 10:42:47
181
前几天试压生产前环境,发现TPS不稳定。因为是重构系统,所以说原系统在高并发的时候完全没有问题,结果重构的系统在被几十个并发压缩的时候就不稳定了。虽然测试的同事什么都没说,但是感觉被打了一巴掌。
所以各种故障排除,首先想到的是JVM参数,所以优化一下,希望能得到好的结果。虽然后来证明不稳定是因为LoadRunner安装的压力测试服务器不稳定造成的,和我的系统无关,但是也有记录,一个是做备份,一个是给别人做参考。
写在前面
由于Hotspot JDK提供的参数默认值在不同版本之间不断变化,因此这些参数也会相互影响。此外,不同的服务器配置可能会影响最终结果。所以,不要迷信一篇网上的文章(包括这篇)中的参数配置。所有配置都需要自己测试才能使用。鉴于JVM参数的默认值不断变化,可以使用-xx3360 printflagsfinal打印当前环境下JVM参数的默认值,如Java-xx3360 print flags final-version,或者使用Java[生产环境参数]-xx3360 print flags final-version | grep[待验证参数]查看具体参数数据。
以下是8G服务器的参数。JDK版本信息如下:
java版本' 1.8.0_73 '
Java TM SE运行时环境(构建1.8.0_73-b02)
Java HotSpot(TM) 64位服务器虚拟机(构建25.73-b02,混合模式)
一个
2
三
堆设置
堆内存设置应该算是一个Java程徐苑的基本素养,至少修改了Xms、Xmx、Xmn三个参数。但是2G堆大小的JVM总共可以占用多少内存呢?
堆内存线程数*线程堆栈生成二进制代码堆外内存
2G 1000 * 1M 256m 48/240m(~ 2G)=5.5G(3.5G)
-堆内存:存储Java对象,默认为物理内存的1/64
-线程堆栈:存储局部变量(原子类型、引用)和其他,默认值为1M
-生成:存储类定义和常量池,注意JDK7/8之间的区别
-二进制代码:JDK7和8,打开多层编译时默认值不同,从48到240M。
-堆外内存:由Netty、堆外缓存等使用。默认的更大值大约是堆内存的大小
也就是说,如果堆内存设置为2G,那么1000个线程的JVM可能需要占用5.5G,考虑到系统占用、IO占用等各种情况,一个8G的服务器会启动一个服务。当然,如果线程数量少,并发性不高,压力也不高,还是可以启动多个线程,减少堆内存。
-Xms2g和-Xmx2g:堆内存大小,个是最小的堆内存,第二个是更大的堆内存,比较合适的值是2-4g,大一点一定要考虑GC时间
-Xmn1g或(-xx3360new size=1g和-xx3360new size=1g)或-xx3360new ratio=1:设置新一代的大小。JDK默认新一代占堆内存大小的1/3,即-XX:NewRatio=2。这里是集合1g,也就是-XX:NewRatio=1。可以根据自己的需要来设置。
- XX:MetaspaceSize=128M和- XX:MaxMetaspaceSize=512M。JDK8的不朽一代几乎可以用完机器所有的内存。为了保护服务器不会因为内存占用过多而无法连接,需要设置初始值128m,更大值512m来保护。
-xx: surviorratio:新生代各生存区大小默认为8,即新生代的1/10,1/(surviorratio 2)。有些人喜欢设置小点为新生代留点,但要避免因为生存面积太小容纳不下临时物体而被提升到老一辈,或者从GC Log看实际情况。
-Xss256k:堆外,线程占用堆栈内存,每个线程默认值为1M。存储方法调出参数、局部变量、标量替换后的局部变量等的栈。有些人喜欢设置小点来节省内存,打开更多的线程。不过,反正也没必要把内存设置得太小。有些人喜欢把它设置的大一些,特别是有JSON解析之类的递归调用的时候。
-XX:MaxDirectMemorySize:堆外内存/直接内存的大小,也就是堆内存M的大小
-xx: reserved codecachesize:JIT编译的二进制代码的存储区,满了以后就不编译了。默认情况下,240M是多层编译的。你可以看到JMX代码缓存的大小。
气相色谱设置
目前主流GC是CMS和G1,有大神建议以8G为界。据说JDK 9默认为G1。因为应用设置的内存比较小,所以选择CMS收集器。以下参数也是针对CMS采集器的,然后如果需要,补充G1采集器的参数。
CMS设置
-xx: useconfmarkswepgc:启用CMS垃圾收集器
-xx3360 CMS initializing occupancy=80和-xx: usecms initializing occupancy only:这两个参数需要一起使用,否则个参数的75只是一个参考值,JVM会重新计算GC时间。
-xx: maxteningthreshold=15:受试者在Survivor地区的youngcc生存了多少次,然后晋升到老一辈,默认为15。年轻的GC是应用暂停的更大来源,新一代GC后存活对象的数量直接影响暂停时间。所以,如果你清楚的知道Young GC的执行频率,以及应用中大多数临时对象的最长生命周期,就可以把它设置的更短一些,这样就可以把不是临时对象的新生代中的长期对象快速提升到老一代。
-XX:-DisableExplicitGC:允许使用System.gc()主动调用GC。这里需要说明的是,一些JVM优化建议是设置-XX :-disablexplicitgc,关闭System.gc()的手动调用。这是因为System.gc()会触发Full GC,频繁的Full GC会严重影响性能。然而,许多NIO框架,如Netty,使用堆外内存,如果没有Full GC,这些内存是无法回收的。如果不主动调用System.gc(),需要等到JVM自己触发Full GC,这可能会导致长时间的暂停(STW),增加机器负载。所以不可能完全禁止System.gc()并缩短Full GC的时间,所以使用-xx: explicitgcinvokescurrent或-xx: explicitgcinvokescurrentdunloadsclasses选项,使用CMS收集器触发Full GC。这两个选项需要与-xx: useconmarkswepgc一起使用。
-xx: explicitgcinvokescurrent:使用System.gc()时,触发CMS GC而不是Full GC。默认情况下不打开,只有在使用-xx: useconmarkswepgc选项时才能打开此选项。
-xx: explicitgcinvokesconcurrentand nloadsclasses:使用System.gc()时,CMS中还包含代。只有在使用-xx: useconmarkswepgc选项时,才能打开此选项。
-XX:并行处理启用:默认值为false。引用对象,比如WeakReference,是并行处理的,除非GC日志中有一个引用处理时间较长的日志,否则效果不会明显。
-xx: savegeberforFull GC:在full GC之前执行youngcc一次。
-XX: UseGCOverheadLimit:限制GC的运行时间。如果GC时间太长,抛出OOM。
-XX: UseParallelGC:设置并行垃圾收集器
-xx:useparallelodgc:将旧版本设置为使用并行垃圾收集器
-XX :-使用串行垃圾收集器:关闭串行垃圾收集器
-XB :cm sparlalilinialialmarkenabled和-XB :cm sparlalileremarkenaled:减少标记暂停
-xx: cmsavegbeforeremark:默认关闭。在CMS备注之前,执行一次minor GC来清除新一代,这样从老一代引用的新一代对象数量会更少,全世界停止CMS备注的阶段也会更短。如果看到GC日志中的备注期过长,可以打开此项看看有没有效果;否则,不要打开它,因为YGC已经被添加了很多次。
-XX:CMSWaitDuration=10000:设置垃圾收集的更大时间间隔,默认为2000。
-xx: cmsslasunloadingenabled:在CMS中清理生成的过期类,无需等待Full GC,JDK7默认关闭,JDK8打开。看你自己的情况,比如运行Groovy之类的动态语言脚本来生成大量的临时类。它会增加CMS备注的暂停时间,所以如果不经常加载新类,更好保持此参数打开。
气相色谱日志
气相色谱过程可以通过气相色谱日志提供优化依据。
-XX:打印详细信息:启用气相色谱日志打印功能
-Xloggc:/path/to/gc.log:指定gc日志位置
-XX: PrintHeapAtGC:打印GC前后的详细堆栈信息
-XX:打印日期戳:打印可读的日期而不是时间戳
-xx3360 printgapplicationstopdime:打印所有导致JVM暂停的时间。如果发现一些不知道的暂停,暂时加-xx3360 printssafeppointstatisticcount-xx3360 printssafeppointstatisticcount=1查找原因。
-xx3360 pringcapplication concurrent time:打印JVM在两次暂停之间的正常运行时间,与-xx: pringcapplicationtopedtime一起使用效果更好。
-xx: printeningdistribution:在每次小的垃圾收集后检查新生命周期的阈值
-XX: UseGCLogFileRotation和-XX : numberrogclogfiles=10和-xx-XX : gclogfilesize=10M:GC:GC日志在重新启动后将被清空,但是如果一个应用程序长时间不重新启动,GC日志将增加,所以添加这三个参数意味着GC日志被滚动到文件中,但是如果重新启动,名称可能会混淆。
-xx: printflstatistics=1:打印每次垃圾收集前后的内存碎片统计
其他参数设置
-ea:启用断言。这个没什么好说的。可以选择启用,也可以不启用。没有太大区别。按照自己的系统。
-XX: UseThreadPriorities:启用线程优先级,主要是因为我们可以给周期性任务赋予较低的优先级,避免干扰客户端工作。在我当前的环境中,它是默认启用的。
-xx: threadpriority policy=42:允许降低线程优先级
-xx: headumpponout of memory error:内存溢出是堆转储
-xx: headumppath=/path/to/Java _ PID . hprof:此参数与-xx: headumpponoutofmemory error配合使用,在设置堆转储时设置内容输出文件。
-xx: error file=/path/to/hs _ err _ PID . log:指定致命错误日志位置。一般在JVM出现致命错误时会输出一个类似hs_err_pid.log的文件。默认文件在工作目录中(如果没有权限,请尝试在/tmp中创建)。不过还是自己指定位置比较好,方便收藏和查找,避免丢失。
-xx: stringtablesize=1000003:指定字符串常量池大小,默认值为60013。对Java有一点常识的人都应该知道,字符串是常量,创建后不能修改。这些常量所在的地方叫做字符串常量池。如果您自己的系统中有许多字符串操作,并且这些字符串值相对固定,则可以在允许的情况下适当调整池的大小。
-XX: AlwaysPreTouch:启动时刷掉参数定义的所有内存。使用此参数可能会降低启动速度,但在以后的内存使用中会更快。可以保证内存页面的连续分配,在新一代升级时不会因为申请内存页面而延长GC暂停。通常只有内存大于32G才会有感觉。
-XX:-usebiasedlinking:禁用偏置锁定(在高并发环境(即非多线程、高并发应用)下禁用偏置锁定可以带来一定的性能优化)
-xx3360autoboxcachemax=20000:增加数字对象自动装箱的范围。JDK默认为-128 ~ 127的int和long。如果超出范围,将立即创建对象。因此,增加范围可以提高性能,但也需要测试。
-xx:-omitstacktrayinfastthrow:不要忽略重复异常的栈,这是JDK的优化,大量重复的JDK异常将不再打印它们的StackTrace。但是,如果系统长时间不重启,如果在同一个地方运行N次,结果会被JDK忽略。如果查看日志的时候没有看到具体的StackTrace,怎么调试?所以还是关掉比较好。
-XX: PerfDisableSharedMem:启用标准内存使用。JVM控制分为标准内存或共享内存,区别在于一个在JVM内存中,一个是生成/tmp/hsperfdata_{userid}/{pid}文件,存储统计数据,通过mmap映射到内存中,其他进程可以通过文件访问内容。有了这个参数,可以禁止JVM在文件中写入统计数据,代价是不能再使用jps和jstat命令,只能通过jmx获取数据。但是在故障排除中,这些小工具,比如jps和jstat,都是非常好用的,而且比jmx这种重的东西好用多了,需要自己做选择。下面是GC暂停的一个例子。
-djava . net . preferip v4 stack=true:此参数属于网络问题,可以根据需要进行设置。在一些支持ipv6的机器中,可以通过inetaddress获得完整的机器名称。getlocalhost()。gethostname(),但是在ipv4机器中,通过此方法获得的机器名称可能不完整,因此可以通过此参数获得完整的机器名称。
大神举的例子
大神给出的以下例子可以借鉴,但建议在自己的环境中验证后使用。毕竟大神的环境和自己的不一样。
性能相关
-XX:-usebiasedlinking-XX:-usecounterdecade-XX: autobox cache max=20000
-xx3360 perfdisablesharedmem(可选)-xx: alwayspretouch-djava . security . EGD=file :/dev/。/urandom
取决于内存大小(JDK7)
-xms 4096m-xmx 4096m-xmn 2048m-Xx : max directmemorysize=4096m
-xx: permsize=128m-xx: max permsize=512m-xx: reserved codecachesize=240m
如果使用jdk8,请将-xx: permsize=128m-xx3360 max permsize=512m替换为-xx3360 max metascesize=128m-xx3360 max metascesize=512m。如前所述,这两组参数是为了安全,因此建议添加它们。
CMS GC相关
-Xx : useConcMarksweepgc-Xx : CMS initiationingccupincreaction=75
-XX: usecms initiationccupinconly-XX: maxteningringthreshold=6
-XX:显式调用并发-XX:并行启用
气相色谱对数相关
-Xloggc :/dev/shm/app-GC . log-XX : printgapplicationstoppedtime
-XX:打印日期戳-XX:打印详细信息
异常日志关联
-XX :-OmitStackTraceInFastThrow-XX : error file=$ { LOGDIR }/hs _ err _ % p . log
-XX : heapdumponotofmemoryerror-XX : heapdumppath=$ { LOGDIR }/
JMX相关
-Dcom . sun . management . JMX remote . PORT=$ { JMX _ PORT }-Dcom . sun . management . JMX remote
-Djava . RMI . server . hostname=127 . 0 . 0 . 1-Dcom . sun . management . JMX remote . authentic=false
-Dcom . sun . management . JMX remote . SSL=false
10
2022-04
10
2022-04
09
2022-04
09
2022-04
09
2022-04
16
2021-06