GC需要完成:

  • 哪些内存需要回收
  • 什么时候回收
  • 如何回收

如何确定对象不再使用

  • 引用计数算法
    给对象添加一个引用计数器,当有一个地方引用它时,计数器值进行加1操作;当引用失效时,计数器值进行减1操作;当计数器值为0,则说明对象不可能再被使用。但是它无法解决循环引用的问题。

    public class ReferenceCountingGC {
      
        public Object instance = null;
    
        public static void testGC(){
    
            ReferenceCountingGC objA = new ReferenceCountingGC ();
            ReferenceCountingGC objB = new ReferenceCountingGC ();
    
            // 对象之间相互循环引用,对象objA和objB之间的引用计数永远不可能为 0
            objB.instance = objA;
            objA.instance = objB;
    
            objA = null;
            objB = null;
    
            System.gc();
    }
    }

    上述代码最后面两句将objA和objB赋值为null,也就是说objA和objB指向的对象已经不可能再被访问,但是由于它们互相引用对方,导致它们的引用计数器都不为 0,那么垃圾收集器就永远不会回收它们。

  • 可达性分析算法
    通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的。如下图,对象Object5、Object6、Object7虽然互相有所关联,但是它们到GC Roots是不可达的,因此将它们判定为可回收的对象。

    在Java语言中,可作为GC Roots的对象包括:

    • 虚拟机栈(栈帧中的本地变量表)中引用的对象
    • 方法去中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中JNI(即一般说的Native方法)引用的对象。

关于引用

无论是通过引用计数算法还是可达性分析算法,判断对象是否存活都与“引用”有关。
宣告一个对象真正失效,至少要经历两次标记过程,如果对象在进行可达性分析后发现没有与 GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize() 方法,当对象没有覆盖finalize()方法,或者finalize()方法已经被虚拟机调用过,虚拟机将这两种情况都视为“没有必要执行”。
关于Java中的引用:

  • 强引用:程序代码中普遍存在,类似“Object obj = new Object()”这类的引用,只要强引用还存在,垃圾收集器永远不会回收掉被引用的对象。
  • 软引用:用来描述一些还有用但并非必需的对象。对于软引用关联的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围之中进行第二次回收。如果这次回收还没有足够的内存,才会抛出内存溢出异常。在JDK 1.2之后,提供了SoftReference类来实现软引用。
  • 弱引用:用来描述非必需对象,强度比软引用更弱一些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用挂链的对象。在JDK 1.2之后,提供了WeakReference类来实现弱引用。
  • 虚引用:也称为幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。在JDK 1.2之后,提供了PhantomReference类来实现虚引用。

永久代的垃圾收集主要回收两部分内容:废弃常量和无用的类。相较于废弃常量,判定一个类是否是“无用的类”的条件则相对苛刻很多,类需要同时满足下面三个条件:(不满足一定不回收,满足不一定回收,区别于对象无效就回收)
• 该类所有的实例都已经被回收,也就是Java堆中不存在该类的任何实例
• 加载该类的ClassLoader已经被回收
• 该类对应的java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法


垃圾收集算法

  • 标记-清除(Mark-Sweep)算法
    首先标记出所有需要回收的对象,在标记完成之后统一回收所有被标记的对象。标记过程就是使用引用计数法或可达性分析进行标记。
    不足:

    • 效率问题:标记和清除两个过程的效率都不高
    • 空间问题:标记清除之后会产生大量不连续的内存碎片,空间碎片太多导致以后在程序运行过程中需要分配较大对象时,无法找到足够的连续内存而不得不提前出发另一次垃圾收集动作。
  • 复制算法
    将可用内存按容量划分为大小相等的两块,每次只使用其中的一块。当这一块内存用完了,就将还存活着的对象复制到另外一块上面,然后再把已使用的内存空间一次清理掉。

  • 标记-整理算法
    标记过程仍然与“标记-清除”算法一样,但后续步骤不再直接对可回收对象进行清理,二十让所有存活的对象都向一端移动,然后直接清理掉端边界以外的内存。

  • 分代收集算法
    根据对象存活周期的不同将内存划分为几块,一般把Java堆分为新生代和老年代,根据各个年代的特点采用最适当的收集算法。在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那么就选用复制算法,而老年代中因为对象存活率高、没有额外空间对它进行分配担保,必须使用“标记-清理”或者“标记-整理”算法来进行回收。

