Java 8 是旧时代的 Java 6,还不快晋级,。最近在做 Java8 到 Java17 的搬迁工作,前期做了一些准备,过程中的一些信息记录如下(继续更新。。。)

分为几个部分:

  • 编译相关
  • 参数搬迁相关
  • 运转相关

编译相关

JEP 320

在 Java11 中引入了一个提案 JEP 320: Remove the Java EE and CORBA Modules (openjdk.org/jeps/320) 提案,移除了 Java EE and CORBA 的模块,假如项目顶用到需求手动引入。比方代码顶用到了 javax.annotation.* 下的包:

import javax.annotation.PreDestroy;
public abstract class FridayAgent 
    @PreDestroy
    public void destroy() {
        agentClient.close();
    }
}    

在编译时会找不到相关的类。这是因为 Java EE 现已在 Java 9 中被符号为 deprecated,Java 11 中被正式移除,能够手动引入 javax 的包:

<dependency>
    <groupId>javax.annotation</groupId>
    <artifactId>javax.annotation-api</artifactId>
    <version>1.3.2</version>
</dependency>

运用了 sun.misc.* 下的包

比方 sun.misc.BASE64Encoder,这个简略,替换一下工具类即可。

[ERROR]   symbol:   class BASE64Encoder
[ERROR]   location: package sun.misc

netty 低版本运用了 sun.misc.*,编译错误信息如下

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]

对应的源码如下:

/**
 * 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.* 下的包

错误信息如下:

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

假如你的项目中运用 lombok,而且是低版本的话,就会呈现,lombok 的原理是在编译期做一些手脚,用到了 com.sun.tools.javac 下的文件,晋级到最新版能够解决。ps,个人很不喜爱 lombok, 调试的时候代码和 class 对不上真的很恶心。

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
   <!-- <version>1.16.4</version>-->
    <version>1.18.24</version>
</dependency>

kotlin 版本约束

咱们后端在许多年前就 all-in Kotlin,Kotlin 的晋级也是咱们的重中之重。

[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 版本开端支撑 Java17 的字节码,低于 1.6.0 的编译会直接报错

抛弃依靠剖析

能够用 jdeps --jdk-internals --multi-release 17 --class-path . encloud-api.jar 来做项目的依靠剖析

Java 17 晋级攻略(继续更新)

这样你就能够知道哪些库需求做晋级了。

参数搬迁

什么是 Unified Logging

在 Java 领域,有广为人知的日志结构,slf4j、log4j 等,这些结构提供了统一的编程接口,让用户能够经过简略的装备完成日志输出的个性化装备,比方日志 tag、等级(info、debug 等)、上下文(线程 id、行号、时刻等),在 JVM 内部之前一直缺乏这样的标准,于是出来了 Unified Logging,完成了日志格局的大一统,这便是咱们接下来要介绍的重点 Unified Logging

咱们触摸最多的是 gc 的日志,在 java8 中,咱们装备 gc 日志的参数是 -Xloggc:/tmp/gc.log。在 JVM 中除了 GC,还有很多的其它相关的日志,比方线程、os 等,在新的 Unified Logging 日志中,日志输出的方法改变为了 java -Xlog:xxx,GC 不再特别只是做为日志的一种存在方法。

java -Xlog -version

输出结果如下:

Java 17 晋级攻略(继续更新)

能够看到日志输出里,不仅有 GC 相关的日志,还有 os 线程相关的信息。事实上 java 的日志的生产者有非常多部分,比方 thread、class load、unload、safepoint、cds 等。

Java 17 晋级攻略(继续更新)

归根到底,日志打印,需求回答清楚三个问题:

  • what:要输出什么信息(tag),以什么日志等级输出(level)
  • where:输出到哪里(console 还是 file)
  • decorators:日志怎么

输出什么信息(selectors)

首先来看 what 的部分,怎么指定要输出哪些信息,这个在 JVM 内部被称之为 selectors。

JVM 选用的是 <tag-set>=<level>的方法来表示 selectors,默认情况下,tag 为all,表示一切的 tag,level 为 INFOjava -Xlog -version 等价于下面的方法

java -Xlog:all=info -version

假如咱们想输出tag 为 gc,日志等级为 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,等级为 debug 的日志信息。

不过这儿有一个比较坑的点是,这儿的 tag 匹配规则是准确匹配,假如某条日志的 tag 是 gc,metaspace,经过上面的规则是匹配不到的,咱们能够手动指定的方法来输出。

$ 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 也是能够进行组合的,不同的 selector 之间用逗号分隔即可。比方一起输出 gcgc+metaspace 这两类 tag 的日志,就能够这么写:

$ 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

当然这么搞是很费事的,JVM 提供了通配符 * 来解决准确匹配的问题,比方咱们想要一切 tag 为 gc 的日志,能够这么写:

$ 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 等级的日志,则能够省略 level 的设置,运用 java -Xlog:gc* -version 即可。

假如想知道有哪些个性化的 tag 能够挑选,能够用 java -Xlog:help 来找到一切可用的 tag。

阶段性小结

Java 17 晋级攻略(继续更新)

第二部分:输出到哪里(output)

默认情况下,日志会输出到 stdout,jvm 支撑以下三种输出方法:

  • stdout
  • stderr
  • file

一般来说咱们会把日志输出到文件中,便利后续进一步剖析

-Xlog:all=debug:file=/path_to_logs/app.log

还能够指定日志切割的巨细和方法

-Xlog:gc*:file=/path_to_logs/app.log:filesize=104857600,filecount=5

第三部分:日志 decorators

每条日志除了正常的信息以外,还有不少日志相关的上下文信息,在 jvm 中被称为 decorators,有下面这些可选项。

Option Description
time Current time and date in ISO-8601 format.
uptime Time since the start of the JVM in seconds and milliseconds (e.g., 6.567s).
timemillis The same value as generated by System.currentTimeMillis().
uptimemillis Milliseconds since the JVM started.
timenanos The same value as generated by System.nanoTime().
uptimenanos Nanoseconds since the JVM started.
pid The process identifier.
tid The thread identifier.
level The level associated with the log message.
tags The tag-set associated with the log message.

比方能够用 java -Xlog:all=debug:stdout:level,tags,time,uptime,pid -version 选项来打印日志。

[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 小结

输出格局如下:

-Xlog:[selectors]:[output]:[decorators][:output-options]
  • selectors 是多个 tag 和 level 的组合,起到了 what(过滤器)的作用,格局为 tag1[+tag2...][*][=level][,...]
  • decorators 是日志相关的描述信息,也能够理解为上下文
  • output 是输出相关的选项,一般咱们会装备为输出到文件,按文件巨细切割

这儿弥补一个知识点,便是默认值:

  • tag:all
  • level:info
  • output:stdout
  • decorators: uptime, level, tags

GC 参数搬迁

能够看到 GC 相关的参数都现已收拢到 Xlog 下,曾经的许多 Java8 下的参数现已被移除或许符号为过期。

比方 PrintGCDetails 现已被 -Xlog:gc* 替代:

java -XX:+PrintGCDetails -version
[0.001s][warning][gc] -XX:+PrintGCDetails is deprecated. Will use -Xlog:gc* instead.

常见的符号为抛弃的参数还有 -XX:+PrintGC-Xloggc:<filepath>,搬迁前后的参数如下:

旧参数 新参数
-XX:+PrintGCDetails -Xlog:gc*
-XX:+PrintGC -Xlog:gc
-Xloggc:<filepath> -Xlog:gc:file=<filepath>

除此之外,很多的 GC 的参数被移除,比方常用的参数 -XX:+PrintTenuringDistribution,Java17 会拒绝发动

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.

更具体的移除的参数如下

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

这些移除的参数大部分都能在新的日志体系下找到对应的参数,比方 PrintHeapAtGC 这个参数能够用 -Xlog:gc+heap=debug 来替代

$ 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

虽然理解起来不太直观,不过要记住 -XX:+PrintGCApplicationStoppedTime-XX+PrintGCApplicationConcurrentTime 这两个参数一起被 -Xlog:safepoint 替代。

还有一个常见的参数 -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

看一下这部分的源码的变迁,就能够知道确实是如此了,在 Java8 中,PSYoungGen::resize_spaces代码如下:

Java 17 晋级攻略(继续更新)

在 Java17 中,这部分日志打印被 gc+ergo 的标签日志替代:

Java 17 晋级攻略(继续更新)

还有一个分代 GC 中非常有用的参数 -XX:+PrintTenuringDistribution,现在被 gc+age=trace 替代

完好的参数变迁对应表如下:

旧 GC 参数 -XX:+… 对应新 GC 参数 GC 参数意义
PrintGC -Xloggc: gc Print message at garbage collection
PrintGCDetails -Xloggc: gc* Print more details at garbage collection
-verbose:gc gc=trace gc+heap=trace gc+heap+exit=trace gc+metaspace=trace gc+sweep=debug gc+heap+ergo=debug Verbose GC
PrintGCCause GC cause is now always logged Include GC cause in GC logging
PrintGCID GC ID is now always logged Print an identifier for each garbage collection
PrintGCApplicationStoppedTime safepoint Print the time the application has been stopped
PrintGCApplicationConcurrentTime safepoint Print the time the application has been running
PrintTenuringDistribution gc+age*=trace Print tenuring age information
PrintAdaptiveSizePolicy gc+ergo*=trace Print information about AdaptiveSizePolicy
PrintHeapAtGC gc+heap=debug Print heap layout before and after each GC
PrintHeapAtGCExtended gc+heap=trace Print extended information about the layout of the heap when -XX:+PrintHeapAtGC is set
PrintClassHistogramBeforeFullGC classhisto*=trace Print a class histogram before any major stop-world GC
PrintClassHistogramAfterFullGC classhisto*=trace Print a class histogram after any major stop-world GC
PrintStringDeduplicationStatistics gc+stringdedup*=debug Print string deduplication statistics
PrintJNIGCStalls gc+jni=debug Print diagnostic message when GC is stalled by JNI critical section
PrintReferenceGC gc+ref=debug Print times spent handling reference objects during GC
PrintGCTaskTimeStamps task*=debug Print timestamps for individual gc worker thread tasks
PrintTaskQueue gc+task+stats=trace Print taskqueue statistics for parallel collectors
PrintPLAB gc+plab=trace Print (survivor space) promotion LAB’s sizing decisions
PrintOldPLAB gc+plab=trace Print (old gen) promotion LAB’s sizing decisions
PrintPromotionFailure gc+promotion=debug Print additional diagnostic information following promotion failure
PrintTLAB gc+tlab=trace Print various TLAB related information (augmented with -XX:+TLABStats)
PrintTerminationStats gc+task+stats=debug Print termination statistics for parallel collectors
G1PrintHeapRegions gc+region=trace If set G1 will print information on which regions are being allocated and which are reclaimed
G1PrintRegionsLivenessInfo gc+liveness=trace Prints the liveness information for all regions in the heap at the end of a marking cycle
G1SummarizeConcMark gc+marking=trace Summarize concurrent mark info
G1SummarizeRSets gc+remset*=trace Summarize remembered set processing info
G1TraceConcRefinement gc+refine=debug Trace G1 concurrent refinement
G1TraceEagerReclaimHumongousObjects gc+humongous=debug Print some information about large object liveness at every young GC
G1TraceStringSymbolTableScrubbing gc+stringdedup=trace Trace information string and symbol table scrubbing
PrintParallelOldGCPhaseTimes gc+phases=trace Print the time taken by each phase in ParallelOldGC
CMSDumpAtPromotionFailure gc+promotion=trace Dump useful information about the state of the CMS old generation upon a promotion failure (complemented by flags CMSPrintChunksInDump or CMSPrintObjectsInDump)
CMSPrintEdenSurvivorChunks gc+heap=trace Print the eden and the survivor chunks used for the parallel initial mark or remark of the eden/survivor spaces
PrintCMSInitiationStatistics gc=trace Statistics for initiating a CMS collection
PrintCMSStatistics gc=debug (trace) gc+task=trace gc+survivor=trace log+sweep=debug (trace) Statistics for CMS (complemented by CMSVerifyReturnedBytes)
PrintFLSCensus gc+freelist+census=debug Census for CMS’ FreeListSpace
PrintFLSStatistics gc+freelist+stats=debug (trace) gc+freelist*=debug (trace) Statistics for CMS’ FreeListSpace
TraceCMSState gc+state=debug Trace the state of the CMS collection
TraceSafepoint safepoint=debug Trace application pauses due to VM operations in safepoints
TraceSafepointCleanupTime safepoint+cleanup=info break down of clean up tasks performed during safepoint
TraceAdaptativeGCBoundary heap+ergo=debug Trace young-old boundary moves
TraceDynamicGCThreads gc+task=trace Trace the dynamic GC thread usage
TraceMetadataHumongousAllocation gc+metaspace+alloc=debug Trace humongous metadata allocations
VerifySilently gc+verify=debug

举例

-XX:+PrintGCDetails                             // gc*
-XX:+PrintGCApplicationStoppedTime              // safepoint
-XX:+PrintGCApplicationConcurrentTime           // safepoint 
-XX:+PrintGCCause                               // 默认会输出
-XX:+PrintGCID                                  // 默认会输出
-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

运转相关

Java 17 晋级攻略(继续更新)

反射+私有 API 调用之伤

在 Java8 中,没有人能阻止你拜访特定的包,比方 sun.misc,对反射也没有约束,只要 setAccessible(true) 就能够了。Java9 模块化今后,一切都变了,只能经过 --add-exports--add-opens 来打破模块封装

  • --add-opens 导出特定的包
  • --add-opens 允许模块中特定包的类路径深度反射拜访

比方:

--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

关于 GC 算法的挑选

CMS 正式退出历史舞台,G1 正式接棒,ZGC 蓄势待发。在GC 算法的挑选上,现在来看 G1 还是最佳的挑选,ZGC 因为有内存占用被 OS 符号过高(三倍同享内存)虚高的问题,进程或许被 OOM-killer 杀掉。

ZGC 三倍 RES 内存

ZGC 底层用到了一个称之为染色指针的技术,运用三个视图(Marked0、Marked1 和 Remapped)来映射到同一块同享内存区域,原理如下:

#include <iostream>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstdio>
#include <cstdlib>
int main() {
    // shm_open()函数用来打开或许创立一个同享内存区,两个进程能够经过给shm_open()函数传递相同的名字以到达操作同一同享内存的目的
    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;
    // 创立一个同享内存后,默认巨细为0,所以需求设置同享内存巨细。ftruncate()函数可用来调整文件或许同享内存的巨细
    ::ftruncate(fd, size);
    int prot = PROT_READ | PROT_WRITE;
    // 创立同享内存后,需求将同享内存映射到调用进程的地址空间,可经过mmap()函数来完成
    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%xn", p1, *p1);
    ::printf("Address of addr2: %p, value is 0x%xn", p2, *p2);
    ::printf("Address of addr3: %p, value is 0x%xn", p3, *p3);
    ::getchar();
    *p2 = 0xcafebaba;
    ::printf("Address of addr1: %p, value is 0x%xn", p1, *p1);
    ::printf("Address of addr2: %p, value is 0x%xn", p2, *p2);
    ::printf("Address of addr3: %p, value is 0x%xn", p3, *p3);
    ::getchar();
    munmap(p1, size);
    munmap(p2, size);
    munmap(p3, size);
    shm_unlink("/test");
    std::cout << "hello" << std::endl;
}

你能够想象 p1、p2、p3 这三块内存区域便是 ZGC 中三种视图。

但是在 linux 计算中,虽然是同享内存,但是仍然会计算三次,比方 RES。

同一个使用,运用 G1 RES 显现占用 2G,ZGC 则显现占用 6G

java -XX:+AlwaysPreTouch -Xms2G -Xmx2G -XX:+UseZGC MyTest
java -XX:+AlwaysPreTouch -Xms2G -Xmx2G -XX:+UseG1GC MyTest                                                                                              

Java 17 晋级攻略(继续更新)

接下面咱们评论的都是 G1 相关的。

G1 参数调整

不要装备新生代的巨细

这个在《JVM G1 源码剖析和调优》一书里有具体的介绍,有两个主要的原因:

  • G1对内存的办理是不连续的,重新分配一个分区代价很低
  • G1 的需求依据方针中止时刻动态调整收集的分区的个数,假如不能调整新生代的巨细,那么 G1 或许不能满足中止时刻的要求

比如 -Xmn, -XX:NewSize, -XX:MaxNewSize, -XX:SurvivorRatio 都不要在 G1 中呈现,只需求操控最大、最小堆和方针暂停时刻即可

调整 -XX:InitiatingHeapOccupancyPercent 到适宜的值

IHOP 默认值为 45,这个值是发动并发符号的先决条件,只有当老时代内存栈总空间的 45% 之后才会发动并发符号任务。

添加这个值:导致并发符号或许花费更多的时刻,一起导致 YGC 和 Mixed-GC 收集时的分区数变少,能够依据整体使用占用的均匀内存来设置。