自动内存管理与垃圾回收(一):如何判断对象已死

深入了解JVM,才能对Java了如指掌!

对象已死?

引用计数算法

思想:为每一个对象创建一个引用计数器,记录该对象被引用的次数,很显然当计数器值为零时就意味着这个对象是不可能再被使用的,即对象已死。

优点:原理简单,判定效率高
缺点:占用额外的内存空间,需要辅以一些其它的机制(如如何解决相互循环引用问题)

可达性分析

思想:通过一系列GC Root的根对象为起始结点集,从这些节点开始,根据引用关系向下搜索,搜索过程中走过的路径称为引用链,如果一个对象到GC Root没有任何引用链相连,则该对象不可能被再次使用。(Java使用该算法)

GC Root

固定可作为GC Root的对象:

  • 虚拟机栈、本地方法栈中引用的对象。
  • 方法区中类静态属性、常量引用的对象。
  • 所有被同步锁synchronized所持有的对象。
  • Java虚拟机内部的引用,如:系统类加载器、常驻的异常对象。
  • 反应Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。

除了这些可以固定作为GC Root的对象外,还可以有其它对象临时性的加入,如:只回收某个区域,但是会将跨代引用这个区域对象的对象加入到GC Root中。

finalize方法

判定一个对象死亡需要经过两次筛选:第一次筛选就是筛选出没有与GC Root相连接的引用链的对象;第二次筛选是判断该对象是否有必要执行finalize()方法。如果对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,则没必要执行了。

如果有必要执行,则会将该对象放到一个名为F-Queue的队列中,然后会由一条虚拟机自动创建的、低调度优先级的线程去执行队列中对象的finalize()方法(只是承诺去执行,但是却不一定会等待它运行结束,防止其执行时间过长导致内存回收子系统崩溃)。然后虚拟机会对这些对象再进行一次小规模的标记,如果对象在finalize()方法中成功拯救自己,即将自已与引用链上的对象建立起关联,则在这次标记中会将其移出即将回收的集合。

回收方法区

方法区主要回收两部分内容:废弃的常量和不再使用的类型。

回收废弃常量:例如字面量"java",如果当前系统中没有任何一个字符串对象引用常量池中的"java"常量,即没有字符串对象的值是"java",则当发生垃圾回收时,"java"常量就会被清理出常量池。常量池中的符号引用也是类似的逻辑。

回收不再使用的类型(类型卸载)的条件:

  1. 该类及其派生子类的所有实例都已经被回收了;
  2. 加载该类的类加载器已经被回收了;
  3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法通过反射访问该类。

使用的类型(类型卸载)的条件:

  1. 该类及其派生子类的所有实例都已经被回收了;
  2. 加载该类的类加载器已经被回收了;
  3. 该类对应的java.lang.Class对象没有在任何地方被引用,无法通过反射访问该类。

满足以上三个条件的无用类允许被回收,但是不一定会被回收(可以通过参数指定)。

おすすめ

転載: blog.csdn.net/zhang_qing_yun/article/details/120088825