JVM GC(java虚拟机垃圾回收)

时间:2022-12-29 09:46:50

前面介绍了java虚拟机的内存模型,现在了解一下虚拟机的垃圾回收机制。

要对垃圾进行回收,首先要注意三点:

1、哪些内存需要回收?

2、什么时候回收?

3、怎么回收

我们重点需要掌握的是第一点和第三点,下面讲一讲我了解到的垃圾回收

回答第一点的问题,jvm中哪些内存需要回收?其实JVM垃圾回收器会回收JVM的四大内存区域:虚拟机栈(100%回收),本地方法栈(100%回收),方法区(少量回收),堆(大量回收);前面三种不是垃圾回收其重点回收的区域,而堆内存里的对象往往多而生命期短,所以堆内存将是回收的重点区域。这就引出一点:如何确认堆内存的哪些对象需要被回收。答案是如果一个对象没有被引用,那么这个对象就可以被垃圾回收器回收。确定对象是否被引用有两种算法:

1、引用计数器法

所谓的引用计数器,就是每个对象增加一块额外的内存区域,用于存在当前对象被引用的次数,如果引用计数为0就可以回收,大于0就不能回收。

如A a = new A(); 此时对象的程序计数为1,A aa = a; 此时对象的程序计数为2, a = null, aa = null; 此时对象的程序计数为0,可以被回收。

这种方法的好处就是简单,但也存在一种缺陷:当两个对象相互引用的时候,导致程序计数总不为0。jdk2以前gc是使用这种方法,但它的缺陷导致之后用得很少。

2、根搜索算法

根搜索算法是从离散数学的图论引用出来的,程序把所有的引用关系看作是一张图,从一个节点GC root开始,寻找这个节点的引用节点,找到后在找到这些引用的节点的引用节点,直到将所有的引用节点找完后,剩余的节点就是没有被引用的节点,也就可以被回收。

这种算法我认为其中有两个重要的地方:

1)如何确定开始节点GC root

2)结点是怎样加到图上去的

目前java可以作为GC root对象的有:

a、虚拟机栈中的变量(本地变量表)

b、方法区中的静态变量

c、方法区中的常量

d、本地方法栈中的变量

jdk2及其以后gc使用根搜索算法。

既然知道了哪些对象可以回收,那么如何回收这些对象,gc回收主要有三种算法:

1、标记-清除法

2、复制法

3、标记-整理法

下面一次介绍这三种算法的思想:

1、标记-清除法

根搜索算法找到“存活”的对象并将其标记。标记完所有的对象后,垃圾回收器就清除所有未标记的对象。这种方法的优点是:当存活的对象较多时,回收的速度特别快;但其缺点也很明显:清除的内存会产生内存碎片。

2、复制法

根据根搜索算法找到“存活”的对象;将存活的对象复制到一块未占用的内存区域,并修改每个对象的引用地址。这种方法的优点是:当存活的对象较少时,回收的速度特别快;但其缺点也很明显:需要一块干净的内存来存放存活的对象,而且存活的对象较多时,回收的速度将会特别慢。

目前JVM堆内存中的新生代主要是采用这种算法,因为新生代中的对象往往生命期比较短,存活的对象比较少,而且堆内存的空间比较大。具体的过程要了解堆内存的内存结构和具体的复制步骤。

3、标记-整理法

为了弥补标记-清除法的缺点,标记-整理法避免了内存的碎片化。

标记-整理法的步骤和标记-清除法类似。先进行标记,然后清除为标记的对象,最后将所有存活的对象往内存左边进行移动并修改对象引用的地址。此方法是标记-清除法的一个改进,但移动对象和修改对象引用的地址也会带来必要的消耗。

目前JVM堆内存的老年代和方法区都是采用这种算法,因为堆内存老年代的对象往往比较少,而且回收的次数和内存大小也比较少;方法区也类似,因为方法区主要存放的是加载类的信息,回收会比较少。