JVM 預設啟動引數中,DisableExplicitGC 為 false,ExplicitGCInvokesConcurrent 為 false,對於大多數 GC (除了 ZGC 的其他 GC,包括 CMS,G1,Shenandoah GC 等等),都是會進行 FullGC 的,並且都是同步 GC 的,其中底層的原理會在另一篇詳細分析,我們先來搞清楚為什麼要留這樣一個介面呢?
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
.
2. 使用了 WeakReference, SoftReference 的程式,需要相應的 GC 回收。
對於 WeakReference,只要發生 GC,無論是 Young GC 還是 FullGC 就會被回收。SoftReference 只有在 FullGC 的時候才會被回收。當我們程式想主動對於這些引用進行回收的時候,需要能觸發 GC 的方法,這就用到了System.gc()
。
3. 測試,學習 JVM 機制的時候
有些時候,我們為了測試,學習 JVM 的某些機制,需要讓 JVM 做一次 GC 之後開始,這也會用到System.gc()
。
微信搜尋“我的程式設計喵”關注公眾號,每日一刷,輕鬆提升技術,斬獲各種offer: