從 Java 8 升級(jí)到 Java 17 全過(guò)程,賊特么坑!
點(diǎn)擊關(guān)注公眾號(hào),Java干貨及時(shí)送達(dá)??
來(lái)源:juejin.cn/post/7117531586232320031
編譯相關(guān) JEP 320 使用了 sun.misc.* 下的包 lombok 使用了 com.sun.tools.javac.* 下的包 kotlin 版本限制 廢棄依賴(lài)分析 參數(shù)遷移 什么是 Unified Logging 輸出什么信息(selectors) 第二部分:輸出到哪里(output) 第三部分:日志 decorators GC 參數(shù)遷移 運(yùn)行相關(guān) 反射+私有 API 調(diào)用之傷 關(guān)于 GC 算法的選擇 G1 參數(shù)調(diào)整
最近在做 Java8 到 Java17 的遷移工作,前期做了一些準(zhǔn)備,但是在升級(jí)過(guò)程還是有些問(wèn)題,太emo了,一些信息記錄如下,分為幾個(gè)部分:
編譯相關(guān) 參數(shù)遷移相關(guān) 運(yùn)行相關(guān)
前人栽樹(shù)后人乘涼,有需要升級(jí)的可以參考一下,避免踩坑。。。
編譯相關(guān)
JEP 320
在 Java11 中引入了一個(gè)提案 JEP 320: Remove the Java EE and CORBA Modules (openjdk.org/jeps/320) 提案,移除了 Java EE and CORBA 的模塊,如果項(xiàng)目中用到需要手動(dòng)引入。比如代碼中用到了 javax.annotation.* 下的包:
public abstract class FridayAgent
@PreDestroy
public void destroy() {
agentClient.close();
}
}
在編譯時(shí)會(huì)找不到相關(guān)的類(lèi)。這是因?yàn)?Java EE 已經(jīng)在 Java 9 中被標(biāo)記為 deprecated,Java 11 中被正式移除,可以手動(dòng)引入 javax 的包:
<dependency>
<groupId>javax.annotation</groupId>
<artifactId>javax.annotation-api</artifactId>
<version>1.3.2</version>
</dependency>
使用了 sun.misc.* 下的包
比如 sun.misc.BASE64Encoder,這個(gè)簡(jiǎn)單,替換一下工具類(lèi)即可。
[ERROR] symbol: class BASE64Encoder
[ERROR] location: package sun.misc
netty 低版本使用了 sun.misc.*,編譯錯(cuò)誤信息如下
Caused by: java.lang.NoClassDefFoundError: Could not initialize class io.netty.util.internal.PlatformDependent0
at io.netty.util.internal.PlatformDependent.getSystemClassLoader(PlatformDependent.java:694) ~[netty-all-4.0.42.Final.jar!/:4.0.42.Final]
對(duì)應(yīng)的源碼如下:
/**
* The {@link PlatformDependent} operations which requires access to {@code sun.misc.*}.
*/
final class PlatformDependent0 {
}
https://github.com/netty/netty/issues/6855
lombok 使用了 com.sun.tools.javac.* 下的包
錯(cuò)誤信息如下:
Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.2:compile (default-compile) on project encloud-common: Fatal error compiling: java.lang.ExceptionInInitializerError: Unable to make field private com.sun.tools.javac.processing.JavacProcessingEnvironment$DiscoveredProcessors com.sun.tools.javac.processing.JavacProcessingEnvironment.discoveredProcs accessible: module jdk.compiler does not "opens com.sun.tools.javac.processing" to unnamed module
如果你的項(xiàng)目中使用 lombok,而且是低版本的話,就會(huì)出現(xiàn),lombok 的原理是在編譯期做一些手腳,用到了 com.sun.tools.javac 下的文件,升級(jí)到最新版可以解決。ps,個(gè)人很不喜歡 lombok, 調(diào)試的時(shí)候代碼和 class 對(duì)不上真的很惡心。
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<!-- <version>1.16.4</version>-->
<version>1.18.24</version>
</dependency>
kotlin 版本限制
我們后端在很多年前就 all-in Kotlin,Kotlin 的升級(jí)也是我們的重中之重。
[ERROR] Failed to execute goal org.jetbrains.kotlin:kotlin-maven-plugin:1.2.71:compile (compile) on project encloud-core: Compilation failure [ERROR] Unknown JVM target version: 17 [ERROR] Supported versions: 1.6, 1.8
Kotlin 在 1.6.0 版本開(kāi)始支持 Java17 的字節(jié)碼,低于 1.6.0 的編譯會(huì)直接報(bào)錯(cuò)
廢棄依賴(lài)分析
可以用 jdeps --jdk-internals --multi-release 17 --class-path . encloud-api.jar 來(lái)做項(xiàng)目的依賴(lài)分析

