java垃圾回收算法

在面试的时候,经常会被问Java的垃圾回收机制是怎样的?

首先我们要知道什么是垃圾?垃圾在我们的生活中是指对人类无用的东西。而在学习Java,它指的垃圾是不再存活的对象。那为什么要回收呢?

那什么是垃圾回收机制?垃圾回收机制是Java虚拟机提供的功能,用于在空闲时间以不定时的方式动态回收无任何引用的对象占据的内存空间。

常见的判断对象是否存活有两种方法:引用计数法和可达性分析。

引用计数法

为每一个创建的对象分配一个引用计数器,用来存储该对象被引用的个数。当该个数为零,意味着没有人再使用这个对象,可以认为“对象死亡”。

可达性分析

基本思路是把所有引用的对象想象成一棵树,从树的根结点 GC Roots 出发,持续遍历找出所有连接的树枝对象,这些对象则被称为“可达”对象,或称“存活”对象。其余的对象则被视为“死亡”的“不可达”对象,或称“垃圾”。

GC Roots 究竟指谁呢?

我们可以猜测,GC Roots 本身一定是可达的,这样从它们出发遍历到的对象才能保证一定可达。那么,Java 里有哪些对象是一定可达呢?主要有以下四种:
1、虚拟机栈(帧栈中的本地变量表)中引用的对象。
2、方法区中静态属性引用的对象。
3、方法区中常量引用的对象。
4、本地方法栈中JNI引用的对象。

有哪些方式来回收这些垃圾呢?

上面已经知道,所有GC Roots不可达的对象都称为垃圾,参考下图,黑色的表示垃圾,灰色表示存活对象,绿色表示空白空间。

标记-清理

第一步,所谓“标记”就是利用可达性遍历堆内存,把“存活”对象和“垃圾”对象进行标记。
第二步,既然“垃圾”已经标记好了,那我们再遍历一遍,把所有“垃圾”对象所占的空间直接清空即可。

标记-整理

既然上面的方法会产生内存碎片,在清理的时候,把所有存活对象扎堆到同一个地方,让它们待在一起,这样就没有内存碎片了。

这两种方案适合存活对象多,垃圾少的情况,它只需要清理掉少量的垃圾,然后挪动下存活对象就可以了。

复制

 这种方法比较粗暴,直接把堆内存分成两部分,一段时间内只允许在其中一块内存上进行分配。 

这种方案适合存活对象少,垃圾多的情况,这样在复制时就不需要复制多少对象过去,多数垃圾直接被清空处理。

Java 的分代回收机制

Java 的堆结构

由于Java堆唯一目的就是用来存放对象实例,因此其也是垃圾收集器管理的主要区域,故也称为称为 GC堆。
一块 Java 堆空间一般分成三部分,这三部分用来存储三类数据:

新生代

刚刚创建的对象。在代码运行时会持续不断地创造新的对象,这些新创建的对象会被统一放在一起。因为有很多局部变量等在新创建后很快会变成不可达的对象,快速死去,因此这块区域的特点是存活对象少,垃圾多。
新生代内存按照 8:1:1 的比例分为一个eden区和两个survivor(survivor0,survivor1)区,大部分对象在Eden区中生成。

老年代

存活了一段时间的对象。这些对象早早就被创建了,而且一直活了下来。我们把这些存活时间较长的对象放在一起,它们的特点是存活对象多,垃圾少。
老年代的内存也比新生代大很多(大概比例是1:2),当老年代满时会触发Major GC(Full GC),老年代对象存活时间比较长,因此FullGC发生的频率比较低。

永久代

永久存在的对象。比如一些静态文件、Java类、方法等。这些对象的特点是不需要垃圾回收,永远存活。

也就是说,常规的 Java 堆至少包括了 新生代 和 老年代 两块内存区域,而且这两块区域有很明显的特征:
新生代:存活对象少、垃圾多
老年代:存活对象多、垃圾少

新生代-复制回收机制

对于新生代区域,由于每次 GC 都会有大量新对象死去,只有少量存活。因此采用复制回收算法,GC 时把少量的存活对象复制过去即可。

老年代-标记整理回收机制

根据上面我们知道,老年代一般存放的是存活时间较久的对象,所以每一次 GC 时,存活对象比较较大,也就是说每次只有少部分对象被回收。
因此,根据不同回收机制的特点,这里选择存活对象多,垃圾少的标记整理回收机制,仅仅通过少量地移动对象就能清理垃圾,而且不存在内存碎片化。

由于对象进行了分代处理,因此垃圾回收区域、时间也不一样。垃圾回收有两种类型,Minor GC 和 Full GC。
• Minor GC:对新生代进行回收,不会影响到老年代。因为新生代的 Java 对象大多死亡频繁,所以 Minor GC 非常频繁,一般在这里使用速度快、效率高的算法,使垃圾回收能尽快完成。
• Full GC:也叫 Major GC,对整个堆进行回收,包括新生代和老年代。由于Full GC需要对整个堆进行回收,所以比Minor GC要慢,因此应该尽可能减少Full GC的次数,导致Full GC的原因包括:老年代被写满、永久代(Perm)被写满和System.gc()被显式调用等。

你可能感兴趣的:(Java,笔记,1024程序员节,java,jvm)