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

    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收集器

        

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,332评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,508评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,812评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,607评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,728评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,919评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,071评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,802评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,256评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,576评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,712评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,389评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,032评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,798评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,026评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,473评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,606评论 2 350

推荐阅读更多精彩内容