为什么需要 System.gc() ?

简介: 为什么需要 System.gc() ?

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

相关文章
|
7月前
|
Java
JVM打印GC信息
JVM打印GC信息
|
8月前
Cannot request memory (Xms 65536 kb, Xmx 270336 kb) from system for job test, sleep for 60 secs and
Azkaban任务因内存不足持续运行,日志显示无法申请65536kb至270336kb内存。系统要求至少3GB内存,但当前executor节点内存低于此阈值。解决方案包括释放内存、增加内存或关闭内存检查(通过在`commonprivate.properties`设置`memCheck.enabled=false`)。
42 5
调用 System.gc() 后究竟发生了什么?
调用 System.gc() 后究竟发生了什么?
|
算法 Java API
Java虚拟机System.gc()解析
对于Java语言来说是不用刻意手动去释放内存,同时,也尽可能不需要手动去干预Java虚拟机的GC行为。在本篇文章中,我们试图从多个方面去解析有关System.gc()API调用的最常见问题。希望对需要了解这块技术的朋友有所帮助。
209 0
|
存储 监控 算法
System.gc()与Runtime.gc()的区别
System.gc()与Runtime.gc()的区别
209 0
|
Java
Java Finalize和System.gc方法
Java Finalize和System.gc方法
116 0
|
算法 Java
System.gc()做了什么?
System.gc()我们都知道是手动垃圾回收,这点无需多说,今天我们来了解一下System.gc()是怎么进行垃圾回收的。
321 0
|
消息中间件 Java 测试技术
JVM相关 - 深入理解 System.gc()
本文基于 Java 17-ea,但是相关设计在 Java 11 之后是大致一样的 我们经常在面试中询问 System.gc() 究竟会不会 立刻 触发 Full GC ,网上也有很多人给出了答案,但是这些答案都有些过时了。本文基于最新的信息 Java 的下一个即将发布的 LTS 版本 Java 17(ea)的源代码,深入解析 System.gc() 背后的故事。
System类以及垃圾回收gc()使用详情
System类以及垃圾回收gc()使用详情
|
算法 Java
Minor GC 和 Full GC 有什么不同
Minor GC 和 Full GC 有什么不同
195 0

热门文章

最新文章