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
来做项目的依靠剖析
这样你就能够知道哪些库需求做晋级了。
参数搬迁
什么是 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
输出结果如下:
能够看到日志输出里,不仅有 GC 相关的日志,还有 os 线程相关的信息。事实上 java 的日志的生产者有非常多部分,比方 thread、class load、unload、safepoint、cds 等。
归根到底,日志打印,需求回答清楚三个问题:
- what:要输出什么信息(tag),以什么日志等级输出(level)
- where:输出到哪里(console 还是 file)
- decorators:日志怎么
输出什么信息(selectors)
首先来看 what 的部分,怎么指定要输出哪些信息,这个在 JVM 内部被称之为 selectors。
JVM 选用的是 <tag-set>=<level>
的方法来表示 selectors,默认情况下,tag 为all
,表示一切的 tag,level 为 INFO
,java -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 之间用逗号分隔即可。比方一起输出 gc
和 gc+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。
阶段性小结
第二部分:输出到哪里(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
代码如下:
在 Java17 中,这部分日志打印被 gc+ergo 的标签日志替代:
还有一个分代 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
运转相关
反射+私有 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
接下面咱们评论的都是 G1 相关的。
G1 参数调整
不要装备新生代的巨细
这个在《JVM G1 源码剖析和调优》一书里有具体的介绍,有两个主要的原因:
- G1对内存的办理是不连续的,重新分配一个分区代价很低
- G1 的需求依据方针中止时刻动态调整收集的分区的个数,假如不能调整新生代的巨细,那么 G1 或许不能满足中止时刻的要求
比如 -Xmn, -XX:NewSize, -XX:MaxNewSize, -XX:SurvivorRatio
都不要在 G1 中呈现,只需求操控最大、最小堆和方针暂停时刻即可
调整 -XX:InitiatingHeapOccupancyPercent
到适宜的值
IHOP 默认值为 45,这个值是发动并发符号的先决条件,只有当老时代内存栈总空间的 45% 之后才会发动并发符号任务。
添加这个值:导致并发符号或许花费更多的时刻,一起导致 YGC 和 Mixed-GC 收集时的分区数变少,能够依据整体使用占用的均匀内存来设置。