這樣你就可以知道哪些庫(kù)需要做升級(jí)了。
參數(shù)遷移
什么是 Unified Logging
在 Java 領(lǐng)域,有廣為人知的日志框架,slf4j、log4j 等,這些框架提供了統(tǒng)一的編程接口,讓用戶(hù)可以通過(guò)簡(jiǎn)單的配置實(shí)現(xiàn)日志輸出的個(gè)性化配置,比如日志 tag、級(jí)別(info、debug 等)、上下文(線程 id、行號(hào)、時(shí)間等),在 JVM 內(nèi)部之前一直缺乏這樣的規(guī)范,于是出來(lái)了 Unified Logging,實(shí)現(xiàn)了日志格式的大一統(tǒng),這就是我們接下來(lái)要介紹的重點(diǎn) Unified Logging。
我們接觸最多的是 gc 的日志,在 java8 中,我們配置 gc 日志的參數(shù)是 -Xloggc:/tmp/gc.log。在 JVM 中除了 GC,還有大量的其它相關(guān)的日志,比如線程、os 等,在新的 Unified Logging 日志中,日志輸出的方式變更為了 java -Xlog:xxx,GC 不再特殊只是做為日志的一種存在形式。
java -Xlog -version
輸出結(jié)果如下:

可以看到日志輸出里,不僅有 GC 相關(guān)的日志,還有 os 線程相關(guān)的信息。事實(shí)上 java 的日志的生產(chǎn)者有非常多部分,比如 thread、class load、unload、safepoint、cds 等。

