JVM参数主要有⼏种分类
前言
JVM参数解析与调优
JVM 全称 Java Virtual Machine,Java程序编译之后生成的.class文件就是交由JVM执行,由JVM将.class文件内容翻译成对于系统可识别的机器语言,这就是Java之所以能一次编译,到处运行。
JVM参数
JVM堆内存
整个堆大小 = 年轻代(Young Generation) + 年老代(Old Generation) + 持久代(Perm Area) JVM堆内存用与new创建的对象和数组,栈内存则用于分配基础类型变量和对象的引用,当程序运行到作用域外时,栈内引用将被释放,而失去了引用地址的堆内存里的对象则变为了垃圾,在未知时间被GC回收释放内存;
堆内存构成图:
-
-Xms 初始堆大小 默认物理内存的1/64(小于1GB)空余堆大小小于40%时,JVM就会增大堆直到-Xmx的最大限制
-
-Xmx 最大堆大小 默认物理内存的1/4(小于1GB)空余堆大小大于70%时,JVM就会减少堆直到-Xms的最小限制
我们可以通过将“-Xms”和“-Xmx”设置为相同大小来获得一个固定大小的堆内存。 -Xms和-Xmx实际上是-XX:InitialHeapSize和-XX:MaxHeapSize的缩写。我们也可以直接使用这两个参数,它们所起得效果是一样的
-
-Xmn 年轻代大小
-
-XX:NewSize 设置年轻代初始大小
-
-XX:MaxNewSize 年轻代最大值
-
-XX:PermSize 设置持久代初始值
-
-XX:MaxPermSize 设置持久代最大值
-
-Xss 每个线程堆栈大小 JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K,这个参数对影响比较大,需经过严格测试后进行调整
-
-XX:NewRatio 年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代) -XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5,Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。
-
-XX:SurvivorRatio Eden区与Survivor区的大小比值 设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
-
-XX:+HeapDumpOnOutOfMemoryError and -XX:HeapDumpPath 当我们没法为-Xmx(最大堆内存)设置一个合适的大小,那么就有可能面临内存溢出(OutOfMemoryError)的风险,这可能是我们使用JVM时面临的最可怕的猛兽之一导致内存溢出的根本原因需要仔细的定位。通常来说,分析堆内存快照(Heap Dump)是一个很好的定位手段,如果发生内存溢出时没有生成内存快照,特别是对于那种JVM已经崩溃或者错误只出现在顺利运行了数小时甚至数天的生产系统上时,将很难去分析崩溃问题。
幸运的是,我们可以通过设置 -XX:+HeapDumpOnOutOfMemoryError 让JVM在发生内存溢出时自动的生成堆内存快照。有了这个参数,当我们不得不面对内存溢出异常的时候会节约大量的时间。默认情况下,堆内存快照会保存在JVM的启动目录下名为java_pid.hprof 的文件里(在这里就是JVM进程的进程号)。也可以通过设置-XX:HeapDumpPath=来改变默认的堆内存快照生成路径,可以是相对或者绝对路径。
JVM调试工具
jps (JVM Process Status Tool)
--- 输出jvm运行的java进程状态信息
命令格式:
jps [options] [hostId]
hostId缺省值为当前主机 命令指令包括:
-q 不输出类名、Jar名和传入main方法的参数 -m 输出传入main方法的参数 -l 输出main类或Jar的全限名 -v 输出传入JVM的参数
使用如下:
[root@localhost ~]# [root@localhost ~]# jps -m -l 28353 uyun.bat.monitor.impl.Startup 22852 uyun.bat.datastore.Startup 25799 uyun.bat.event.impl.Startup 19976 /opt/uyun/platform/jetty/start.jar 29320 uyun.bat.report.Startup
jstack
--- 输出具体java进程内线程堆栈信息
jstask应该是比较常用的JVM调试工具,命令格式:
jstack [option] [pid]
命令指令:
-l long listings 打印线程锁信息,发生死锁时可以使用该参数调试 -m mixed mode 不仅输出java堆栈信息,还会输出C/C++堆栈信息
实际应用例子: 查看进程最占CPU的线程堆栈信息
- ps -ef | grep 查找对应进程,或者top命令查看系统使用信息,找出消耗最大的进程,我这里使用的是top命令:
top - 07:38:01 up 3 days, 6:20, 5 users, load average: 15.72, 15.02, 14.14 Tasks: 148 total, 7 running, 141 sleeping, 0 stopped, 0 zombie %Cpu(s): 71.2 us, 26.1 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 2.7 si, 0.0 st KiB Mem: 20397888 total, 20124388 used, 273500 free, 0 buffers KiB Swap: 1081340 total, 1081340 used, 0 free. 2163376 cached Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 24152 es 20 0 3323892 492160 28496 S 35.7 2.4 1613:51 java 3247 mysql 20 0 1152924 297932 5836 S 9.9 1.5 418:51.47 mysqld 20009 root 20 0 3688420 1.563g 13132 S 7.9 8.0 653:51.83 java 22852 root 20 0 3450392 546480 12828 S 7.6 2.7 322:33.85 java 5779 root 20 0 3652656 1.114g 4976 S 4.3 5.7 109:57.89 java 28353 root 20 0 3624988 337680 12824 S 3.3 1.7 125:49.11 java 268 root 0 -20 0 0 0 S 2.3 0.0 43:33.35 kworker/0:1H 11539 root 20 0 369916 14108 4020 R 2.0 0.1 0:00.06 python 25799 root 20 0 3356336 475416 12832 S 1.7 2.3 64:56.00 java 1544 root 20 0 247448 27916 1144 S 1.3 0.1 56:24.67 redis-server 11540 root 20 0 131528 5048 3880 S 1.3 0.0 0:00.04 sshd 21497 root 20 0 3306144 313020 12712 S 1.0 1.5 20:59.73 java 1 root 20 0 133816 6772 2084 S 0.7 0.0 40:38.49 systemd
经过top命令查找,最占用CPU的是一个Java进程,进程id为24152,占用内存达到35.7%,经过ps -ef|grep pid查看,这个进程是ElasticSearch进程
2.第二步我们需要查找该进程内最耗费CPU的线程,可以使用ps -Lfp pid, ps -mp pid -o THREAD, top -Hp pid,这里我们用第三个命令,top -Hp 24152
top - 07:44:20 up 3 days, 6:27, 5 users, load average: 19.72, 15.50, 14.42 Threads: 40 total, 1 running, 39 sleeping, 0 stopped, 0 zombie %Cpu(s): 64.3 us, 32.0 sy, 0.0 ni, 0.0 id, 0.0 wa, 0.0 hi, 3.7 si, 0.0 st KiB Mem: 20397888 total, 19894260 used, 503628 free, 0 buffers KiB Swap: 1081340 total, 1081340 used, 0 free. 1994824 cached Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 24937 es 20 0 3319268 485312 21512 R 23.0 2.4 748:12.71 java 24953 es 20 0 3319268 485312 21512 S 4.3 2.4 151:27.67 java 24157 es 20 0 3319268 485312 21512 S 3.0 2.4 142:46.82 java 24459 es 20 0 3319268 485312 21512 S 0.3 2.4 1:58.92 java 24876 es 20 0 3319268 485312 21512 S 0.3 2.4 16:58.66 java 24152 es 20 0 3319268 485312 21512 S 0.0 2.4 0:00.00 java 24154 es 20 0 3319268 485312 21512 S 0.0 2.4 0:02.02 java 24155 es 20 0 3319268 485312 21512 S 0.0 2.4 0:00.00 java 24156 es 20 0 3319268 485312 21512 S 0.0 2.4 42:25.76 java 24158 es 20 0 3319268 485312 21512 S 0.0 2.4 0:31.47 java
这里我们看到最耗费性能的线程pid为24937
- 第三步先获取线程id 24937的十六进制值
[root@localhost ~]# printf "%x\n" 24937 6169
接着使用jstack来输出线程id 24937的堆栈信息,根据线程id的十六进制值grep
jstack 24152 | grep 6169
执行jstack命令后系统并没有返回jvm信息,而是给出一个报错信息:
Unable to open socket file: target process not responding or HotSpot VM not loaded
之所以会报这个找不到文件的错误,首先我们得知道jvm运行时会生成一个hsperfdata_$user的目录,Linux下默认是在/tmp,我们也可以通过配置jvm启动参数-Djava.io.tmpdir来指定进程号信息临时文件的存放位置,检查过之后确认 /tmp下有生成目录hsperfdata_es
[root@localhost hsperfdata_es]# pwd /tmp/hsperfdata_es [root@localhost hsperfdata_es]# ls 24152 [root@localhost hsperfdata_es]#
那之所以jstack会报错找不到文件,原因是ElasticSearch进程是使用es用户启动的,而我们登录的是root账号,因此访问不到这个文件,切换用户为es后再次使用jstack打印线程堆栈信息:
[root@localhost hsperfdata_es]# su es [es@localhost hsperfdata_es]$ jstack 24152 | grep 6169 "elasticsearch[Grenade][bulk][T#1]" #49 daemon prio=5 os_prio=0 tid=0x00007f78440b2000 nid=0x6169 runnable [0x00007f7840fa1000] [es@localhost hsperfdata_es]$
打印出该线程信息,显示该线程是runnable正常运行的就绪状态,经查看详细堆栈信息,应该是es内部创建分片索引的进程,因此占用比较多性能,在对自己环境进程正式排查的时候,可以多进行几次打印,对比多次之间的线程运行情况,正常情况下由于程序运行速度是非常快的,如果发现多次打印对于线程都一直处于同一状态如Runnable,而且堆栈信息也卡在相同的几处地方,就可以考虑看一下对应代码是不是存在死循环或者方法调用缓慢的问题了。
jmap jhat
--- jmap 输出堆内存使用情况 --- jhat java堆内存分析工具
jmap和jhat经常在一起被使用。
jmap -heap pid
用于查看进程堆内存使用情况,包括堆配置参数和各代中堆内存使用情况,如:
whale.server01:/root# jmap -heap 17047 Attaching to process ID 17047, please wait... Debugger attached successfully. Server compiler detected. JVM version is 25.77-b03 using thread-local object allocation. Parallel GC with 4 thread(s) Heap Configuration: MinHeapFreeRatio = 0 MaxHeapFreeRatio = 100 MaxHeapSize = 1073741824 (1024.0MB) NewSize = 175112192 (167.0MB) MaxNewSize = 357564416 (341.0MB) OldSize = 351272960 (335.0MB) NewRatio = 2 SurvivorRatio = 8 MetaspaceSize = 21807104 (20.796875MB) CompressedClassSpaceSize = 1073741824 (1024.0MB) MaxMetaspaceSize = 134217728 (128.0MB) G1HeapRegionSize = 0 (0.0MB) Heap Usage: PS Young Generation Eden Space: capacity = 70254592 (67.0MB) used = 22570248 (21.52466583251953MB) free = 47684344 (45.47533416748047MB) 32.126366914208255% used From Space: capacity = 9961472 (9.5MB) used = 6859688 (6.541908264160156MB) free = 3101784 (2.9580917358398438MB) 68.86219225431743% used To Space: capacity = 10485760 (10.0MB) used = 0 (0.0MB) free = 10485760 (10.0MB) 0.0% used PS Old Generation capacity = 184025088 (175.5MB) used = 99082056 (94.49201202392578MB) free = 84943032 (81.00798797607422MB) 53.841602292835205% used 24334 interned Strings occupying 2508568 bytes.
使用jmap -histo[:live] pid查看堆内存中的对象数目、大小统计直方图,如果带上live则只统计活对象,如下:
whale.server01:/root# jmap -histo:live 17047 | more num #instances #bytes class name ---------------------------------------------- 1: 77521 8038136 [C 2: 1056 2890752 [J 3: 6362 2595656 [B 4: 5812 1968312 [I 5: 76614 1838736 java.lang.String 6: 19709 1734392 java.lang.reflect.Method 7: 18318 1268024 [Ljava.lang.Object; 8: 10179 1136280 java.lang.Class 9: 33025 1056800 java.util.concurrent.ConcurrentHashMap$Node 10: 12388 594624 org.aspectj.weaver.reflect.ShadowMatchImpl 11: 16901 540832 java.util.HashMap$Node 12: 12304 492160 java.util.LinkedHashMap$Entry 13: 12388 396416 org.aspectj.weaver.patterns.ExposedState 14: 2633 352464 [Ljava.util.HashMap$Node; 15: 11008 352256 java.util.Hashtable$Entry 16: 436 330440 [Ljava.util.concurrent.ConcurrentHashMap$Node; 17: 13383 321192 java.util.ArrayList 18: 9319 298208 java.lang.ref.WeakReference 19: 741 278616 java.lang.Thread 20: 17352 277632 java.lang.Object 21: 5707 228280 java.lang.ref.SoftReference 22: 3612 173376 java.util.HashMap 23: 302 164288 rx.internal.util.unsafe.SpscArrayQueue 24: 5104 163328 java.util.concurrent.locks.ReentrantLock$NonfairSync 25: 2872 160832 java.util.LinkedHashMap 26: 4784 153088 java.lang.ThreadLocal$ThreadLocalMap$Entry 27: 1828 146240 java.lang.reflect.Constructor 28: 1473 139856 [Ljava.lang.ThreadLocal$ThreadLocalMap$Entry; 29: 5152 123648 java.beans.MethodRef 30: 3831 119752 [Z 31: 5550 118632 [Ljava.lang.Class; 32: 2003 112168 java.security.Provider$Service 33: 464 107616 [Ljava.util.Hashtable$Entry;
参考文献: 传送门 传送门2(JVM性能调优监控工具jps、jstack、jmap、jhat、jstat使用详解) 传送门3(jstat命令使用)
---------------------
个性 签名:真正的学习不是记住知识,而是学会如何提出问题,研究问题,解决问题。
如果觉得这篇文章对你有小小的帮助的话,记得在下方“关注”哦,博主在此感谢!