在堆里面存放着Java几乎所有的对象实例,垃圾收集器要进行垃圾回收,要做的第一步便是找出那些对象是需要回收的。

怎么判断对象是否需要回收?

常用的方法有两种。

1、引用计数算法。为每一个对象添加一个引用计数器,每当有人持有对其的一个引用的时候,该计数器加1。这种算法(Reference Counting)实现简单,判断效率高,是一个很不错的算法,如Python语言、COM和Squirrel中都用它来管理内存。但是主流的Java虚拟机实现中并没有使用这个算法,主要原因是它很难解决对象之间循环引用的问题。比如在对象A中持有一个指向B的成员,而在对象B中又有一个指向A的成员,那么这两个对象将无法被回收,造成了内存泄露。

2、可达性分析算法。通过一系列的称为“GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain)。当一个对象从GC Roots不可达时,则证明这个对象时应该被回收的。这样,即使对象A和B互有引用,但却是GC Roots不可达的对象,它们已然会被垃圾回收器回收。

在Java中,可作为GC Roots对象包括下面几种:

1、虚拟机栈中引用的对象

2、方法区中类静态属性引用的对象

3、方法区中常量引用的对象

4、本地方法栈中JNI引用的对象

这两种算法判断都是通过引用贯穿其中。但是这种引用的定义过于狭隘。在JDK1.2之前,如果Reference类型的数据中存储的数值代表的是另一块内存的起始地址,则称这块内容代表着一个引用。但有时候,我们希望能有这样一种对象:当内存充足的时候能保留在内存中,当内存较为紧张的时候,能回收这些对象。JDK1.2之后,Java对引用的概念进行了扩充,分为4类:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)。

1、普通代码中默认的都是强引用,例如Object obj = new Object()。只要引用还在,永远不会回收。

2、软引用描述一些非必须的对象,只有当内存将要发生溢出异常之前才会将其回收。使用SoftReference类来实现。

3、弱引用也用来描述非必须的对象,但是只能生存岛下一次垃圾收集发生之前。使用WeakReference类来实现。

4、虚引用也成为幽灵引用,它的存在不会影响一个对象的生存时间,也无法通过虚引用获取一个对象实例。他的存在只是为了能在这个对象被收集器回收时能获得一个系统通知。使用PhantomReference类实现。

被回收的对象何时死亡?

通过引用计数或可达性分析确定一个对象可被回收直到该对象死亡要经过两次标记过程:

1、当第一次被标记为不可达时,它将会进行一次筛选,筛选条件是此对象时候有必要执行finalize()方法。访对象没有覆盖finalize()或已经被调用过,则虚拟机认为它“没必要执行”。如果这个对象被判定为有必要执行finalize()方法,则会将这个对象放置在F-Queue的队列之中,并在稍后一个由虚拟机自动建立、低优先级的Finalizer线程去执行它。这里的“执行”仅代表虚拟机会触发这个方法,并不一定会等待它运行结束。防止由于finalize()方法执行缓慢或者发生死循环等导致其它对象处于等待状态。finalize()方法是对象逃脱死亡厄运最后的机会,可以通过将this指针赋值给某个类变量或者对象的成员变量来拯救自己。

