在堆里面存放着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. CocoStudio创建动画帧

    进入动画编辑器 选择“形体模式” 右键点击资源窗口的资源,可以进行删除,重命名的操作:  可以再资源窗口下方的预览窗口,查看选中的资源预览效果图: 右键点击“对象结构”,创建图层 选择“动画模式” 右 ...

  2. MVC、MVP、MVVM架构模式

    MVC模式 如何设计一个程序的结构,这是一门专门的学问,叫做"架构模式"(architectural pattern),属于编程的方法论. MVC模式就是架构模式的一种,不仅适用于 ...

  3. 浅析Web Services

    Web Services 可使您的应用程序成为 Web 应用程序. Web Services 通过 Web 进行发布.查找和使用. 1.什么是Web Services? Web Services 是应 ...

  4. CentOS 安装python3.5

    1.刚开始centos可能会缺少gcc等组件,先安装组件 yum groupinstall "Development Tools" 2.下载源码,解压后进入目录 #下载地址http ...

  5. win7下IIS的安装和配置图文教程

    1. 首先是安装IIS.打开控制面板,找到”程序与功能”,点进去 2. 点击左侧”打开或关闭Windows功能” 3. 找到”Internet 信息服务”,按照下图打勾即可 等待安装完成 4. 安装完 ...

  6. 获取崩溃时的调用栈和生成dump文件,然后自动重启

    首先要说明的是:  linux 下 比较方便可以得到 崩溃时的调用栈,win下 比较难办   1. linux 获取调用栈 代码奉上: #include <execinfo.h> //在头 ...

  7. mysql的my.ini配置文件

    第一步,我们找到mysql安装文件下面的my.ini文件,打开可以看到第一句: # MySQL Server Instance Configuration File Mysql服务实例配置文件 好,咱 ...

  8. 【Unity】关于U3d与bip骨骼适配

    http://blog.csdn.net/kiki110120/article/details/50371662 写在2015/12/21 1.Generic 在拿到美术FBX文件后,导入u3d,默认 ...

  9. 学习h5(开始)

    webstorm 下载地址:http://www.sdifenzhou.com/6176.html webstorm注册码: 43B4A73YYJ-eyJsaWNlbnNlSWQiOiI0M0I0QT ...

  10. 404. Sum of Left Leaves 左叶子之和

    [抄题]: Find the sum of all left leaves in a given binary tree. Example: 3 / \ 9 20 / \ 15 7 There are ...