在第二章,我们主要讲解的是三个问题:
1.哪些内存需要被回收
2.什么时候回收
3.回收方式
1.如何判断对象已经死亡了
常见的实现方式有两种:
引用计数算法:
添加一个引用计数器,每当有人引用的时候,加一,没人引用的时候,减一.
从客观角度讲,这样的实现简单,但是有一个问题,就是不易于解决对象的相互引用的问题
简单来说,就是在两个对象A B中,分别都有着字段B A,两者相互引用,在两个对象都不可能被访问的时候,两个对象还互相引用对方,导致双方的引用计数都不为0,无法被回收
所以在虚拟机中,无法解决这个问题的情况下,没有虚拟机是通过引用计数算法来判断对象是否存活的
于是JVM采用的是可达性分析算法
通过了可达性分析来判断对象是否存活了.这个算法的思路是通过GC Roots的对象来作为起始点,进行向下的搜索,这个链式搜索的过程,称为引用链,
其回收的方式就是就是判断被回收的对象是否与GcRoots可达
那就不得不说说在Java中,可以作为GC Roots的对象
常见的有:
1.虚拟机栈中引用的对象
2,静态属性引用的对象
3.方法区常量引用的对象
4.Native方法引用的对象
2.那么,在一个对象可以回收的时候,其过程为
一个对象被判断为了不被引用的对象,并非非死不可,而是会进行两次判断,在进行相关回收
如果一个对象被判断为了不被引用,那么会被第一次的标记,并且筛选
筛选过程中判断是否需要执行finalize()方法,如果已经执行过了,或者没有覆盖finalize()方法,那么不会执行
如果确定有必要执行了,就会将这个对象放在一个叫 F-Queue队列中,并且交由一个低优先级的线程执行它
但这个队列可能会因为其中某个对象出现了bug,导致了死循环等问题而导致的无线等待
但在这个队列中利用好finalize()方法区拯救自己是逃脱的唯一机会了
如果在finalize()中重新将自己和其他类关联起来了,那么会移出回收队列
不然就直接回收了
需要注意的是,finalize()方法在执行过一次之后就不会在执行了,第二次直接回收了
这个方法并不适合现在的开发环境,与其使用finalize()方法,不如使用
try-finally()
3.关于方法区的回收
虽然说方法区是在虚拟机规范中可以不实现垃圾收集的,但是在实际虚拟机中,往往不实现垃圾收集会导致内存泄漏
永久代的垃圾收集分为两个部分,废弃常量的无用类
废旧常量很好理解,比如String,如果没有人使用这个String对象了,那么就会被内存回收
但是无用类比较难以判断
需要同时满足三个条件:
1.所有的实例都已经被回收,也就是Java堆中不存在任何的实例
2.加载类的ClassLoader都已经被回收
3.这个Class对象没有被引用,没有任何方法能通过反射获取到这个类的方法
如果满足了条件,虽然可以进行回收,但不一定回收
如果频繁的利用动态代理去生成类,那么久需要类卸载的功能,保证永久代不溢出