2、触发finalize()方法之后,稍后GC将对F-Queue中的对象进行第二次小规模标记。如果对象在finalize()中成功拯救了自己,则它将会被移除出“即将回收”的集合。否则之后它就真的被回收了。

  1. package org.bupt.xiaoye;
  2. /**
  3. * 此代码演示了两点:
  4. * 1.对象可以在被GC时自我拯救。
  5. * 2.这种自救的机会只有一次,因为一个对象的finalize()方法最多只会被系统自动调用一次
  6. */
  7. public class FinalizeEscapeGC {
  8. public static FinalizeEscapeGC SAVE_HOOK = null;
  9. public void isAlive() {
  10. System.out.println("yes, i am still alive :)");
  11. }
  12. @Override
  13. protected void finalize() throws Throwable {
  14. super.finalize();
  15. System.out.println("finalize mehtod executed!");
  16. FinalizeEscapeGC.SAVE_HOOK = this;
  17. }
  18. public static void main(String[] args) throws Throwable {
  19. SAVE_HOOK = new FinalizeEscapeGC();
  20. //对象第一次成功拯救自己
  21. SAVE_HOOK = null;
  22. System.gc();
  23. // 因为Finalizer方法优先级很低,暂停0.5秒,以等待它。防止finalize虽然被触发但是没有执行完成。注释掉sleep方法后,会出现finalize没有执行完而程序就退出的情况
  24. Thread.sleep(500);
  25. if (SAVE_HOOK != null) {
  26. SAVE_HOOK.isAlive();
  27. } else {
  28. System.out.println("no, i am dead :(");
  29. }
  30. // 下面这段代码与上面的完全相同,但是这次自救却失败了
  31. SAVE_HOOK = null;
  32. System.gc();
  33. // 因为Finalizer方法优先级很低,暂停0.5秒,以等待它
  34. Thread.sleep(500);
  35. if (SAVE_HOOK != null) {
  36. SAVE_HOOK.isAlive();
  37. } else {
  38. System.out.println("no, i am dead :(");
  39. }
  40. }
  41. }

finalize()方法并不建议使用,因为它运行代价高昂,不确定性太大,而且不能保证各个对象之间的调用顺序。

方法区的垃圾回收

Java虚拟机规范中并没有强制要求虚拟机必须实现方法区(HotSpot中的永久代)的垃圾回收,而且方法区中进行垃圾收集的“性价比”比较低:在堆中,尤其是新生代中,常规应用进行一次垃圾收集一般可以回收70%~95%的空间,而永久代的垃圾收集效率远低于此。

方法区中垃圾收集主要分为两部分:废弃常量和无用的类

废弃常量回收与Java堆中对象的收集非常类似。如果没有任何地方引用这些常量便会被系统清理。

无用的类的回收就比较复杂。只有该类满足下面三个条件才算是“无用的类”:

1、该类的所有实力都已经被回收

2、加载该类的ClassLoader已经被回收了

3、该类对应的java.lang.Class对象没有在被任何地方被引用,无法再任何地方通过反射访问该类的方法

当然满足了这3个条件,并不是一定会回收。HotSpot提供了-Xnoclassgc参数来控制。-Xnoclassgc 每次永久存储区满了后一般GC 算法在做扩展分配内存前都会触发一次FULL GC,除非设置了-Xnoclassgc.   在大量使用反射、动态代理、CGLib等ByteCode框架、动态生成JSP以及OSGi这类频繁自定义ClassLoader的场景都需要虚拟机具备类卸载功能。

