因为历史原因,Java GC已经有几代的实现,本文回顾java 11 ZGC之前的GC实现。
从名字上看,GC是从内存中寻找并删除垃圾的过程,但实际上GC会追踪JVM heap中每一个对象,并删掉无用对象回收内存。
简单来讲,GC由Mark和Sweep两个简单步骤实现。
jvm这样实现GC有着哪些优点和缺点呢?
优点:
在计算机编程领域中,迷途指针,或称悬空指针、野指针,指的是不指向任何合法的对象的指针。----wikipedia
在计算机科学中,内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存。----wikipedia
缺点:
在java11之前,JVM曾有四种GC实现:
Serial GC是最简单的GC实现,因为它基本上可以在单线程 上工作。但是这个GC实现会在GC运行时冻结所有的应用程序线程。因此,它不适合在服务器等多线程应用中使用。
Serial GC对大多有大块暂停时间的应用(比如各种客户端) 比较适合。要启用Serial GC,需要下列参数:
java -XX:+UseSerialGC -jar Application.java
Parallel GC是JVM默认的的GC,也被称为Throughput GC。不像串行垃圾收集器,Parallel使用多线程来管理heap空间。但是它在执行GC的过程中依然会暂停其他应用程序线程。
如果使用这个GC,我们可以详细的指定GC线程数量(threads)和暂停时间(pause time),吞吐量(throughput)和占用空间(footprint)(堆大小)
垃圾收集器线程的数量可以由命令行-XX:ParallelGCThreads=<N>
控制
最大暂停时间(两次GC间隔时间,毫秒计算)可由命令行 -XX:MaxGCPauseMillis=<N>
控制
最大吞吐量(衡量花费在垃圾收集上的时间和之外的时间)由命令行XX:GCTimeRatio=<N>
控制
最大堆占用空间(程序运行时所需的最大堆内存量)由命令行-Xmx<N>
控制
要开启Parallel GC,使用下列参数:
java -XX:+UseParallelGC -jar Application.java
Concurrent Mark Sweep(CMS)GC使用多个垃圾回收线程做垃圾回收。它是为更短的垃圾回收暂停时间而设计,并且程序运行时可以和垃圾回收器共享处理器资源。
简而言之,采用此类GC的程序平均响应更慢,但是不会在执行GC时暂停响应。
需要注意的是,由于GC是并发的,如果并发操作正在运行,这时显式调用像System.gc()的垃圾回收,会导致并发模式失败(Concurrent Mode Failure)。
如果超过98%的时间用于CMS GC且少于2%的时间回收堆,CMS收集器将会抛出OutOfMemoryError
。如有必要,可以通过命令行XX:-UseGCOverheadLimit
来禁用这个功能。
要启用CMS垃圾收集器,可以使用下列命令:
java -XX:+UseParNewGC -jar Application.java
G1(Garbage First) GC被设计用于面向运行在多核处理器上且有极大内存空间的程序。G1是取代CMS的下一代收集器,因为它有更高的性能效率。
不像其他收集器,G1收集器会将堆划分为一组大小相等的堆区域,每个堆区域都是一块连续的虚拟内存。当执行GC时,G1进入一个并发全局标记阶段(阶段1:Marking),以确定整个堆中对象的存活性。
Mark阶段完成后,G1会了解哪块区域大部分应该被清空。G1首先回收这些区域的内存,这样可以达到快速回收大量可用内存的目的(阶段2:Sweeping)。这就是为什么这种GC方法被称为Garbage-First。
开启G1 GC,可以使用下面的参数:
java -XX:+UseG1GC -jar Application.java