JVM(Java Virtual Machine)的GC(Garbage Collection,垃圾回收)是Java语言的一个重要特性,它负责自动管理内存,释放那些不再被使用的对象所占用的内存空间。以下是对JVM GC的详细介绍:
- GC的基本概念
定义:GC即垃圾回收,是指JVM用于释放那些不再被使用的对象所占用的内存。
目的:清除不再使用的对象,防止内存泄漏,提高内存利用率。 - GC的工作原理
JVM通过两种主要方法来识别垃圾对象:
引用计数:虽然这种方法简单有效,但存在循环引用的问题,因此在现代JVM中较少使用。
可达性分析:JVM使用的主流方法。以根集合(如栈上的局部变量、静态变量等)为起点,通过引用链遍历对象图,所有可达的对象都被视为存活对象,其余则为垃圾对象。
- GC算法
主要垃圾回收算法有3种,分别是标记清除算法、标记复制算法、标记整理算法。
(1)标记清除算法:算法核心原理,它会给所有存活对象打上标记,那么没有被标记的对象,就是需要被回收的垃圾对象,这些垃圾对象会被垃圾回收器直接回收。这种算法会产生比较多的内存碎片,而且这些内存碎片,会随着系统运行时间的增长,出现无法分配空间或连续的内存空间,会导致更加频繁的GC操作。另外这种方式清理出来的空闲内存是不连续的,产生内存碎片,需要维护一个空闲列表。
(2)标记复制算法:它把内存分为两等份,每次只需要使用其中的一份,等到正在使用的这份存满之后,它就会标记出存活的对象,然后再把现在存活的对象,拷贝到另外一份闲置的内存中,被留在原来那块内存中的对象,就会全部被垃圾回收器回收。原来闲置的那个内存空间就会变成正在使用的状态。原来使用的那块内存经垃圾回收后,就变成闲置的内存等待继续使用。这就是标记复制算法一次完整的垃圾回收过程。标记复制算法会一直重复这个循环。这种算法实际使用的内存只占50%,另外50%是闲置的,比较浪费内存空间。如果存在大量复制对象,垃圾回收的耗时会比较长。这种算法更适合处理一些活动对象比较少,垃圾对象比较多的场景,所以适用于新生代(速度快,效率高)。
(3)标记整理算法:它先标记出存活的对象,然后把所有的存活对象,整理到内存的另外一端,没有被标记的对象就是可以被覆盖,或是被释放。这样就解决了空间碎片化问题,但从垃圾收集和清理效率来看,增加了一个移动的动作,所以耗时会更久。此算法适合老年代。
- 分代收集(Generational Collection)
因为 Java 对象基本上都是临时的对象,很快就会被回收,所以JVM 的内存是分代设计的,根据对象在内存中的存活时间,分为年轻代、老年代、永久代。年轻代采用的是标记复制算法,在每次复制的时候,存活下来的对象会很少。而老年代是经历过几次GC的对象,JVM 会认为它可能会继续存活下去,不大适合去采用标记复制算法,所以老年代采用的是标记清除算法或标记-整理算法。比如说CMS这种回收器,采用的就是标记清除的方式。那么永久代是一直会是存活的对象,只有在触发 Full GC 的时候,才会被回收。所以永久代的对象创建过多的话,会比较容易出现内存溢出。最典型的场景是在 JSP 页面比较多的情况下,容易出现永久代的内存溢出。
- JVM中的GC收集器
JVM提供了多种GC收集器,每种收集器都有其特定的应用场景和优缺点。常见的收集器包括:
Serial GC:单线程执行GC,适用于单核CPU、新生代空间较小及对暂停时间要求不高的应用。
Parallel GC:多线程执行GC,适用于多核CPU、对吞吐量有高要求的应用。
CMS(Concurrent Mark Sweep)GC:一种以最短回收停顿时间为目标的收集器,适用于对响应时间敏感的应用。但它在并发收集时会产生浮动垃圾,且需要预留一定的空间来避免频繁的全堆GC。
G1(Garbage-First)GC:面向服务端应用的垃圾收集器,它设计用来满足在堆内存不断增大的情况下,减少停顿时间的需求。G1收集器将堆划分为多个大小相等的独立区域(Region),并优先收集垃圾最多的区域。
- GC对JVM内存的影响
GC主要影响JVM中的堆内存区域。堆内存被划分为新生代和老年代,GC在这两个区域中分别采用不同的算法进行回收。GC的执行会导致应用程序的暂停(Stop-The-World),但现代JVM通过优化算法和收集器,已经大大减少了这种暂停的时间。
总之,JVM的GC是Java语言内存管理的重要机制,它通过自动化的方式释放不再使用的内存空间,提高了程序的稳定性和可靠性。同时,JVM提供了多种GC算法和收集器,以满足不同应用场景的需求。