歸根到底,日志打印,需要回答清楚三個(gè)問(wèn)題:
what:要輸出什么信息(tag),以什么日志級(jí)別輸出(level) where:輸出到哪里(console 還是 file) decorators:日志如何
輸出什么信息(selectors)
首先來(lái)看 what 的部分,如何指定要輸出哪些信息,這個(gè)在 JVM 內(nèi)部被稱(chēng)之為 selectors。
JVM 采用的是 <tag-set>=<level>的形式來(lái)表示 selectors,默認(rèn)情況下,tag 為all,表示所有的 tag,level 為 INFO,java -Xlog -version 等價(jià)于下面的形式
java -Xlog:all=info -version
如果我們想輸出tag 為 gc,日志級(jí)別為 debug 的日志,可以用 java -Xlog:gc=debug 的形式:
$ java -Xlog:gc=debug -version
[0.023s][info][gc] Using G1
[0.023s][debug][gc] ConcGCThreads: 3 offset 22
[0.023s][debug][gc] ParallelGCThreads: 10
[0.024s][debug][gc] Initialize mark stack with 4096 chunks, maximum 524288
這樣就輸出了 tag 為 gc,級(jí)別為 debug 的日志信息。
不過(guò)這里有一個(gè)比較坑的點(diǎn)是,這里的 tag 匹配規(guī)則是精確匹配,如果某條日志的 tag 是 gc,metaspace,通過(guò)上面的規(guī)則是匹配不到的,我們可以手動(dòng)指定的方式來(lái)輸出。
$ java -Xlog:gc+metaspace -version
[0.022s][info][gc,metaspace] CDS archive(s) mapped at: ... size 12443648.
[0.022s][info][gc,metaspace] Compressed class space mapped at: reserved size:...
[0.022s][info][gc,metaspace] Narrow klass base:..., Narrow
klass shift: 0, Narrow klass range: 0x100000000
這里的 selector 也是可以進(jìn)行組合的,不同的 selector 之間用逗號(hào)分隔即可。比如同時(shí)輸出 gc 和 gc+metaspace 這兩類(lèi) tag 的日志,就可以這么寫(xiě):
$ java -Xlog:gc=debug,gc+metaspace -version
[0.020s][info][gc] Using G1
[0.020s][debug][gc] ConcGCThreads: 3 offset 22
[0.020s][debug][gc] ParallelGCThreads: 10
[0.020s][debug][gc] Initialize mark stack with 4096 chunks, maximum 524288
[0.022s][info ][gc,metaspace] CDS archive(s) mapped at:
[0.022s][info ][gc,metaspace] Compressed class space mapped at:
[0.022s][info ][gc,metaspace] Narrow klass base: 0x0000000800000000
當(dāng)然這么搞是很麻煩的,JVM 提供了通配符 * 來(lái)解決精確匹配的問(wèn)題,比如我們想要所有 tag 為 gc 的日志,可以這么寫(xiě):
$ java -Xlog:gc*=debug -version
[0.024s][debug][gc,heap] Minimum heap 8388608
[0.024s][info ][gc ] Using G1
[0.024s][debug][gc,heap,coops] Heap address: 0x0000000707400000
[0.024s][debug][gc ] ConcGCThreads: 3 offset 22
[0.024s][debug][gc ] ParallelGCThreads: 10
[0.024s][debug][gc ] Initialize mark stack with 4096 chunks
[0.024s][debug][gc,ergo,heap ] Expand the heap. requested expansion amount:
[0.025s][debug][gc,heap,region] Activate regions [0, 125)[0.025s][debug][gc,ihop ] Target occupancy update: old: 0B, new: 262144000B
[0.025s][debug][gc,ergo,refine] Initial Refinement Zones: green: 2560
[0.026s][debug][gc,task ] G1 Service Thread
[0.026s][debug][gc,task ] G1 Service Thread (Periodic GC Task) (register)
[0.026s][info ][gc,init ] Version: 17.0.3+7 (release)
...
如果只想要 INFO 級(jí)別的日志,則可以省略 level 的設(shè)置,使用 java -Xlog:gc* -version 即可。
如果想知道有哪些個(gè)性化的 tag 可以選擇,可以用 java -Xlog:help 來(lái)找到所有可用的 tag。
階段性小結(jié)

第二部分:輸出到哪里(output)
默認(rèn)情況下,日志會(huì)輸出到 stdout,jvm 支持以下三種輸出方式:
stdout stderr file
一般而言我們會(huì)把日志輸出到文件中,方便后續(xù)進(jìn)一步分析
-Xlog:all=debug:file=/path_to_logs/app.log
還可以指定日志切割的大小和方式
-Xlog:gc*:file=/path_to_logs/app.log:filesize=104857600,filecount=5
第三部分:日志 decorators
每條日志除了正常的信息以外,還有不少日志相關(guān)的上下文信息,在 jvm 中被稱(chēng)為 decorators,有下面這些可選項(xiàng)。

