2021-2-26:爲何須要 System.gc() ?

JVM 默認啓動參數中,DisableExplicitGC 爲 false,ExplicitGCInvokesConcurrent 爲 false,對於大多數 GC (除了 ZGC 的其餘 GC,包括 CMS,G1,Shenandoah GC 等等),都是會進行 FullGC 的,而且都是同步 GC 的,其中底層的原理會在另外一篇詳細分析,咱們先來搞清楚爲何要留這樣一個接口呢?java

1. 使用並管理堆外內存的框架,須要 Full GC 的機制觸發堆外內存回收

JVM 的內存,不止堆內存,還有其餘不少塊,經過 Native Memory Tracking 能夠看到:編程

Native Memory Tracking:

Total: reserved=6308603KB, committed=4822083KB
-                 Java Heap (reserved=4194304KB, committed=4194304KB)
                            (mmap: reserved=4194304KB, committed=4194304KB) 

-                     Class (reserved=1161041KB, committed=126673KB)
                            (classes #21662)
                            (  instance classes #20542, array classes #1120)
                            (malloc=3921KB #64030) 
                            (mmap: reserved=1157120KB, committed=122752KB) 
                            (  Metadata:   )
                            (    reserved=108544KB, committed=107520KB)
                            (    used=105411KB)
                            (    free=2109KB)
                            (    waste=0KB =0.00%)
                            (  Class space:)
                            (    reserved=1048576KB, committed=15232KB)
                            (    used=13918KB)
                            (    free=1314KB)
                            (    waste=0KB =0.00%)

-                    Thread (reserved=355251KB, committed=86023KB)
                            (thread #673)
                            (stack: reserved=353372KB, committed=84144KB)
                            (malloc=1090KB #4039) 
                            (arena=789KB #1344)

-                      Code (reserved=252395KB, committed=69471KB)
                            (malloc=4707KB #17917) 
                            (mmap: reserved=247688KB, committed=64764KB) 

-                        GC (reserved=199635KB, committed=199635KB)
                            (malloc=11079KB #29639) 
                            (mmap: reserved=188556KB, committed=188556KB) 

-                  Compiler (reserved=2605KB, committed=2605KB)
                            (malloc=2474KB #2357) 
                            (arena=131KB #5)

-                  Internal (reserved=3643KB, committed=3643KB)
                            (malloc=3611KB #8683) 
                            (mmap: reserved=32KB, committed=32KB) 

-                     Other (reserved=67891KB, committed=67891KB)
                            (malloc=67891KB #2859) 

-                    Symbol (reserved=26220KB, committed=26220KB)
                            (malloc=22664KB #292684) 
                            (arena=3556KB #1)

-    Native Memory Tracking (reserved=7616KB, committed=7616KB)
                            (malloc=585KB #8238) 
                            (tracking overhead=7031KB)

-               Arena Chunk (reserved=10911KB, committed=10911KB)
                            (malloc=10911KB) 

-                   Tracing (reserved=25937KB, committed=25937KB)
                            (malloc=25937KB #8666) 

-                   Logging (reserved=5KB, committed=5KB)
                            (malloc=5KB #196) 

-                 Arguments (reserved=18KB, committed=18KB)
                            (malloc=18KB #486) 

-                    Module (reserved=532KB, committed=532KB)
                            (malloc=532KB #3579) 

-              Synchronizer (reserved=591KB, committed=591KB)
                            (malloc=591KB #4777) 

-                 Safepoint (reserved=8KB, committed=8KB)
                            (mmap: reserved=8KB, committed=8KB)
  • Java Heap: 堆內存,即-Xmx限制的最大堆大小的內存。
  • Class:加載的類與方法信息,其實就是 metaspace,包含兩部分: 一是 metadata,被-XX:MaxMetaspaceSize限制最大大小,另外是 class space,被-XX:CompressedClassSpaceSize限制最大大小
  • Thread:線程與線程棧佔用內存,每一個線程棧佔用大小受-Xss限制,可是總大小沒有限制。
  • Code:JIT 即時編譯後(C1 C2 編譯器優化)的代碼佔用內存,受-XX:ReservedCodeCacheSize限制
  • GC:垃圾回收佔用內存,例如垃圾回收須要的 CardTable,標記數,區域劃分記錄,還有標記 GC Root 等等,都須要內存。這個不受限制,通常不會很大的。
  • Compiler:C1 C2 編譯器自己的代碼和標記佔用的內存,這個不受限制,通常不會很大的
  • Internal:命令行解析,JVMTI 使用的內存,這個不受限制,通常不會很大的
  • Symbol: 常量池佔用的大小,字符串常量池受-XX:StringTableSize個數限制,總內存大小不受限制
  • Native Memory Tracking:內存採集自己佔用的內存大小,若是沒有打開採集(那就看不到這個了,哈哈),就不會佔用,這個不受限制,通常不會很大的
  • Arena Chunk:全部經過 arena 方式分配的內存,這個不受限制,通常不會很大的
  • Tracing:全部採集佔用的內存,若是開啓了 JFR 則主要是 JFR 佔用的內存。這個不受限制,通常不會很大的
  • Logging,Arguments,Module,Synchronizer,Safepoint,Other,這些通常咱們不會關心。

除了 Native Memory Tracking 記錄的內存使用,還有兩種內存 Native Memory Tracking 沒有記錄,那就是:微信

  • Direct Buffer:直接內存
  • MMap Buffer:文件映射內存

針對除了堆內存之外,其餘的內存,有些也是須要 GC 的。例如:MetaSpace,CodeCache,Direct Buffer,MMap Buffer 等等。早期在 Java 8 以前的 JVM,對於這些內存回收的機制並不完善,不少狀況下都須要 FullGC 掃描整個堆才能肯定這些區域中哪些內存能夠回收。框架

有一些框架,大量使用並管理了這些堆外空間。例如 netty 使用了 Direct Buffer,Kafka 和 RocketMQ 使用了 Direct Buffer 和 MMap Buffer。他們都是提早從系統申請好一塊內存,以後管理起來並使用。在空間不足時,繼續向系統申請,而且也會有縮容。例如 netty,在使用的 Direct Buffer 達到-XX:MaxDirectMemorySize的限制以後,則會先嚐試將不可達的Reference對象加入Reference鏈表中,依賴Reference的內部守護線程觸發能夠被回收DirectByteBuffer關聯的Cleaner的run()方法。若是內存仍是不足, 則執行System.gc(),指望觸發full gc,來回收堆內存中的DirectByteBuffer對象來觸發堆外內存回收,若是仍是超過限制,則拋出java.lang.OutOfMemoryError.ide

2. 使用了 WeakReference, SoftReference 的程序,須要相應的 GC 回收。

對於 WeakReference,只要發生 GC,不管是 Young GC 仍是 FullGC 就會被回收。SoftReference 只有在 FullGC 的時候纔會被回收。當咱們程序想主動對於這些引用進行回收的時候,須要能觸發 GC 的方法,這就用到了System.gc()學習

3. 測試,學習 JVM 機制的時候

有些時候,咱們爲了測試,學習 JVM 的某些機制,須要讓 JVM 作一次 GC 以後開始,這也會用到System.gc()測試

微信搜索「個人編程喵」關注公衆號,每日一刷,輕鬆提高技術,斬獲各類offer優化

image

相關文章
相關標籤/搜索