说到GC,相信大家都不陌生,对于Java开发的同学来说,之所以能毫无顾忌的申请新的空间,不用像c/c++那样malloc/new之后,还需要free/delete,就是靠它了。
在正式了解GC之前,我们先想一想,我们都想了解它什么?可能会比较好奇一下几个方面:
- GC回收都回收那些内存?
- GC什么时候会回收内存?判定可以回收的条件是怎样的?
- GC是怎样回收的?
带着以上几个问题,我们往下走:
一、回收哪些内存?
经过之前JVM内存结构的讲解,想必大家已经了解到了:jvm运行时数据区,大的来说分2部分——线程公有和线程私有。私有部分:程序计数器(PC)、jvm栈、本地方法栈,公有部分:方法区或称为perm区、java堆。私有部分是线程私有的,随线程生而生,灭而灭,所以这些地方的内存区域,会随着方法的结束或者线程的结束而被回收,不需要我们关心;但是公有的部分就不一样了,A线程结束了,很可能这个变量,B线程还在使用,这时候就不能回收,所以GC的回收内存区域就是java堆和方法区。
二、什么时候回收内存?
既然叫GC(Garbage Collection)——垃圾收集器,那就是说GC只是收集垃圾内存,何为垃圾内存?和平时生活一样,我们不会再使用的就是垃圾,就需要回收。所以GC回收也就是不会再被使用了的对象实例。
那么问题来了,如何能判定对象是否还使用了?
2.1引用计数算法
给对象添加一个引用计数器,每当有一个地方引用它时,计数器就加1;当引用失效的时候,计数器减1;任何时候计数器为0的对象就是不可能再被使用的对象。
优点:判定高效。
缺点:无法解决循环引用的问题,循环引用的对象的计数器都是1,所以永远无法被回收。
如下例:
public class ReferenceCountingGC {
public Object instance = null;
private static final int _1MB = 1024 * 1024;
private byte[] bigSize = new byte[2*_1MB];
public static void main(String[] args) {
//1.创建2个实例
ReferenceCountingGC objcA = new ReferenceCountingGC();
ReferenceCountingGC objcB = new ReferenceCountingGC();
//2.赋值
objcA.instance = objcB;
objcB.instance = objcA;
//3.对象引用置为空
objcA = null;
objcB = null;
}
}
以上过程的内存图如下:
上面步骤一和步骤二执行之后,对应的内存结构图,如下图一所示。其中对象A和对象B的引用值都是2。
步骤三:将A和B的引用都置为空,得到的结构图如下所示。
我们可以看到线程结束,但是对象A和对象B是不会被回收的。
2.2可达性分析算法
这个算法的基本思路就是通过一系列的称谓“GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所有走过的路径为引用链,当一个对象到GC Roots没有任何引用链项链时,则证明此对象时不可用的。 如下图:
可作为GC Roots的对象包括下面几种:
- 虚拟机栈(栈帧中的本地变量表)中引用的对象
- 方法区中类静态属性引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(即一般说的Native方法)引用的对象
从上图中可以看到,对于对象A和对象B来说,他们是属于不可达的对象,在GC的时候是会被回收的,而对于其他的对象来说,都还在被使用,所以不会被回收。
2.3 java中的引用类型
从JDK1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference),这四种引用的强度一次逐渐减弱
- 强引用就是指在程序代码之中普遍存在的,类似 “Object obj = new Object()” 这类的引用,只要强引用还存在,垃圾回收器永远不会回收掉被引用的对象。
- 软引用是用来描述一些还有用但并非需要的对象,对于软引用关联着的对象,在系统将要发生内存异常之前,将会把这些对象列进回收范围之中进行第二次回收,如果这次回收还没有足够的内存,才会抛出内存异常
- 弱引用也是用来描述非必需对象的,但是它的强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前,当垃圾收集器工作时,无论当前内存释放足够,都会回收掉只被弱引用关联的对象
- 虚引用也称为幽灵引用或者幻影引用,它是最弱的一种引用关系,一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例,对一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知
对于方法区的回收问题:
方法区的回收主要回收:废弃常量和无用的类。
无用的类的判定:
1.该类所有的实例都已经被回收;
2.加载该类的ClassLoader已经被回收;
3.改类对应的java.lang.Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。