<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          你還在用 Java 8?手把手教你從 Java 8 升級到 Java 17 全過程,真香!!

          共 24126字,需瀏覽 49分鐘

           ·

          2022-11-22 08:33

          點擊關(guān)注公眾號,Java干貨及時送達

          作者:挖坑的張師傅
          來源:https://juejin.cn/user/430664257374270

          Java 8 是舊時代的 Java 6,還不快升級,??。

          最近在做 Java8 到 Java17 的遷移工作,前期做了一些準(zhǔn)備,過程中的一些信息記錄如下(持續(xù)更新。。。)

          分為幾個部分:

          • 編譯相關(guān)
          • 參數(shù)遷移相關(guān)
          • 運行相關(guān)

          編譯相關(guān)

          JEP 320

          在 Java11 中引入了一個提案 JEP 320: Remove the Java EE and CORBA Modules 提案,移除了 Java EE and CORBA 的模塊,如果項目中用到需要手動引入。比如代碼中用到了 javax.annotation.* 下的包:

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

          在編譯時會找不到相關(guān)的類。這是因為 Java EE 已經(jīng)在 Java 9 中被標(biāo)記為 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]

          對應(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.* 下的包

          錯誤信息如下:

          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,而且是低版本的話,就會出現(xiàn),lombok 的原理是在編譯期做一些手腳,用到了 com.sun.tools.javac 下的文件,升級到最新版可以解決。ps,個人很不喜歡 lombok, 調(diào)試的時候代碼和 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 的字節(jié)碼,低于 1.6.0 的編譯會直接報錯。

          廢棄依賴分析

          可以用 jdeps --jdk-internals --multi-release 17 --class-path . encloud-api.jar 來做項目的依賴分析

          這樣你就可以知道哪些庫需要做升級了。

          推薦一個開源免費的 Spring Boot 最全教程:

          https://github.com/javastacks/spring-boot-best-practice

          參數(shù)遷移

          什么是 Unified Logging

          在 Java 領(lǐng)域,有廣為人知的日志框架,slf4j、log4j 等,這些框架提供了統(tǒng)一的編程接口,讓用戶可以通過簡單的配置實現(xiàn)日志輸出的個性化配置,比如日志 tag、級別(info、debug 等)、上下文(線程 id、行號、時間等),在 JVM 內(nèi)部之前一直缺乏這樣的規(guī)范,于是出來了 Unified Logging,實現(xiàn)了日志格式的大一統(tǒng),這就是我們接下來要介紹的重點 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)的信息。事實上 java 的日志的生產(chǎn)者有非常多部分,比如 thread、class load、unload、safepoint、cds 等。

          歸根到底,日志打印,需要回答清楚三個問題:

          • what:要輸出什么信息(tag),以什么日志級別輸出(level)
          • where:輸出到哪里(console 還是 file)
          • decorators:日志如何

          輸出什么信息(selectors)

          首先來看 what 的部分,如何指定要輸出哪些信息,這個在 JVM 內(nèi)部被稱之為 selectors。

          JVM 采用的是 <tag-set>=<level>的形式來表示 selectors,默認(rèn)情況下,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 的日志信息。《46 張 PPT 弄懂JVM 調(diào)優(yōu)》分享給你看下。

          不過這里有一個比較坑的點是,這里的 tag 匹配規(guī)則是精確匹配,如果某條日志的 tag 是 gc,metaspace,通過上面的規(guī)則是匹配不到的,我們可以手動指定的方式來輸出。

          $ 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

          當(dāng)然這么搞是很麻煩的,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 的設(shè)置,使用 java -Xlog:gc* -version 即可。

          如果想知道有哪些個性化的 tag 可以選擇,可以用 java -Xlog:help 來找到所有可用的 tag。

          階段性小結(jié)

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

          默認(rèn)情況下,日志會輸出到 stdout,jvm 支持以下三種輸出方式:

          • stdout
          • stderr
          • file

          一般而言我們會把日志輸出到文件中,方便后續(xù)進一步分析

          -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 中被稱為 decorators,有下面這些可選項。

          另外,如果你近期準(zhǔn)備面試跳槽,建議在Java面試庫小程序在線刷題,涵蓋 2000+ 道 Java 面試題,幾乎覆蓋了所有主流技術(shù)面試題。

          OptionDescription
          timeCurrent time and date in ISO-8601 format.
          uptimeTime since the start of the JVM in seconds and milliseconds (e.g., 6.567s).
          timemillisThe same value as generated by System.currentTimeMillis().
          uptimemillisMilliseconds since the JVM started.
          timenanosThe same value as generated by System.nanoTime().
          uptimenanosNanoseconds since the JVM started.
          pidThe process identifier.
          tidThe thread identifier.
          levelThe level associated with the log message.
          tagsThe 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 小結(jié)

          輸出格式如下:

          -Xlog:[selectors]:[output]:[decorators][:output-options]
          • selectors 是多個 tag 和 level 的組合,起到了 what(過濾器)的作用,格式為 tag1[+tag2...][*][=level][,...]
          • decorators 是日志相關(guān)的描述信息,也可以理解為上下文
          • output 是輸出相關(guā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)記為過期。

          比如 PrintGCDetails 已經(jīng)被 -Xlog:gc* 取代:

          java -XX:+PrintGCDetails -version

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

          常見的標(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 會拒絕啟動

          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ù)大部分都能在新的日志體系下找到對應(yīng)的參數(shù),比如 PrintHeapAtGC 這個參數(shù)可以用 -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 這兩個參數(shù)一起被 -Xlog:safepoint 取代。

          點擊關(guān)注公眾號,Java干貨及時送達

          還有一個常見的參數(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

          看一下這部分的源碼的變遷,就可以知道確實是如此了,在 Java8 中,PSYoungGen::resize_spaces代碼如下:

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

          還有一個分代 GC 中非常有用的參數(shù) -XX:+PrintTenuringDistribution,現(xiàn)在被 gc+age=trace 取代。

          另外,如果你近期準(zhǔn)備面試跳槽,建議在Java面試庫小程序在線刷題,涵蓋 2000+ 道 Java 面試題,幾乎覆蓋了所有主流技術(shù)面試題。

          完整的參數(shù)變遷對應(yīng)表如下:

          舊 GC 參數(shù) -XX:+…對應(yīng)新 GC 參數(shù)GC 參數(shù)含義
          PrintGC -Xloggc:gcPrint message at garbage collection
          PrintGCDetails -Xloggc:gc*Print more details at garbage collection
          -verbose:gcgc=trace gc+heap=trace gc+heap+exit=trace gc+metaspace=trace gc+sweep=debug gc+heap+ergo=debugVerbose GC
          PrintGCCauseGC cause is now always loggedInclude GC cause in GC logging
          PrintGCIDGC ID is now always loggedPrint an identifier for each garbage collection
          PrintGCApplicationStoppedTimesafepointPrint the time the application has been stopped
          PrintGCApplicationConcurrentTimesafepointPrint the time the application has been running
          PrintTenuringDistributiongc+age*=tracePrint tenuring age information
          PrintAdaptiveSizePolicygc+ergo*=tracePrint information about AdaptiveSizePolicy
          PrintHeapAtGCgc+heap=debugPrint heap layout before and after each GC
          PrintHeapAtGCExtendedgc+heap=tracePrint extended information about the layout of the heap when -XX:+PrintHeapAtGC is set
          PrintClassHistogramBeforeFullGCclasshisto*=tracePrint a class histogram before any major stop-world GC
          PrintClassHistogramAfterFullGCclasshisto*=tracePrint a class histogram after any major stop-world GC
          PrintStringDeduplicationStatisticsgc+stringdedup*=debugPrint string deduplication statistics
          PrintJNIGCStallsgc+jni=debugPrint diagnostic message when GC is stalled by JNI critical section
          PrintReferenceGCgc+ref=debugPrint times spent handling reference objects during GC
          PrintGCTaskTimeStampstask*=debugPrint timestamps for individual gc worker thread tasks
          PrintTaskQueuegc+task+stats=tracePrint taskqueue statistics for parallel collectors
          PrintPLABgc+plab=tracePrint (survivor space) promotion LAB’s sizing decisions
          PrintOldPLABgc+plab=tracePrint (old gen) promotion LAB’s sizing decisions
          PrintPromotionFailuregc+promotion=debugPrint additional diagnostic information following promotion failure
          PrintTLABgc+tlab=tracePrint various TLAB related information (augmented with -XX:+TLABStats)
          PrintTerminationStatsgc+task+stats=debugPrint termination statistics for parallel collectors
          G1PrintHeapRegionsgc+region=traceIf set G1 will print information on which regions are being allocated and which are reclaimed
          G1PrintRegionsLivenessInfogc+liveness=tracePrints the liveness information for all regions in the heap at the end of a marking cycle
          G1SummarizeConcMarkgc+marking=traceSummarize concurrent mark info
          G1SummarizeRSetsgc+remset*=traceSummarize remembered set processing info
          G1TraceConcRefinementgc+refine=debugTrace G1 concurrent refinement
          G1TraceEagerReclaimHumongousObjectsgc+humongous=debugPrint some information about large object liveness at every young GC
          G1TraceStringSymbolTableScrubbinggc+stringdedup=traceTrace information string and symbol table scrubbing
          PrintParallelOldGCPhaseTimesgc+phases=tracePrint the time taken by each phase in ParallelOldGC
          CMSDumpAtPromotionFailuregc+promotion=traceDump useful information about the state of the CMS old generation upon a promotion failure (complemented by flags CMSPrintChunksInDump or CMSPrintObjectsInDump)
          CMSPrintEdenSurvivorChunksgc+heap=tracePrint the eden and the survivor chunks used for the parallel initial mark or remark of the eden/survivor spaces
          PrintCMSInitiationStatisticsgc=traceStatistics for initiating a CMS collection
          PrintCMSStatisticsgc=debug (trace) gc+task=trace gc+survivor=trace log+sweep=debug (trace)Statistics for CMS (complemented by CMSVerifyReturnedBytes)
          PrintFLSCensusgc+freelist+census=debugCensus for CMS' FreeListSpace
          PrintFLSStatisticsgc+freelist+stats=debug (trace) gc+freelist*=debug (trace)Statistics for CMS' FreeListSpace
          TraceCMSStategc+state=debugTrace the state of the CMS collection
          TraceSafepointsafepoint=debugTrace application pauses due to VM operations in safepoints
          TraceSafepointCleanupTimesafepoint+cleanup=infobreak down of clean up tasks performed during safepoint
          TraceAdaptativeGCBoundaryheap+ergo=debugTrace young-old boundary moves
          TraceDynamicGCThreadsgc+task=traceTrace the dynamic GC thread usage
          TraceMetadataHumongousAllocationgc+metaspace+alloc=debugTrace humongous metadata allocations
          VerifySilentlygc+verify=debug

          舉例

          -XX:+PrintGCDetails                           \  // gc*
          -XX:+PrintGCApplicationStoppedTime            \  /
          / safepoint
          -XX:+PrintGCApplicationConcurrentTime         \  /
          / safepoint
          -XX:+PrintGCCause                             \  /
          / 默認(rèn)會輸出
          -XX:+PrintGCID                                \  /
          / 默認(rèn)會輸出
          -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

          運行相關(guān)

          反射+私有 API 調(diào)用之傷

          在 Java8 中,沒有人能阻止你訪問特定的包,比如 sun.misc,對反射也沒有限制,只要 setAccessible(true) 就可以了。Java9 模塊化以后,一切都變了,只能通過 --add-exports--add-opens 來打破模塊封裝

          • --add-opens 導(dǎo)出特定的包
          • --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

          關(guān)于 GC 算法的選擇

          CMS 正式退出歷史舞臺,G1 正式接棒,ZGC 蓄勢待發(fā)。在GC 算法的選擇上,目前來看 G1 還是最佳的選擇,ZGC 因為有內(nèi)存占用被 OS 標(biāo)記過高(三倍共享內(nèi)存)虛高的問題,進程可能被 OOM-killer 殺掉。

          ZGC 三倍 RES 內(nèi)存

          ZGC 底層用到了一個稱之為染色指針的技術(shù),使用三個視圖(Marked0、Marked1 和 Remapped)來映射到同一塊共享內(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ù)用來打開或者創(chuàng)建一個共享內(nèi)存區(qū),兩個進程可以通過給shm_open()函數(shù)傳遞相同的名字以達到操作同一共享內(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)建一個共享內(nèi)存后,默認(rèn)大小為0,所以需要設(shè)置共享內(nèi)存大小。ftruncate()函數(shù)可用來調(diào)整文件或者共享內(nèi)存的大小
              ::ftruncate(fd, size);
              int prot = PROT_READ | PROT_WRITE;
              // 創(chuàng)建共享內(nèi)存后,需要將共享內(nèi)存映射到調(diào)用進程的地址空間,可通過mmap()函數(shù)來完成
              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)計中,雖然是共享內(nèi)存,但是依然會統(tǒng)計三次,比如 RES。

          同一個應(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)整

          不要配置新生代的大小

          這個在《JVM G1 源碼分析和調(diào)優(yōu)》一書里有詳細(xì)的介紹,有兩個主要的原因:

          • G1對內(nèi)存的管理是不連續(xù)的,重新分配一個分區(qū)代價很低
          • G1 的需要根據(jù)目標(biāo)停頓時間動態(tài)調(diào)整搜集的分區(qū)的個數(shù),如果不能調(diào)整新生代的大小,那么 G1 可能不能滿足停頓時間的要求

          諸如 -Xmn, -XX:NewSize, -XX:MaxNewSize, -XX:SurvivorRatio 都不要在 G1 中出現(xiàn),只需要控制最大、最小堆和目標(biāo)暫停時間即可

          調(diào)整 -XX:InitiatingHeapOccupancyPercent 到合適的值

          IHOP 默認(rèn)值為 45,這個值是啟動并發(fā)標(biāo)記的先決條件,只有當(dāng)老年代內(nèi)存棧總空間的 45% 之后才會啟動并發(fā)標(biāo)記任務(wù)。

          增加這個值:導(dǎo)致并發(fā)標(biāo)記可能花費更多的時間,同時導(dǎo)致 YGC 和 Mixed-GC 收集時的分區(qū)數(shù)變少,可以根據(jù)整體應(yīng)用占用的平均內(nèi)存來設(shè)置。

          End


          Spring Boot 學(xué)習(xí)筆記,這個太全了!

          23 種設(shè)計模式實戰(zhàn)(很全)

          Nacos 2.1.1 正式發(fā)布,真心強!

          Spring Cloud Alibaba 最新重磅發(fā)布!

          Stream 中的 map、peek、foreach 方法的區(qū)別?

          Spring Cloud 微服務(wù)最新課程!

          瀏覽 73
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  二区色| 在线观看视频一区 | 91AV三级影院 | 欧美色图第三页 | 影音先锋在线亚洲av |