《深入理解Java虚拟机》——垃圾收集器与内存分配策略的更多相关文章

  1. 深入理解java虚拟机----->垃圾收集器与内存分配策略(下)

    1.  前言 内存分配与回收策略 JVM堆的结构分析(新生代.老年代.永久代) 对象优先在Eden分配 大对象直接进入老年代 长期存活的对象将进入老年代 动态对象年龄判定 空间分配担保  2.  垃圾 ...

  2. 深入理解JAVA虚拟机 垃圾收集器和内存分配策略

    引用计数算法 很多教科书判断对象是否存活的算法是这样的:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1:当引用失效时,计数器值就减1:任何时刻计数器都为0的对象就是不可能再被使用的 ...

  3. [深入理解Java虚拟机]<垃圾收集器与内存分配策略>

    Overview 垃圾收集考虑三件事: 哪些内存需要回收? 什么时候回收? 如何回收? 重点考虑Java堆中动态分配和回收的内存. Is Object alive? 引用计数法 给对象添加一个引用计数 ...

  4. Java虚拟机垃圾收集器与内存分配策略

    Java虚拟机垃圾收集器与内存分配策略 概述 那些内存须要回收,什么时候回收.怎样回收是GC须要完毕的3件事情. 程序计数器.虚拟机栈与本地方法栈这三个区域都是线程私有的,内存的分配与回收都具有确定性 ...

  5. Java虚拟机 垃圾收集器与内存分配策略

    说起GC,我们要思考的主要有三件事 哪些内存需要回收 那些已经“死去”的对象,那么哪些对象“死”,哪些对象“活”呢,有个简单的办法 引用计数法,但是没法解决循环依赖问题 所以Java虚拟机采用的是可达 ...

  6. Java虚拟机--垃圾收集器和内存分配

    垃圾收集器和内存分配 程序计数器.虚拟机栈.本地方法栈这三个区域和线程的生命周期一致,所以方法结束或者线程结束时,内存自然就跟着回收了.Java堆和方法区,只有在程序处于运行期间才能知道会创建哪些对象 ...

  7. 深入理解JVM(三)垃圾收集器和内存分配策略

    3.1 关于垃圾收集和内存分配 垃圾收集和内存分配主要针对的区域是Java虚拟机中的堆和方法区: 3.2 如何判断对象是否“存活”(存活判定算法) 垃圾收集器在回收对象前判断其是否“存活”的两个算法: ...

  8. 深入理解JVM:垃圾收集器与内存分配策略

    堆里面存放着Java世界差点儿全部的对象实例,垃圾收集器在对堆进行回收前.第一件事情就是要确定这些对象之中哪些还存活,哪些已经死去.推断对象的生命周期是否结束有下面几种方法 引用计数法 详细操作是给对 ...

  9. 《深入理解Java虚拟机》(三)垃圾收集器与内存分配策略

    垃圾收集器与内存分配策略 详解 3.1 概述 本文参考的是周志明的 <深入理解Java虚拟机>第三章 ,为了整理思路,简单记录一下,方便后期查阅. 3.2 对象已死吗 在垃圾收集器进行回收 ...

  10. 《深入理解java虚拟机》第三章 垃圾收集器与内存分配策略

    第三章 垃圾收集器与内存分配策略 3.1 概述 哪些内存需要回收 何时回收 如何回收 程序计数器.虚拟机栈.本地方法栈3个区域随线程而生灭. java堆和方法区的内存需要回收.   3.2 对象已死吗 ...

随机推荐

  1. CSS中的定位与浮动

    CSS中的定位与浮动 本文主要讲述CSS中的三种定位样式static.relative和absolute的区别以及浮动元素的特征. 定位样式 CSS中定位样式position的取值有三个,默认值:st ...

  2. HDU 5912 Fraction(模拟——分子式化简求解)

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5912 Problem Description Mr. Frog recently studied h ...

  3. YourPHP笔记

    http://blog.sina.com.cn/s/blog_7c54793101016qq1.htm 基础认识: Ø  yourphp安装为子目录时不可以以"yourphp"为文 ...

  4. Mysql 范围查询优化

    Range查询:用单独的Index的一个或多个index值来检索表的子集行数据,当然包含多个index. 1:一个index (单一部分)的range access 方法:(eg : 指的这种key ...

  5. 如何更改MyEclipse中XML文件的字体?

    windows>Preferences>General>Appearance>Colors and Fonts>Basic>Text Font

  6. Python解析JSON详解

    JSON 函数 使用 JSON 函数需要导入 json 库:import json. 函数  描述 json.dumps  将 Python 对象编码成 JSON 字符串 json.loads  将已 ...

  7. Assembly oth

    body, table{font-family: 微软雅黑; font-size: 13.5pt} table{border-collapse: collapse; border: solid gra ...

  8. 让我们一起爱(装)上Homestead吧

    本文是Laravel实战:任务管理系统(一)的扩展阅读原文链接 先来点残酷现实:  真正用过homestead的,一般不会问homestead到底好在哪里 如果你还没有爱上homestead,只能说明 ...

  9. mysql中能够使用索引的典型场景

    mysql 演示数据库:http://downloads.mysql.com/docs/sakila-db.zip 匹配全值 explain select * from rental where re ...

  10. python_如何通过实例方法名字调用方法?

    案例: 某项目中,我们的代码使用的2个不同库中的图形类: Circle,Triangle 这两个类中都有一个获取面积的方法接口,但是接口的名字不一样 需求: 统一这些接口,不关心具体的接口,只要我调用 ...