美文网首页
垃圾收集器与内存分配策略(2019-02-18)

垃圾收集器与内存分配策略(2019-02-18)

作者: Rondo9 | 来源:发表于2019-02-18 16:20 被阅读0次

        Java与C++之间有一堵墙由内存动态分配和垃圾收集技术所围成的"高墙", 墙外面的人想进去, 墙里面的人却想出来。

    一、 概述

        GC需要完成的3件事情:

            1. 哪些内存需要回收?

            2. 什么时候回收?

            3. 如何回收?

    二、 对象已死吗

        1. 引用计数算法

            无法处理不用的相互引用的对象组

        2. 可达性分析算法

            一个对象到GC Roots没有任何引用链时, 此对象不可用

            可作为GC Roots的对象:

                (1) 虚拟机栈中引用的对象

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

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

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

        3. 再谈引用

            4种引用强度:

                强引用: 程序代码中普遍存在的, 只要强引用还存在, 永远不会回收;

                软引用: 还有用但并非必须的对象, 在系统将要发生内存溢出异常前, 会把这些对象列进回收范围进行第二次回收, 如果这次回收还没有足够的内存才会抛出内存溢出异常;

                弱引用: 非必需的对象, 只能生存到下一次垃圾收集发生之前;

                虚引用: 在这个对象被收集器回收时收到一个系统通知

        4. 生存还是死亡

            宣告对象死亡, 至少要经历两次标记过程, 第一次筛选的条件是是否有必要执行finalize()方法, 如有必要, 这个对象将会放置到F-Queue队列中,  由Finalizer线程执行, 但不会保证等待它运行结束, 第二次筛选条件是重新与引用链上的任何一个对象建立关系, 则成功拯救, 否则将被回收, finalize()方法只会被调用一次

        5. 回收方法区

            永久代的垃圾收集主要回收两部分内容:

                废弃常量: 没有任何地方引用常量池中的此常量, 此常量就会被系统清理出常量池

                无用的类:

                    1. 该类所有的实例都已经被回收;

                    2. 加载该类的ClassLoader已经被回收;

                    3. 该类对应的java.lang.Class对象没有在任何地方被引用

                    满足上述3个条件仅仅说是"可以"进行回收, 通过-Xnoclassgc参数进行控制

            在频繁自定义ClassLoader的场景中都需要虚拟机具备类卸载的功能, 以保证永久代不会溢出。

    三、 垃圾收集算法

        1. 标记-清除算法

            算法分为"标记"和"清除"两个阶段: 首先标记出所有需要对手的对象, 在标记完成后统一回收所有被标记的对象。

            不足点:

                (1) 效率问题, 标记和清除两个过程的效率都不高

                (2) 空间问题, 标记清除后会产生大量不连续的内存碎片

        2. 复制算法

            将可用内存按容量划分为大小相等时两块, 每次只使用其中的一块, 当这一块的内存用完了, 就将还活着的对象复制到另外一块上面, 然后再把已使用过的内存空间一次清理掉。

            不足点:

                代价是将内存缩小为原来的一半

            Eden: Survivor1: Survivor2 = 8: 1: 1

            分配担保机制

        3. 标记-整理算法

            标记过程同上, 整理过程是让所有存活的对象都向一端移动, 然后直接清理掉端边界以外的内存。

        4. 分代收集算法

            新生代使用复制算法, 老年代使用标记-清除或标记-整理

    四、 HotSpot的算法实现

        1. 枚举根节点

            使用OopMap的数据结构在特定的位置记录引用的位置

        2. 安全点

            上述的特定位置指的就是安全点, 只有到达安全点才能GC暂停, 安全点的选定太少会让GC等待时间过长, 太多会频繁的暂停增大运行负荷, 所以安全点的选定以"是否具有让程序长时间执行的特征"为标准, 因为每条指令的执行时间都非常短暂, 只有在方法调用、 循环跳转、 异常跳转等指令序列复用的"长时间执行"的功能指令才会产生安全点。

            另一点需要考虑的是, 如何在GC发生时让所有线程都"跑"到最近的安全点上停顿下来, 有下面两种方案:

                1. 抢先式中断(基本已被pass);

                2. 主动式中断: 当GC需要中断线程时, 不直接对线程操作, 设置一个标志, 各个线程执行时在安全点处主动轮询标志, 为真就将自己中断挂起, 另外再加上创建对象需要分配内存的地方

        3. 安全区域

            安全点的不足点在于只能保证程序执行的时候, 如果程序没有分配CPU时间(Sleep或Blocked状态), 线程无法响应JVM的中断请求, 安全区是引用关系不会发生变化的一端代码片段, 安全区可以看做是被扩展的安全点, 当线程进入安全区时标识自己, 当线程要离开安全区时检查系统是否已经完成了根节点枚举, 如果完成, 继续执行, 否则等待知道收到信号。

    五、 垃圾收集器

        收集算法是内存回收的方法论, 垃圾收集器则是内存回收的具体实现。

    垃圾收集器

        1. Serial收集器

            单线程收集器, 采用复制算法, 它在进行工作时必须暂停其他所有的工作线程, 单CPU环境下有着最高的收集效率, 用于Client模式下的新生代。

        2. ParNew收集器

            Serial收集器的多线程版, Server模式下首选的新生代收集器, 能配合CMS收集器。

            -XX:ParallelGCThreads: 限制垃圾收集的线程数

        3. Parallel Scavenge收集器

            采用复制算法的多线程收集器, 目标是达到一个可控制的吞吐量(运行用户代码时间 / (运行用户代码时间 + 垃圾收集时间)), 提供两个参数用于精确控制吞吐量:

                -XX:MaxGCPauseMillis: 控制最大垃圾收集停顿时间, 越小GC发生越频繁;

                -XX:GCTimeRatio: 设置吞吐量大小, 范围(0, 100), 垃圾收集时间占总时间比率, 19为1 / (1 + 19)的垃圾回收时间比

            -XX:+UseAdaptiveSizePolicy: 自适应调节

        4. Serial Old收集器

            Serial收集器的老年代版本, 采用标记-整理算法

        5. Parallel Old收集器

            Parallel Scavenge收集器的老年代版本, 采用标记-整理算法

        6. CMS收集器

            以获取最短回收停顿时间为目标的收集器, 采用标记-清除算法, 过程分为4个步骤:

                1. 初始标记: "Stop The World", 仅仅标记GC Roots能直接关联到的对象, 速度很快;

                2. 并发标记: 进行GC Roots Tracing;

                3. 重新标记: "Stop The World", 修正并发标记时用户线程产生变动的那一部分的标记记录, 停顿时间比初始标记时间稍长, 远比并发标记时间短;

                4. 并发清除: 清除

            3个明显的缺点:

                1. 并发阶段时占用CPU, 数量为(CPU数量 + 3) / 4, 降低总吞吐量;

                2. 并发清除过程中用户线程会产生新的浮动垃圾, 无法做到老年代完全被填满时再收集, 需要预留一部分空间, JDK1.6中阈值为92%, 通过调节-XX:CMSInitiatingOccupancyFraction设置阈值;

                3. 基于标记清除算法, 会产生大量的空间碎片, -XX:useCMSCompactAtFullCollection开关参数, 顶不住时开启内存碎片的合并整理过程, -XX:CMSFullGCsBeforeCompaction, 调节实行多少次不压缩的GC后跟着压缩一次

        7. G1收集器

            

    相关文章

      网友评论

          本文标题:垃圾收集器与内存分配策略(2019-02-18)

          本文链接:https://www.haomeiwen.com/subject/mkvaeqtx.html