在堆里面存放着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. ad采样后幅度的衰减

    adc采集到的信号对低频有一定的衰减.因为要确定衰减的程度.通过da输出到示波器上观察. 数据如下: 输入 输出(enable) 输出(disable) 1v(20hz) 1v 0.88v 1v(10 ...

  2. git 一些用法

    创建远程分并跟踪: git remote add remote_branch_name git@github.com:test/test.git git fetch upstream 跟踪原始代码 删 ...

  3. 记一则css3计算

    .Head{ background-image: url("../../Img/PersonalCenter/banner.png"); background-repeat: no ...

  4. 10.Redis 性能测试

    转自:http://www.runoob.com/redis/redis-tutorial.html Redis 性能测试是通过同时执行多个命令实现的. 语法 redis 性能测试的基本命令如下: r ...

  5. c++Builder Delphi XML 解析例子

    XMLDocument; Xml.XMLIntf.hpp Xml.XMLDoc.hpp #include "Xml.Win.msxmldom.hpp" XMLDocument控件 ...

  6. 杀死 ps grep 出来的所有进程

    ps -ef |grep HouseList_Day |awk

  7. ssm框架整合之Spring4+SpringMVC+Mybaties3之配置文件如何配置及内容解释--可直接拷贝使用--不定时更改之2017/4/29

    经测试,需注意以下几点: 1,controller的自动扫描不能放在applicationContext.xml中,要放在spring-mvc.xml中.同样是<context:componen ...

  8. iOS 上的蓝牙框架 - Core Bluetooth for iOS

    原文: Core Bluetooth for iOS 6 Core Bluetooth 是在iOS5首次引入的,它允许iOS设备可以使用健康,运动,安全,自动化,娱乐,附近等外设数据.在iOS 6 中 ...

  9. MyBatis 体系结构

  10. MySQL 基础常用命令

    一.启动与关闭 1.1 Linux下启动mysql 的命令: a. rpm包安装:service mysqld start b. 源码包安装:/usr/local/mysql/bin/mysqld_s ...