Java的垃圾回收机制

来源:互联网 发布:共识网被关闭 知乎 编辑:程序博客网 时间:2024/06/05 09:32

Java的垃圾回收机制一直是Java最自以为豪的地方,经常会在面试被人问到,现在整理一下。

JAVA的垃圾回收机制,简单来说可以分为:垃圾、回收这几个大的部分来说。

关于垃圾的一些事

首先,根据题目,我们先思考一下:什么是垃圾?

         所谓垃圾是指:一个没有任何引用的对象。为什么这么说呢,仔细想想,如果一个对象没有任何引用,说明在程序中就无法在使用这个对象,那么这个对象存在就毫无意义。因此将他定义为垃圾。


其次:哪里的垃圾会被回收?

         我们知道。Java分为堆、栈、方法区等区域。在这些区域中哪写会使用垃圾回收机制呢? 注意看我们的上面对垃圾的定义: 一个没有任何引用的对象。其中对象两个字应该能让我们确定,堆是会使用垃圾回收的。至于栈,栈中的栈帧随着方法的进入和退出而有条不紊的执行着出栈和入栈的操作。每一个栈帧中分配多少内存基本上是在类结构确定下来时就已知的,所以他在内存中分配和回收都具有确定性。不会考虑回收的问题,至于程序计数器、本地方发栈等生命周期和线程相同的也不用考虑。至于方法区,会存在“废弃常量”和“废弃的类”的概念,会使用垃圾回收,因此。使用到垃圾回收的地方只有:堆和方法区。


然后:怎么判断一个对象是垃圾?

          1.引用计数法:给对象中添加一个引用计数器,每当一个地方引用它时,计数器就加1;当引用失效时,计数器就减一;当计数器为0时说明不能再被使用。但是这样做会存在一个相互循环引用的问题:见下面代码;

          2.实际上,Java采用的是一种叫做可达性分析的算法:一系列称为“GC Roots”的对象作为起始点从这些节点开始向下搜索,搜索走过的路径称之为“引用连”,当一个对象到达GC Roots没有任何引用链时,说明对象不可用。

       

最后:垃圾就一定会被回收吗?

         理论上来说,一个没有被任何东西引用的对象就应该被立即回收掉,释放出内存。但是,Java并不是直接这样做的,他提供了一次让垃圾表成有用的对象的机会。那就是finalize()函数。要想真正的回收一个对象,至少要执行两次标记的过程。 流程如下图片


当一个对象到引用链没有任何连接时,JVM会进行第一次标记;当对象重了finalize函数并且没有被JVM调用过的时候,会将此对象放入到一个F-Queue队列,稍后由JVM自动建一个低优先级的Finailzer线程去运行它。Ps:这里的运行只是指JVM会触发这个方法,但不承诺等他运行结束。为什么呢?想想看,如果该finalize方法出现死循环什么的,难道JVM要一直等他运行完?显然是不可能的。再触发执行finalize时,GC对对象进行二次标记,在这里,对象可以与引用连上的任意一个对象建立关连,就可以“复活”了。如果没有,将被彻底回收删除。

下面的代码也可以说明这一点:

package test;import java.util.concurrent.TimeUnit;public class test {public static test t = null;@Overrideprotected void finalize() throws Throwable {// TODO Auto-generated method stubsuper.finalize();System.out.println("程序执行到finalize,对象即将销毁。");t = this;System.out.println("此时t赋值为this");}public static void main(String[] args) throws InterruptedException {t = new test();t = null;System.gc();TimeUnit.MILLISECONDS.sleep(500);if(t != null){System.out.println("t 此时不为null.");}else{System.out.println("t 此时为 null");}//第二次处理t = null;System.gc();TimeUnit.MILLISECONDS.sleep(500);if(t != null){System.out.println("t 此时不为null.");}else{System.out.println("t 此时为 null");}}}

输出情况:

程序执行到finalize,对象即将销毁。此时t赋值为thist 此时不为null.t 此时为 null

此时,有人会奇怪,为什么finalize只会执行一次? 这是因为任何一个对象的finalize()都只会被系统自动调用一次,如果第二次,会判断他被JVM调用过,因此不会再执行finalize()函数。


   

0 0
原创粉丝点击