• JVM工作原理--垃圾收集


       首先明确一点的是, JVM的垃圾回收针对的是JVM中运行时数据区中的方法区 和 堆区来讨论的,因为运行时数据区其他的 虚拟机栈,本地方法栈和程序计数器随着线程的创建和停止和创建的清除,不需要专门的机制来对其进行GC处理。

    下面以 WWH 思维视角来考虑堆区及方法区的垃圾收集。

    1 对象判活--收集什么

      垃圾收集在收集不再被使用到的对象前,首先需要找出这些对象,现有的两种判断对象是否需要被回收调的判活方式主要有两种:

    • 引用计数法
    • 可达性分析法

    引用计数法

      引用计数法的做法是: 在对象中添加一个引用计数器, 每当有一个地方引用到它时,计数器值就加1,当引用失效时,计数器值就减1;因此在任何时刻引用计数器的值为 0 的对象是不可能在被使用的。

    可达性分析法

           可达性分析法通过以一系列的称为"GC Roots" 的根对象作为起始节点,从这些节点开始根据引用向下搜索, 搜索的路径叫做 "引用链",如果某个对象到"GC Roots" 间没有任何引用链相连,就可认为该对象是不能被访问到的,从而将该对象判定为"垃圾"。

    可达性分析算法引用链关系如下图:

      966119497168d619e406ff7441815c5b.png

    常见的GC Roots对象有以下这些

    • 虚拟机栈中引用的对象,譬如各个线程被调用的方法栈中使用到的参数,局部变量,临时变量等
    • 方法区中类静态属性引用的对象,譬如 Java 类的引用类型静态变量
    • 方法区中常量引用的对象, 譬如字符串常量池中的引用
    • JAVA虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象 及系统类加载器等
    2 GC触发条件--什么时候收集

      垃圾的回收涉及对象的标记,复制 或者内存区域的整理 是一个耗时的过程,因此不到迫不得已,JVM不会对内存中的"垃圾"做回收,只有当遇到以下情况时,才会触发GC操作

    • Eden区和S1区已满,无法为新对象分配内存
    • 老代年空间不足
    • 方法区空间不足
    • System.gc()被显示调用(不一定触发GC)
    3 垃圾收集算法--怎样收集

      在判定出那些对象是可以回收的之后,需要采用一种方式来对这些对象进行清除以空出它们占用的内存空间;通常JVM中采用的是以下三类垃圾收集算法

    • 标记-清除算法
    • 标记-复制算法
    • 标记-整理算法

    在引出这些算法之前有必要先看看JVM的分代收集算法

    分代收集算法
      分代收集思想的由来是来源于大多数程序运行时的实际情况的经验总结,它建议在以下几个假设之上

    1. 绝大多数对象都是朝生熄灭的
    2. 熬过越多次垃圾收集的过程的对象就越难以消亡
    3. 跨代引用相对于同代引用只占极少数

    基于前两个假设,JVM将待回收的堆区分为两个部分: 新生代 和 老年代 。新生代用来存放那些"朝生夕灭"的对象,老年代用来存放那些"难以消亡"的对象。 基于第三个假设,在新生代中增加一个记忆器(Remember Set) 用来标识老年代中那些内存会存在跨代引用, 从而避免了老年代引用新生代的情况下在Minor GC时对老年代所有GC Root 做可达性分析, 可有效提高 Minor GC 的效率。

    标记-清除算法
        标记清除算法分两个阶段,第一个阶段将对象标记出来(可以标记存活对象,也可以标记待清理对象) 标记的过程就是是否属于"垃圾"的判定过程。第二阶段对垃圾做清理操作,如一阶段标记的是垃圾对象,则在此阶段将垃圾对象清除掉。

    标记-清除算法的优缺点非常明显

    • 优点: 清理方式简单
    • 缺点: ①效率不太稳定,清理效率跟待清理对象成反比 ② 清理后内存碎片化严重

    考虑到该算法在垃圾对象较多时效率变低的缺点,该算法并没有用在新生代的垃圾收集上;
    而是在注重降低延时的老年代收集 CMS 上应用。


    标记-复制算法
        标记-复制算法的第一阶段和清除法都是标记 , 不过在标记完对象之后,复制法通过将堆区(新生代)划分为不同部分的方式,仅复制存活对象到另一部分内存,然后原有区域的所有对象都做清空处理。

    注:标记-复制算法由于需要频繁的复制对象, 因此适合用在存活对象较少的新生代的Minor GC 中;
    由于新生代 Eden , S1, S2 区域划分比例是固定的,不可避免的会有大对象无法复制到S2的情况,因此需要在S2无法容纳的情况下申请老年代担保。


    标记-整理算法
       前面讲到标记-复制算法对于对象存活率较高的场合,复制清除的效率就会降低,因此老年代中采用的一种标记-整理算法。标记整理算法第一阶段的标记和前两种是一样的,在第二阶段它采用将存活对象移动到内存空间的一端,然后清理掉边界以外的内存。

    标记-整理算法前后对比:
      878717d4b27f4c42a307f565daea5ad7.png

    可以看到标记-清除 和 标记-整理 两种算法在做法上面一个最重要的却别就是是否在内存中移动了存活对象;移动对象带来的好处是空闲内存区域连续,可以在分配内存空间时放心分配大的内存区域; 移动带来的缺点当然是算法更复杂也更加耗时,因而在GC时会出现延时比较大的情况。 两种做法的优缺点决定了他们在应用上的差异:

    • 标记-清除法: 用在对GC时延较敏感的场合,收集器 CMS采用改算法实现
    • 标记-整理法: 用在对吞吐量要求较高的场合,收集器 Parallel Scavenge实现
  • 相关阅读:
    【JZOJ100047】基因变异【BFS】
    【JZOJ100047】基因变异【BFS】
    【JZOJ100046】收集卡片【模拟】
    【JZOJ100046】收集卡片【模拟】
    【NOIP2018】【洛谷P5022】旅行【基环树】
    【NOIP2018】【洛谷P5022】旅行【基环树】
    【POJ3417】Network【LCA】【差分】
    【POJ3417】Network【LCA】【差分】
    初学编程,选C还是选C++,你选对了吗
    TIOBE9月编程语言排行榜:时隔五年,为什么C语言依然排行第一
  • 原文地址:https://www.cnblogs.com/mingorun/p/13546553.html
Copyright © 2020-2023  润新知