比如可以用 java -Xlog:all=debug:stdout:level,tags,time,uptime,pid -version 選項(xiàng)來(lái)打印日志。
[2022-06-15T19:54:01.529+0800][0.001s][5235][info ][os,thread] Thread attached
[2022-06-15T19:54:01.529+0800][0.001s][5235][debug][os,thread] Thread 5237 stack...
[2022-06-15T19:54:01.529+0800][0.001s][5235][debug][perf,datacreation]
Unified Logging 小結(jié)
輸出格式如下:
-Xlog:[selectors]:[output]:[decorators][:output-options]
selectors 是多個(gè) tag 和 level 的組合,起到了 what(過(guò)濾器)的作用,格式為 tag1[+tag2...][*][=level][,...]decorators 是日志相關(guān)的描述信息,也可以理解為上下文 output 是輸出相關(guān)的選項(xiàng),一般我們會(huì)配置為輸出到文件,按文件大小切割
這里補(bǔ)充一個(gè)知識(shí)點(diǎn),就是默認(rèn)值:
tag:all level:info output:stdout decorators: uptime, level, tags
GC 參數(shù)遷移
可以看到 GC 相關(guān)的參數(shù)都已經(jīng)收攏到 Xlog 下,以前的很多 Java8 下的參數(shù)已經(jīng)被移除或者標(biāo)記為過(guò)期。
比如 PrintGCDetails 已經(jīng)被 -Xlog:gc* 取代:
java -XX:+PrintGCDetails -version
[0.001s][warning][gc] -XX:+PrintGCDetails is deprecated. Will use -Xlog:gc* instead.
常見(jiàn)的標(biāo)記為廢棄的參數(shù)還有 -XX:+PrintGC 和 -Xloggc:<filepath>,遷移前后的參數(shù)如下:
| 舊參數(shù) | 新參數(shù) |
|---|---|
| -XX:+PrintGCDetails | -Xlog:gc* |
| -XX:+PrintGC | -Xlog:gc |
-Xloggc:<filepath> | -Xlog:gc:file=<filepath> |
除此之外,大量的 GC 的參數(shù)被移除,比如常用的參數(shù) -XX:+PrintTenuringDistribution,Java17 會(huì)拒絕啟動(dòng)
java -XX:+PrintTenuringDistribution -version
Unrecognized VM option 'PrintTenuringDistribution'
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
更詳細(xì)的移除的參數(shù)如下
CMSDumpAtPromotionFailure,
CMSPrintEdenSurvivorChunks,
GlLogLevel,
G1PrintHeapRegions,
G1PrintRegionLivenessInfo,
G1SummarizeConcMark,
G1SummarizeRSetStats,
G1TraceConcRefinement,
G1TraceEagerReclaimHumongousObjects,
G1TraceStringSymbolTableScrubbing,
GCLogFileSize, NumberofGCLogFiles,
PrintAdaptiveSizePolicy,
PrintclassHistogramAfterFullGC,
PrintClassHistogramBeforeFullGC,
PrintCMSInitiationStatistics
PrintCMSStatistics,
PrintFLSCensus,
PrintFLSStatistics,
PrintGCApplicationConcurrentTime
PrintGCApplicationStoppedTime,
PrintGCCause,
PrintGCDateStamps,
PrintGCID,
PrintGCTaskTimeStamps,
PrintGCTimeStamps,
PrintHeapAtGC,
PrintHeapAtGCExtended,
PrintJNIGCStalls,
PrintOldPLAB
PrintParallel0ldGCPhaseTimes,
PrintPLAB,
PrintPromotionFailure,
PrintReferenceGC,
PrintStringDeduplicationStatistics,
PrintTaskqueue,
PrintTenuringDistribution,
PrintTerminationStats,
PrintTLAB,
TraceDynamicGCThreads,
TraceMetadataHumongousAllocation,
UseGCLogFileRotation,
VerifySilently
這些移除的參數(shù)大部分都能在新的日志體系下找到對(duì)應(yīng)的參數(shù),比如 PrintHeapAtGC 這個(gè)參數(shù)可以用 -Xlog:gc+heap=debug 來(lái)替代
$ java -Xlog:gc+heap=debug -cp . G1GCDemo01
[0.004s][debug][gc,heap] Minimum heap 8388608 Initial heap 268435456 Maximum heap
hello, g1gc!
[12.263s][debug][gc,heap] GC(0) Heap before GC invocations=0 (full 0):
[12.265s][debug][gc,heap] GC(0) garbage-first heap
[12.265s][debug][gc,heap] GC(0) region size 2048K, 1 young (2048K)
[12.265s][debug][gc,heap] GC(0) Metaspace used 3678K
[12.265s][debug][gc,heap] GC(0) class space used 300K
[12.280s][debug][gc,heap] GC(0) Uncommittable regions after shrink: 124
雖然理解起來(lái)不太直觀,不過(guò)要記住 -XX:+PrintGCApplicationStoppedTime 和 -XX+PrintGCApplicationConcurrentTime 這兩個(gè)參數(shù)一起被 -Xlog:safepoint 取代。
還有一個(gè)常見(jiàn)的參數(shù) -XX:+PrintAdaptiveSizePolicy 被 -Xlog:gc+ergo*=trace 取代,
[0.122s][debug][gc, ergo, refine] Initial Refinement Zones: green: 23, yellow:
69, red: 115, min yellow size: 46
[0.142s ][debug][gc, ergo, heap ] Expand the heap. requested expansion amount: 268435456B expansion amount: 268435456B
[2.475s][trace][gc, ergo, cset] GC(0) Start choosing CSet. pending cards: 0 predicted base time: 10.00ms remaining time:
190.00ms target pause time: 200.00ms
[2.476s][trace][gc, ergo, cset ] GC(9) Add young regions to CSet. eden: 24 regions, survivors: 0 regions, predicted young
region time: 367.19ms, target pause time: 200.00ms
[2.476s ][debug][gc, ergo, cset ] GC(0) Finish choosing CSet. old: 0 regions, predicted old region time: 0.00ms, time
remaining: 0.00
[2.826s][debug][gc, ergo] GC(0) Running G1 Clear Card Table Task using 1 workers for 1 units of work for 24 regions.
[2.827s][debug][gc, ergo] GC (0) Running G1 Free Collection Set using 1 workers for collection set length 24
[2.828s][trace][gc, ergo, refine] GC(0) Updating Refinement Zones: update rs time: 0.004ms, update rs buffers: 0, update rs
goal time: 19.999ms
[2.829s][debug][gc, ergo, refine] GC(0) Updated Refinement Zones: green: 23, yellow: 69, red: 115
[3.045s][trace][gc, ergo, set ] GC(1) Start choosing CSet. pending cards: 5898 predicted base time: 26.69ms remaining
time: 173.31ms target pause time: 200.00ms
[3.045s][trace][gc, ergo, cset ] GC(1) Add young regions to Set. eden: 9 regions, survivors: 3 regions, predicted young
region time: 457.38ms, target pause time: 200.00ms
[3.045s][debug](gc, ergo, set ] GC(1) Finish choosing CSet. old: @ regions, predicted old region time: 0.00ms, time
remaining: 0.00
[3.090s ][debug][gc, ergo
] GC (1) Running G1 Clear Card Table Task using 1 workers for 1 units of work for 12 regions.
[3.091s][debug][gc, ergo
GC (1) Running G1 Free Collection Set using 1 workers for collection set length 12
[3.093s][trace][gc, ergo, refine] GC(1) Updating Refinement Zones: update rs time: 2.510ms, update rs buffers: 25, update rs
goal time: 19.999ms
[3.093s ][debug][gc, ergo, refine] GC(1) Updated Refinement Zones: green: 25, yellow: 75, red: 125
看一下這部分的源碼的變遷,就可以知道確實(shí)是如此了,在 Java8 中,PSYoungGen::resize_spaces代碼如下:

在 Java17 中,這部分日志打印被 gc+ergo 的標(biāo)簽日志取代:

還有一個(gè)分代 GC 中非常有用的參數(shù) -XX:+PrintTenuringDistribution,現(xiàn)在被 gc+age=trace 取代
完整的參數(shù)變遷對(duì)應(yīng)表如下:




舉例
-XX:+PrintGCDetails \ // gc*
-XX:+PrintGCApplicationStoppedTime \ // safepoint
-XX:+PrintGCApplicationConcurrentTime \ // safepoint
-XX:+PrintGCCause \ // 默認(rèn)會(huì)輸出
-XX:+PrintGCID \ // 默認(rèn)會(huì)輸出
-XX:+PrintTenuringDistribution \ // gc+age*=trace
-XX:+PrintGCDateStamps \ // :time,tags,level
-XX:+UseGCLogFileRotation \ // :filecount=5,filesize=10M
-XX:NumberOfGCLogFiles=5 \ // :filecount=5,filesize=10M
-XX:GCLogFileSize=10M \ // :filecount=5,filesize=10M
-Xloggc:/var/log/`date +%FT%H-%M-%S`-gc.log \ // -Xlog::file=/var/log/%t-gc.log
變遷后:
-Xlog:
gc*,
safepoint,
gc+heap=debug,
gc+ergo*=trace,
gc+age*=trace,
:file=/var/log/%t-gc.log
:time,tags,level
:filecount=5,filesize=10M
推薦的配置
-Xlog:
// selections
codecache+sweep*=trace,
class+unload, // TraceClassUnloading
class+load, // TraceClassLoading
os+thread,
safepoint, // TraceSafepoint
gc*, // PrintGCDetails
gc+stringdedup=debug, // PrintStringDeduplicationStatistics
gc+ergo*=trace,
gc+age=trace, // PrintTenuringDistribution
gc+phases=trace,
gc+humongous=trace,
jit+compilation=debug
// output
:file=/path_to_logs/app.log
// decorators
:level,tags,time,uptime,pid
// output-options
:filesize=104857600,filecount=5
運(yùn)行相關(guān)
反射+私有 API 調(diào)用之傷
在 Java8 中,沒(méi)有人能阻止你訪問(wèn)特定的包,比如 sun.misc,對(duì)反射也沒(méi)有限制,只要 setAccessible(true) 就可以了。Java9 模塊化以后,一切都變了,只能通過(guò) --add-exports 和 --add-opens 來(lái)打破模塊封裝
--add-opens導(dǎo)出特定的包--add-opens允許模塊中特定包的類(lèi)路徑深度反射訪問(wèn)
比如:
--add-opens java.base/java.lang=ALL-UNNAMED
--add-opens java.base/java.io=ALL-UNNAMED
--add-opens java.base/java.math=ALL-UNNAMED
--add-opens java.base/java.net=ALL-UNNAMED
--add-opens java.base/java.nio=ALL-UNNAMED
--add-opens java.base/java.security=ALL-UNNAMED
--add-opens java.base/java.text=ALL-UNNAMED
--add-opens java.base/java.time=ALL-UNNAMED
--add-opens java.base/java.util=ALL-UNNAMED
--add-opens java.base/jdk.internal.access=ALL-UNNAMED
--add-opens java.base/jdk.internal.misc=ALL-UNNAMED
關(guān)于 GC 算法的選擇
CMS 正式退出歷史舞臺(tái),G1 正式接棒,ZGC 蓄勢(shì)待發(fā)。在GC 算法的選擇上,目前來(lái)看 G1 還是最佳的選擇,ZGC 因?yàn)橛袃?nèi)存占用被 OS 標(biāo)記過(guò)高(三倍共享內(nèi)存)虛高的問(wèn)題,進(jìn)程可能被 OOM-killer 殺掉。
ZGC 三倍 RES 內(nèi)存
ZGC 底層用到了一個(gè)稱(chēng)之為染色指針的技術(shù),使用三個(gè)視圖(Marked0、Marked1 和 Remapped)來(lái)映射到同一塊共享內(nèi)存區(qū)域,原理如下:
##include <iostream>
##include <sys/mman.h>
##include <sys/stat.h>
##include <fcntl.h>
##include <unistd.h>
##include <cstdio>
##include <cstdlib>
int main() {
// shm_open()函數(shù)用來(lái)打開(kāi)或者創(chuàng)建一個(gè)共享內(nèi)存區(qū),兩個(gè)進(jìn)程可以通過(guò)給shm_open()函數(shù)傳遞相同的名字以達(dá)到操作同一共享內(nèi)存的目的
int fd = ::shm_open("/test", O_RDWR | O_CREAT | O_EXCL, 0600);
if (fd < 0) {
shm_unlink("/test");
perror("shm open failed");
return 0;
}
size_t size = 1 * 1024 * 1024 * 1024;
// 創(chuàng)建一個(gè)共享內(nèi)存后,默認(rèn)大小為0,所以需要設(shè)置共享內(nèi)存大小。ftruncate()函數(shù)可用來(lái)調(diào)整文件或者共享內(nèi)存的大小
::ftruncate(fd, size);
int prot = PROT_READ | PROT_WRITE;
// 創(chuàng)建共享內(nèi)存后,需要將共享內(nèi)存映射到調(diào)用進(jìn)程的地址空間,可通過(guò)mmap()函數(shù)來(lái)完成
uint32_t *p1 = (uint32_t *) (mmap(nullptr, size, prot, MAP_SHARED, fd, 0));
uint32_t *p2 = (uint32_t *) (mmap(nullptr, size, prot, MAP_SHARED, fd, 0));
uint32_t *p3 = (uint32_t *) (mmap(nullptr, size, prot, MAP_SHARED, fd, 0));
::close(fd);
*p1 = 0xcafebabe;
::printf("Address of addr1: %p, value is 0x%x\n", p1, *p1);
::printf("Address of addr2: %p, value is 0x%x\n", p2, *p2);
::printf("Address of addr3: %p, value is 0x%x\n", p3, *p3);
::getchar();
*p2 = 0xcafebaba;
::printf("Address of addr1: %p, value is 0x%x\n", p1, *p1);
::printf("Address of addr2: %p, value is 0x%x\n", p2, *p2);
::printf("Address of addr3: %p, value is 0x%x\n", p3, *p3);
::getchar();
munmap(p1, size);
munmap(p2, size);
munmap(p3, size);
shm_unlink("/test");
std::cout << "hello" << std::endl;
}
你可以想象 p1、p2、p3 這三塊內(nèi)存區(qū)域就是 ZGC 中三種視圖。
但是在 linux 統(tǒng)計(jì)中,雖然是共享內(nèi)存,但是依然會(huì)統(tǒng)計(jì)三次,比如 RES。
同一個(gè)應(yīng)用,使用 G1 RES 顯示占用 2G,ZGC 則顯示占用 6G
java -XX:+AlwaysPreTouch -Xms2G -Xmx2G -XX:+UseZGC MyTest
java -XX:+AlwaysPreTouch -Xms2G -Xmx2G -XX:+UseG1GC MyTest

