GC回收垃圾流程第一步是定位内存空间中没有引用到的对象,然后才能进行回收。那么GC是如何定位这些内存中没有用到的对象呢?
一、引用计数器算法(废弃)
引用计数器算法是给每一个对象设置一个计数器,当有地方引用这个对象的时候,计数器+1;当引用失效的时候,计数器减1;当计数器为0的时候,JVM就默认对象不再使用,是“垃圾”了,这种定位算法存在一个缺点:就是无法解决循环依赖的问题。当A引用B,B也引用A的时候,此时AB对象的引用都不为0,此时也就无法垃圾回收,所以一般主流虚拟机都不采用这个方法。
二、根搜索算法(可达性分析算法)
根搜索算法是通过“GC ROOT”根对象作为起始节点,从该节点开始往下搜索,搜索通过的路径称之为“引用链”,当一个对象没有被GC Root 的引用链连接的时候,说明这个对象是不可用的且无法达到的,然后才可以被GC进行回收。
GC Root对象包括:
- 虚拟机栈中引用的对象
- 方法区域的类静态属性引用的对象
- 方法区域中常量引用的对象
- 本地方法栈中JNI(Native方法)引用的对象
经过以上算法分析后,确定好了要回收的对象,可以进行下一步回收了,以下是常见的GC回收算法:
三、回收算法
(一)标记清除法
标记-清除算法包括两个阶段:“标记”和“清除”,
在标记阶段,遍历堆然后确定所有要回收的对象,并做标记;
在清除阶段,将标记阶段确定不可用的对象清除;
标记-清除算法是基础的收集算法,标记和清除阶段的效率不高,而且清除后会产生大量的不连续空间(内存碎片),这样当程序需要分配大内存对象的时候,可能无法找到足够的连续空间
(二)标记复制算法
复制算法就是把内存分成大小相等的两块,每次使用其中一块,当垃圾回收的时候,把存活的对象复制到另外一块内存上,然后把原来的那块内存清理掉。复制算法实现简单,运行效率高,并且解决了标记清除法内存碎片的问题。缺点是由于每次只能使用其中的一半,造成内存的利用率不高。
(三)标记-整理算法
标记-整理算法和标记-清除算法一样,标记--整理算法是把存活对象往内存一端移动,然后直接回收边界以外的内存。标记--整理算法提高了内存的利用率,并且它适合在收集对象存活时间较长的老年代。
(四)分代收集算法
分代收集是根据对象的存活时间把内存分为新生代和老年代,根据各个代对象的存活特点,每个代采用不同的垃圾回收算法。新生代采用复制算法、老年代采用标记--整理算法。
四、JVM内存模型
Java虚拟机中有三个主要的内存区域:方法区、Java栈和Java堆,它们分别用于存储不同类型的数据。
(一)方法区(Method Area):
用于存储类的结构信息,如类名、属性、方法、接口等。在JDK 8及之前的版本中,方法区是一个逻辑上独立于堆的内存区域,它通常也被称为永久代(PermGen)。而在JDK 8之后,方法区被移除,取而代之的是元空间
(二)Java栈(Java Stack):
每个线程都会有自己的Java栈,用于存储局部变量、操作数栈、方法出口等信息。Java栈是一个先进后出(FILO)的数据结构。当一个方法被调用时,就会在Java栈中创建一个新的栈帧;当方法执行结束时,对应的栈帧就会被弹出。
(三)Java堆(Java Heap):
用于存储对象实例和数组。所有线程共享一个Java堆,而且Java堆也是JVM管理的最大的一块内存区域。在Java程序运行过程中,所有通过new关键字创建的对象都会在Java堆中分配空间。
JVM结构模型图
五、JVM引用类型(重要)
Java中对象的引用分为四种级别,这四种级别由高到低依次为:强引用、软引用、弱引用、虚引用。
(一)强引用:
级别最高的引用,普通变量赋值即为强引用,如 A a = new A();如果一个对象被强引用、那么垃圾回收器绝对不会回收它;当内存空间不足,Java虚拟机宁可跑内存溢出异常,也绝对不会随意回收具有强引用的对象来解决内存不足的问题。
(二)软引用:
如果一个对象具有软引用,那么如果内存空间足够,垃圾回收器不会回收它,一旦内存空间不够了,垃圾回收器就会回收这些软引用对象的内存。软引用可用来实现内存敏感的高速缓存。
(三)弱引用:
弱引用的生命周期更短,弱引用的作用是使对象能够被垃圾回收器回收,即如果一个对象只被弱引用所引用,无论内存空间是否足够,这个对象在下一次垃圾回收时都会被回收。
弱引用可以解决内存泄露的问题。
缓存:如果我们需要为某些数据进行缓存而不想影响垃圾回收器对这些数据的回收,可以使用弱引用来实现缓存功能。
监视器:有时候我们需要对某些对象进行监视,但是又不想让监视器本身影响到这些对象的生命周期,可以使用弱引用来实现监视器功能。
(四)虚引用:
虚引用是最弱的一种引用类型,它的作用是在对象被垃圾回收器回收时收到一个系统通知或者后续处理操作。虚引用必须和引用队列联合使用。
总之,强引用垃圾回收器不会回收,软引用内存空间不足时会回收,弱引用不管内存空间是否足够,垃圾回收器都会回收它;虚引用是最弱一种引用,必须联合引用队列一起使用。
六、GC会回收JVM中哪些内存?
内存运行时JVM会有一个运行时数据区来管理内存。它包括5大部分:程序计数器、虚拟机栈、本地方法栈、方法区、堆。因为程序计数器、虚拟机栈、本地方法栈是线程私有的,随线程而生、随线程而死、生命周期比较短,因此不用考虑内存回收的问题。需要考虑内存回收的是GC主要进行回收的内存是JVM中的方法区和堆。
总结:GC 的目的在于实现无用对象内存自动释放,减少内存碎片、加快分配速度。