JVM优化之优化常用参数和工具
JVM優(yōu)化之優(yōu)化常用參數(shù)和工具
內(nèi)容提要
- jvm運(yùn)行參數(shù)和參數(shù)設(shè)置
- jvm 內(nèi)存模型
- 定位分析死鎖和內(nèi)存溢出
- 其他工具使用
為什么要優(yōu)化JVM
1.生產(chǎn)環(huán)境需要承載更多的并發(fā)要求,對(duì)底層的優(yōu)化能顯著提升性能,節(jié)約成本
2.測(cè)試和生產(chǎn)環(huán)境的不同可能導(dǎo)致我們無法實(shí)時(shí)了解具體性能問題,我們需要借助對(duì)JVM了解分析問題所在。
jvm運(yùn)行參數(shù)和參數(shù)設(shè)置
1.標(biāo)準(zhǔn)參數(shù)
由java -help檢索出來的所有參數(shù)成為標(biāo)準(zhǔn)參數(shù),未來發(fā)行版本中不會(huì)輕易修改,即使修改也會(huì)有官方通知
>java -help
-java [-options] class [args...] (執(zhí)行類)或 java [-options] -jar jarfile [args...] #(執(zhí)行 jar 文件)其中選項(xiàng)包括:
-D<名稱>=<值> #設(shè)置系統(tǒng)屬性
-version #輸出產(chǎn)品版本并退出
-showversion # 輸出產(chǎn)品版本并繼續(xù)
-? -help 輸出此幫助消息
-X #輸出非標(biāo)準(zhǔn)選項(xiàng)的幫助
-d32 #使用 32 位數(shù)據(jù)模型 (如果可用)
-d64 #使用 64 位數(shù)據(jù)模型 (如果可用)
-server #選擇 "server" VM 默認(rèn) VM 是 server,因?yàn)槟窃诜?wù)器類計(jì)算機(jī)上運(yùn)行。
-cp <目錄和 zip/jar 文件的類搜索路徑>
-classpath <目錄和 zip/jar 文件的類搜索路徑> #用 : 分隔的目錄, JAR 檔案和 ZIP 檔案列表, 用于搜索類文件。
###########################以上為非常重要的參數(shù) 希望開發(fā)者一定要記住###########################
-verbose:[class|gc|jni] #啟用詳細(xì)輸出
-version:<值> #警告: 此功能已過時(shí), 將在未來發(fā)行版中刪除。需要指定的版本才能運(yùn)行
-jre-restrict-search | -no-jre-restrict-search #警告: 此功能已過時(shí), 將在未來發(fā)行版中刪除。在版本搜索中包括/排除用戶專用 JRE
-ea[:<packagename>...|:<classname>]
-enableassertions[:<packagename>...|:<classname>]#按指定的粒度啟用斷言
-da[:<packagename>...|:<classname>]
-disableassertions[:<packagename>...|:<classname>] #禁用具有指定粒度的斷言
-esa | -enablesystemassertions # 啟用系統(tǒng)斷言
-dsa | -disablesystemassertions # 禁用系統(tǒng)斷言
-agentlib:<libname>[=<選項(xiàng)>] #加載本機(jī)代理庫(kù) <libname>, 例如 -agentlib:hprof另請(qǐng)參閱 -agentlib:jdwp=help 和 -agentlib:hprof=help
-agentpath:<pathname>[=<選項(xiàng)>] #按完整路徑名加載本機(jī)代理庫(kù)
-javaagent:<jarpath>[=<選項(xiàng)>] #加載 Java 編程語(yǔ)言代理, 請(qǐng)參閱 java.lang.instrument
-splash:<imagepath> # 使用指定的圖像顯示啟動(dòng)屏幕
使用Java代碼測(cè)試系統(tǒng)屬性
Properties properties = System.getProperties();
for (Map.Entry<Object, Object> objectObjectEntry : properties.entrySet()) {
System.out.printf("system prop : %s = %s\n",objectObjectEntry.getKey(),objectObjectEntry.getValue());
}
// java -Dmyname=huyiyu [類名] 之后在打印所有系統(tǒng)參數(shù)中找到myname=huyiyu
server和client參數(shù)
server: 顯式指定-server時(shí)設(shè)置,或者64位系統(tǒng)時(shí)設(shè)置,使用 parNew GC 1 啟動(dòng)慢運(yùn)行快
client:顯式指定-client時(shí)設(shè)置,或者64位系統(tǒng)時(shí)設(shè)置,使用default GC2啟動(dòng)快運(yùn)行慢
系統(tǒng)信息如下:
| windowsX86 | 其他X86 | 其他X86 Mem:>2GB >2CPU | X64 |
|---|---|---|---|
| client | client | server | server |
2.非標(biāo)準(zhǔn)參數(shù)
-Xmixed # 混合模式執(zhí)行 (默認(rèn))
-Xint #僅解釋模式執(zhí)行
-Xbatch #禁用后臺(tái)編譯
-Xms<size> #設(shè)置初始 Java 堆大小
-Xmx<size> #設(shè)置最大 Java 堆大小
-Xss<size> #設(shè)置 Java 線程堆棧大小
-XshowSettings #顯示所有設(shè)置并繼續(xù)
########################以上參數(shù)非常重要需要記住###############################
-XshowSettings:all#顯示所有設(shè)置并繼續(xù)
-XshowSettings:vm 顯示所有與 vm 相關(guān)的設(shè)置并繼續(xù)
-XshowSettings:properties #顯示所有屬性設(shè)置并繼續(xù)
-XshowSettings:locale#顯示所有與區(qū)域設(shè)置相關(guān)的設(shè)置并繼續(xù)
-Xbootclasspath: <用 : 分隔的目錄和 zip/jar 文件> #設(shè)置搜索路徑以引導(dǎo)類和資源
-Xbootclasspath/a:<用 : 分隔的目錄和 zip/jar 文件>#附加在引導(dǎo)類路徑末尾
-Xbootclasspath/p:<用 : 分隔的目錄和 zip/jar 文件>#置于引導(dǎo)類路徑之前
-Xdiag #顯示附加診斷消息
-Xnoclassgc #禁用類垃圾收集
-Xincgc #啟用增量垃圾收集
-Xloggc:<file> #將 GC 狀態(tài)記錄在文件中 (帶時(shí)間戳)
-Xprof #輸出 cpu 配置文件數(shù)據(jù)
-Xfuture #啟用最嚴(yán)格的檢查, 預(yù)期將來的默認(rèn)值
-Xrs #減少 Java/VM 對(duì)操作系統(tǒng)信號(hào)的使用 (請(qǐng)參閱文檔)
-Xcheck:jni #對(duì) JNI 函數(shù)執(zhí)行其他檢查
-Xshare:off #不嘗試使用共享類數(shù)據(jù)
-Xshare:auto #在可能的情況下使用共享類數(shù)據(jù) (默認(rèn))
-Xshare:on #要求使用共享類數(shù)據(jù), 否則將失敗。
#-X 選項(xiàng)是非標(biāo)準(zhǔn)選項(xiàng), 如有更改, 恕不另行通知。
設(shè)置編譯模式
| 類型 | 說明 | 示例 | 用法 | 備注 |
|---|---|---|---|---|
| int | 解釋模式 | -Xint | -Xint | 強(qiáng)制運(yùn)行字節(jié)碼,效率低 |
| comp | 編譯模式 | -Xcomp | -Xcomp | 編譯成native代碼帶來大程度的優(yōu)化 |
| mix | 混合模式 | -Xmix | -Xmix | 解釋和編譯混合進(jìn)行,由jvm決定使用任意推薦使用 |
設(shè)置初始堆內(nèi)存
| 類型 | 說明 | 示例 | 用法 | 備注 |
|---|---|---|---|---|
| Xms | 最小堆內(nèi)存 | -Xms<數(shù)字><單位> | -Xmx1024m | 設(shè)置最小堆內(nèi)存為1024MB |
| Xmx | 最大堆內(nèi)存 | -Xmx<數(shù)字><單位> | -Xms1024m | 設(shè)置最大堆內(nèi)存為1024MB |
| Xss | 初始堆內(nèi)存 | -Xmx<數(shù)字><單位> | -Xss1024m | 設(shè)置初始堆內(nèi)存為1024MB |
XX參數(shù)
XX參數(shù)也是非標(biāo)準(zhǔn)參數(shù),用于jvm調(diào)優(yōu)和debug操作,設(shè)置JVM有兩種模式分別如下
| 類型 | 用法 | 示例 | 備注 |
|---|---|---|---|
| boolean | -XX:+/-<name> | XX:+DisableExplicitGC | +:啟用 -:禁用 |
| 非boolean | :-XX:<name>=<value> | :-XX:NewRatio=1 |
# XX參數(shù)實(shí)戰(zhàn)
-XX:+PrintFlagsFinal -version
# =表示默認(rèn)參數(shù) :=表示重置生效參數(shù)
bool UseXMMForArrayCopy = true {
product} {
default}
bool UseXMMForObjInit = false {
ARCH product} {
default}
bool UseXmmI2D = false {
ARCH product} {
default}
bool UseXmmI2F = false {
ARCH product} {
default}
bool UseXmmLoadAndClearUpper = true {
ARCH product} {
default}
bool UseXmmRegToRegMoveAll = true {
ARCH product} {
default}
bool VMThreadHintNoPreempt = false {
product} {
default}
intx VMThreadPriority = -1 {
product} {
default}
intx VMThreadStackSize = 0 {
pd product} {
default}
intx ValueMapInitialSize = 11 {
C1 product} {
default}
intx ValueMapMaxLoopSize = 8 {
C1 product} {
default}
intx ValueSearchLimit = 1000 {
C2 product} {
default}
bool VerifyMergedCPBytecodes = true {
product} {
default}
bool VerifySharedSpaces = false {
product} {
default}
...
# 使用jinfo可查看運(yùn)行的應(yīng)用的jvm參數(shù) :jinfo pid
jvm 內(nèi)存模型
JDK1.7 jvm內(nèi)存模型
jdk7 jvm 堆模型
- Young 年輕區(qū)
Young區(qū)被劃分為三部分,Eden區(qū)和兩個(gè)大小嚴(yán)格相同的Survivor區(qū),其中,Survivor區(qū)間中,某一時(shí)刻只有
其中一個(gè)是被使用的,另外一個(gè)留做垃圾收集時(shí)復(fù)制對(duì)象用,在Eden區(qū)間變滿的時(shí)候, GC就會(huì)將存活的對(duì)
象移到空閑的Survivor區(qū)間中,根據(jù)JVM的策略,在經(jīng)過幾次垃圾收集后,任然存活于Survivor的對(duì)象將被移
動(dòng)到Tenured區(qū)間。
- Tenured 年老區(qū)
Tenured區(qū)主要保存生命周期長(zhǎng)的對(duì)象,一般是一些老的對(duì)象,當(dāng)一些對(duì)象在Young復(fù)制轉(zhuǎn)移一定的次數(shù)以
后,對(duì)象就會(huì)被轉(zhuǎn)移到Tenured區(qū),一般如果系統(tǒng)中用了application級(jí)別的緩存,緩存中的對(duì)象往往會(huì)被轉(zhuǎn)
移到這一區(qū)間。
- Perm 永久區(qū)
Perm代主要保存class,method,filed對(duì)象,這部份的空間一般不會(huì)溢出,除非一次性加載了很多的類,不過在
涉及到熱部署的應(yīng)用服務(wù)器的時(shí)候,有時(shí)候會(huì)遇到j(luò)ava.lang.OutOfMemoryError : PermGen space 的錯(cuò)誤,
造成這個(gè)錯(cuò)誤的很大原因就有可能是每次都重新部署,但是重新部署后,類的class沒有被卸載掉,這樣就造
成了大量的class對(duì)象保存在了perm中,這種情況下,一般重新啟動(dòng)應(yīng)用服務(wù)器可以解決問題。
- Virtual區(qū)
最大內(nèi)存和初始內(nèi)存的差值,就是Virtual區(qū)。
JDK1.8內(nèi)存模型
JDK1.8的堆內(nèi)存模型
由上圖可以看出,jdk1.8的內(nèi)存模型是由2部分組成,年輕代 + 年老代。
年輕代:Eden + 2*Survivor
年老代:OldGen
在jdk1.8中變化最大的Perm區(qū),用Metaspace(元數(shù)據(jù)空間)進(jìn)行了替換。
需要特別說明的是:Metaspace所占用的內(nèi)存空間不是在虛擬機(jī)內(nèi)部,而是在本地內(nèi)存空間中,這也是與1.7的永
久代最大的區(qū)別所在。
JDK1.8廢棄永久區(qū)的原因
This is part of the JRockit and Hotspot convergence effort. JRockit customers do not need
to configure the permanent generation (since JRockit does not have a permanent
generation) and are accustomed to not configuring the permanent generation.
移除永久代是為融合HotSpot JVM與 JRockit VM而做出的努力,因?yàn)镴Rockit沒有永久代,不需要配置永久代。
現(xiàn)實(shí)使用中,由于永久代內(nèi)存經(jīng)常不夠用或發(fā)生內(nèi)存泄露,爆出異常java.lang.OutOfMemoryError: PermGen。
基于此,將永久區(qū)廢棄,而改用元空間,改為了使用本地內(nèi)存空間。
定位分析死鎖和內(nèi)存溢出
定位死鎖
//以下是一段死鎖代碼
package com.yiyu;
public class DeadLock {
private static final String LOCK_A = "LOCK_A";
private static final String LOCK_B = "LOCK_B";
public static void main(String[] args) throws InterruptedException {
Thread thread1 = new Thread(() -> DeadLock.getLock("thread_1", LOCK_A, LOCK_B));
Thread thread2 = new Thread(() -> DeadLock.getLock("thread_2", LOCK_B, LOCK_A));
thread1.start();
thread2.start();
}
public static void getLock(String user, String lock1, String lock2) {
try {
synchronized (lock1) {
System.out.println(user + "獲得了" + lock1 + "的鎖");
synchronized (lock2) {
System.out.println(user + "獲得了" + lock2 + "的鎖");
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
# 使用jps查看進(jìn)程ID
[root@izwz92w1juq9pnt03ae87yz ~]# jps
10400 Bootstrap
12812 Jps
12733 DeadLock
[root@izwz92w1juq9pnt03ae87yz ~]# jstack 12733
...
Found one Java-level deadlock:
=============================
"Thread-0":
waiting to lock monitor 0x00007fa788008900 (object 0x00000000e34c2258, a java.lang.String),
which is held by "Thread-1"
"Thread-1":
waiting to lock monitor 0x00007fa788006900 (object 0x00000000e34c2228, a java.lang.String),
which is held by "Thread-0"
Java stack information for the threads listed above:
===================================================
"Thread-0":
at DeadLock.getLock(DeadLock.java:17)
- waiting to lock <0x00000000e34c2258> (a java.lang.String)
- locked <0x00000000e34c2228> (a java.lang.String)
at DeadLock.lambda$main$0(DeadLock.java:6)
at DeadLock$$Lambda$1/0x0000000100060840.run(Unknown Source)
at java.lang.Thread.run(java.base@11.0.1/Thread.java:834)
"Thread-1":
at DeadLock.getLock(DeadLock.java:17)
- waiting to lock <0x00000000e34c2228> (a java.lang.String)
- locked <0x00000000e34c2258> (a java.lang.String)
at DeadLock.lambda$main$1(DeadLock.java:7)
at DeadLock$$Lambda$2/0x0000000100062840.run(Unknown Source)
at java.lang.Thread.run(java.base@11.0.1/Thread.java:834)
Found 1 deadlock.
"Thread-0" #10 prio=5 os_prio=0 cpu=22.94ms elapsed=369.77s tid=0x00007fa7a8156000 nid=0x31c9 waiting for monitor entry [0x00007fa79081b000]
java.lang.Thread.State: BLOCKED (on object monitor)
at DeadLock.getLock(DeadLock.java:17) #死鎖發(fā)生的代碼的行
- waiting to lock <0x00000000e34c2258> (a java.lang.String)
- locked <0x00000000e34c2228> (a java.lang.String)
at DeadLock.lambda$main$0(DeadLock.java:6)
at DeadLock$$Lambda$1/0x0000000100060840.run(Unknown Source)
at java.lang.Thread.run(java.base@11.0.1/Thread.java:834)
"Thread-1" #11 prio=5 os_prio=0 cpu=23.86ms elapsed=369.77s tid=0x00007fa7a8157800 nid=0x31ca waiting for monitor entry [0x00007fa79071a000]
java.lang.Thread.State: BLOCKED (on object monitor)
at DeadLock.getLock(DeadLock.java:17)#死鎖發(fā)生的代碼的行
- waiting to lock <0x00000000e34c2228> (a java.lang.String)
- locked <0x00000000e34c2258> (a java.lang.String)
at DeadLock.lambda$main$1(DeadLock.java:7)
at DeadLock$$Lambda$2/0x0000000100062840.run(Unknown Source)
定位內(nèi)存溢出
package com.yiyu;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Stream;
public class OutOffMemoryErrorTest {
public static void main(String[] args) {
Stream.iterate(UUID.randomUUID()
.toString(), a -> a + a)
.limit(1000)
.reduce(String::concat)
.ifPresent(System.out::println);
}
}
java -Xmx6m -XX:+HeapDumpOnOutOfMemoryError OutOffMemoryErrorTest 程序運(yùn)行后會(huì)出現(xiàn)內(nèi)存溢出
具體如下:
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid2176.hprof …
Exception in thread “main” java.lang.OutOfMemoryError: Java heap space
Heap dump file created [6383576 bytes in 0.029 secs]
at java.base/java.lang.String.concat(String.java:1947)
at com.yiyu.OutOffMemoryErrorTest$$Lambda$18/0x00000001000a8840.apply(Unknown Source)
at java.base/java.util.stream.ReduceOps$2ReducingSink.accept(ReduceOps.java:123)
at java.base/java.util.stream.SliceOps$1$1.accept(SliceOps.java:199)
at java.base/java.util.stream.Stream 1. t r y A d v a n c e ( S t r e a m . j a v a : 1231 ) a t j a v a . b a s e / j a v a . u t i l . s t r e a m . R e f e r e n c e P i p e l i n e . f o r E a c h W i t h C a n c e l ( R e f e r e n c e P i p e l i n e . j a v a : 127 ) a t j a v a . b a s e / j a v a . u t i l . s t r e a m . A b s t r a c t P i p e l i n e . c o p y I n t o W i t h C a n c e l ( A b s t r a c t P i p e l i n e . j a v a : 502 ) a t j a v a . b a s e / j a v a . u t i l . s t r e a m . A b s t r a c t P i p e l i n e . c o p y I n t o ( A b s t r a c t P i p e l i n e . j a v a : 488 ) a t j a v a . b a s e / j a v a . u t i l . s t r e a m . A b s t r a c t P i p e l i n e . w r a p A n d C o p y I n t o ( A b s t r a c t P i p e l i n e . j a v a : 474 ) a t j a v a . b a s e / j a v a . u t i l . s t r e a m . R e d u c e O p s 1.tryAdvance(Stream.java:1231) at java.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127) at java.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502) at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488) at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474) at java.base/java.util.stream.ReduceOps 1.tryAdvance(Stream.java:1231)atjava.base/java.util.stream.ReferencePipeline.forEachWithCancel(ReferencePipeline.java:127)atjava.base/java.util.stream.AbstractPipeline.copyIntoWithCancel(AbstractPipeline.java:502)atjava.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:488)atjava.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)atjava.base/java.util.stream.ReduceOpsReduceOp.evaluateSequential(ReduceOps.java:913)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.base/java.util.stream.ReferencePipeline.reduce(ReferencePipeline.java:558)
at com.yiyu.OutOffMemoryErrorTest.main(OutOffMemoryErrorTest.java:14)
從項(xiàng)目中找到對(duì)應(yīng)的文件,使用工具分析
jvm其他工具使用
請(qǐng)參考博客jvm分析工具概述,對(duì)每個(gè)命令的每個(gè)操作分析的非常全面,跳轉(zhuǎn)方便以后閱讀。
JMX配置
原理是啟動(dòng)Java程序時(shí)添加系統(tǒng)參數(shù)已達(dá)到暴露端口提供接入虛擬機(jī)的協(xié)議,網(wǎng)上的配置非常詳細(xì)但是有些不足,使用JMX時(shí)會(huì)開放三個(gè)端口,如果僅僅開啟com.sun.management.jmxremote.port會(huì)發(fā)現(xiàn) Jconsole活visualVM仍然訪問不了,此時(shí) 使用netstat -nltp 查詢打開另外兩個(gè)端口或多一行配置即可,具體原因是因?yàn)镴MX支持本地連接建立的端口。如需知道更詳細(xì)信息請(qǐng)參考博客:JMX實(shí)踐-JMX連接端口
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=9999
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
javax.management.remote.rmi.RMIConnectorServer=9999
-
并行垃圾收集器 部分stw ↩?
-
串行垃圾收集器單線程 全部stw ↩?
總結(jié)
以上是生活随笔為你收集整理的JVM优化之优化常用参数和工具的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: krc文件指的是什么意思
- 下一篇: 隐式转换函数_隐函数可以转化为显函数