在第二章,我们主要讲解的是三个问题:

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对象没有被引用,没有任何方法能通过反射获取到这个类的方法

如果满足了条件,虽然可以进行回收,但不一定回收

如果频繁的利用动态代理去生成类,那么久需要类卸载的功能,保证永久代不溢出

发表评论

邮箱地址不会被公开。 必填项已用*标注