接下面我們討論的都是 G1 相關(guān)的。
G1 參數(shù)調(diào)整
不要配置新生代的大小
這個(gè)在《JVM G1 源碼分析和調(diào)優(yōu)》一書(shū)里有詳細(xì)的介紹,有兩個(gè)主要的原因:
G1對(duì)內(nèi)存的管理是不連續(xù)的,重新分配一個(gè)分區(qū)代價(jià)很低 G1 的需要根據(jù)目標(biāo)停頓時(shí)間動(dòng)態(tài)調(diào)整搜集的分區(qū)的個(gè)數(shù),如果不能調(diào)整新生代的大小,那么 G1 可能不能滿(mǎn)足停頓時(shí)間的要求
諸如 -Xmn, -XX:NewSize, -XX:MaxNewSize, -XX:SurvivorRatio 都不要在 G1 中出現(xiàn),只需要控制最大、最小堆和目標(biāo)暫停時(shí)間即可
調(diào)整 -XX:InitiatingHeapOccupancyPercent 到合適的值
IHOP 默認(rèn)值為 45,這個(gè)值是啟動(dòng)并發(fā)標(biāo)記的先決條件,只有當(dāng)老年代內(nèi)存棧總空間的 45% 之后才會(huì)啟動(dòng)并發(fā)標(biāo)記任務(wù)。
增加這個(gè)值:導(dǎo)致并發(fā)標(biāo)記可能花費(fèi)更多的時(shí)間,同時(shí)導(dǎo)致 YGC 和 Mixed-GC 收集時(shí)的分區(qū)數(shù)變少,可以根據(jù)整體應(yīng)用占用的平均內(nèi)存來(lái)設(shè)置。
程序汪資料鏈接
程序汪接的7個(gè)私活都在這里,經(jīng)驗(yàn)整理
Java項(xiàng)目分享 最新整理全集,找項(xiàng)目不累啦 07版
堪稱(chēng)神級(jí)的Spring Boot手冊(cè),從基礎(chǔ)入門(mén)到實(shí)戰(zhàn)進(jìn)階
臥槽!字節(jié)跳動(dòng)《算法中文手冊(cè)》火了,完整版 PDF 開(kāi)放下載!
臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開(kāi)放下載!
字節(jié)跳動(dòng)總結(jié)的設(shè)計(jì)模式 PDF 火了,完整版開(kāi)放下載!
歡迎添加程序汪個(gè)人微信 itwang009 進(jìn)粉絲群或圍觀朋友圈