深入理解java虚拟机(三)对象回收判断算法以及死亡过程的更多相关文章

  1. 深入理解Java虚拟机(三)——垃圾回收策略

    所谓垃圾收集器的作用就是回收内存空间中不需要了的内容,需要解决的问题是回收哪些数据,什么时候回收,怎么回收. Java虚拟机的内存分为五个部分:程序计数器.虚拟机栈.本地方法栈.堆和方法区. 其中程序 ...

  2. 深入理解Java虚拟机之垃圾回收篇

    垃圾回收简介 ​ Java 会对内存进行自动分配与回收管理,使上层业务更加安全,方便地使用内存实现程序逻辑.在不同的 JVM 实现及不同的回收机制中,堆内存的划分方式是不一样的. ​ 简要地介绍下垃圾 ...

  3. 深入理解java虚拟机【垃圾回收算法】

    Java虚拟机的内存区域中,程序计数器.虚拟机栈和本地方法栈三个区域是线程私有的,随线程生而生,随线程灭而灭:栈中的栈帧随着方法的进入和退出而进行入栈和出栈操作,每个栈帧中分配多少内存基本上是在类结构 ...

  4. 深入理解java虚拟机(三)-----类加载机制

    类加载机制jvm把描述类的数据从class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被jvm直接使用的java类型.在java中,类型的加载.连接和初始化都是在程序运行期间完成的 ...

  5. 深入理解java虚拟机(二)-----垃圾回收

    做一个java程序员很是幸福,不用管不用的对象如何被回收,但是我认为了解一下也不是坏事. 一.如何判断对象已经死亡? 在进行垃圾回收之前,第一件事肯定是判断对象是否已经死亡.1.引用计数算法给对象添加 ...

  6. 深入理解java虚拟机---3垃圾回收机制GC

    本文来源于翁舒航的博客,点击即可跳转原文观看!!!(被转载或者拷贝走的内容可能缺失图片.视频等原文的内容) 若网站将链接屏蔽,可直接拷贝原文链接到地址栏跳转观看,原文链接:https://www.cn ...

  7. 【深入理解Java虚拟机】垃圾回收

    引用计数算法 给对象加一个计数器,引用一次+1,引用时效就-1,当计数器=0时对象就不能再被使用: 实现简单,判定效率高:Java虚拟接没有使用,主要原因是很难解决对象之间循环引用问题: GC算法: ...

  8. 深入理解JAVA虚拟机(内存模型+GC算法+JVM调优)

    目录 1.Java虚拟机内存模型 1.1 程序计数器 1.2 Java虚拟机栈 局部变量 1.3 本地方法栈 1.4 Java堆 1.5 方法区(永久区.元空间) 附图 2.JVM内存分配参数 2.1 ...

  9. 深入理解java虚拟机(四)垃圾收集算法及HotSpot实现

    垃圾收集算法 一般来说,垃圾收集算法分为四类: 标记-清除算法 最基础的算法便是标记-清除算法(Mark-Sweep).算法分为“标记”和“清除”两个阶段:首先标记处需要收集的对象,在标记完成之后,再 ...

随机推荐

  1. python开发_python中的module

    在python中,我们可以把一些功能模块化,就有一点类似于java中,把一些功能相关或者相同的代码放到一起,这样我们需要用的时候,就可以直接调用了 这样做的好处: 1,只要写好了一个功能模块,就可以在 ...

  2. Windbg基本命令应用总结

    .cordll -ve -u -l //reload core dlls ------加载下载系统文件符号的URL---------- .sympath SRV*C:\Symbols*http://m ...

  3. C++Primer笔记-----day06

    ================================================================day06=============================== ...

  4. UGUI BUG

    UNITY UGUI问题:父类使用 GroupLayout,子类使用contentsize filter时,会出现运行时布局重叠,但隐藏后再显示就会好了.

  5. No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer解决方法

    org.codehaus.jackson.map.JsonMappingException: No serializer found for class org.hibernate.proxy.poj ...

  6. Bresenham画线算法

    [Bresenham画线算法] Bresenham是一种光栅化算法.不仅可以用于画线,也可以用用画圆及其它曲线. 通过lower与upper的差,可以知道哪一个点更接近线段: 参考:<计算机图形 ...

  7. Python_12-线程编程

    1.1    Python中的线程使用1.1.1 函数式1.2    创建threading.Thread的子类来包装一个线程对象1.2.1 threading.Thread类的使用1.3    线程 ...

  8. C++——堆、栈、静态存储区

      栈 堆 静态存储区 生命周期 函数结束即释放 new,malloc开辟,delete,free释放 释放前,一直存在 最长,程序退出才释放   程序.局部变量 new,malloc申请的空间,用于 ...

  9. NSArray & NSDictionary

    一.NSArray 1.1 简单创建方法由难到简 NSArray *arr = [[NSArray alloc] init]; NSArray *arr = [NSArray arrayWithObj ...

  10. 关于iOS URL缓存机制原理解析

    关于URL缓存机制中   利用request对象判断是否缓存   其实request是否相等的判断依据是URLString是否相等