zoukankan      html  css  js  c++  java
  • 垃圾回收与内存分配——总结篇

    垃圾回收与内存分配

    一些基础

    • 对象的四种引用类型

      • 强引用,内存不足时报错oom,但不会该类对象
      • 弱引用,当内存不足时才会回收
      • 软引用,不管内存是否充足,在gc都会回收
      • 虚引用,任何时候都可以被回收
    • 怎么判断对象是否仍在使用?

      • 引用计数法
        每个对象有一个引用计数属性,当被引用时计数+1,当引用释放时-1。两个对象互相循环使用时会导致对象无法回收
      • 可达性分析——Java默认
        若从GC Roots向下搜索时,走过的路径称为引用链。当对象没有任何引用链时代表已不可用。
    • 可作为GC Roots的对象有哪几类?

      • 虚拟机栈中引用的对象
      • 方法区类静态属性引用的对象
      • 方法区常量引用的对象
      • 本地方法中引用的对象
    • 方法区回收的必要条件

      主要回收两部分内容:废弃的常量和不再使用的类型。性价比很低

      • 该类所有实例都已被回收
      • 该类对应的类加载器已被回收(很难)
      • 无法通过反射访问该类(该类对应的java.lang,Class没有被引用)
    • 安全点和安全区域

      HotSpot没有为每一条指令生成OopMap,只在“特定位置”记录了这些信息(OopMap可理解成附加信息,对栈内的数据进行说明),这些位置称为安全点,线程在安全点可以被确定,从而确定GC Roots信息。

      • 安全点特定位置

        1. 循环的末尾
        2. 方法返回前
        3. 调用方法的Call之后
        4. 抛出异常的位置
      • 安全区域

      安全区域是指一段代码片中,引用关系不会发生变化,在这个区域任何地方 GC 都是安全的,安全区域可以看做是安全点的一个扩展

      • 线程中断的方式
      1. 主动式:不对线程操作,简单的设置一个标记位,线程不断主动轮询这个标志位状态,为真时,线程在最近的安全点挂起
      2. 被动式:系统直接把所有线程中断,如发现有的线程不在安全点时,就恢复执行到最近的安全点(不采用)
    • 并发情况下的可达性变动解决算法(G1和cms)

      • 增量空间算法——CMS
        当出现新的引用对象时,则记录下该对象,待扫描结束后再以此为根重新扫描一次
      • 原始快照算法——G1
        当出现删除引用时,将要删除的引用记录下来,待扫描结束后再将该对象为根重新扫描

    垃圾回收算法

    • 标记-清除算法
      标记需要回收的对象或不需要回收的对象,再统一清除。
      缺点:

      1. 效率不稳定,无论标记还是清除,效率都会随着对象增多而降低
      2. 内存空间碎片化
    • 标记-复制算法
      把内存空间分为两块,每次只使用其中一块,当该块内存不足后,把存活的对象复制到另一块,再把原空间一次性清除。
      优点:没有内存碎片。
      缺点:内存空间利用率低;当存活对象较多时,效率变低

    • 标记-整理算法
      标记完成后,将存活的对象向一端移动,清除边界以外的内容
      优点:内存规整,对象赋值/创建速度快
      缺点:标记、清理效率不高

    • 分代收集

      分代收集是将对象按照存活时间进行分代(新生代和老年代),根据不同区域的特点结合前三种算法进行收集

      • 新生代,每次垃圾收集都有大量对象死去,选用标记-复制算法

        新生代复制算法的改进:许多新生代的对象存活时间较短,不需要按照1:1的比例进行复制算法内存分配,可将内存分为较大的Eden区和两块较小的Survivor,回收时将Eden 和 Survivor 中还存活着的对象一次性地复制到另外一块 Survivor 空间上,最后清理掉 Eden 和刚才用过的 Survivor 空间

      • 老年代,对象存活率较高,没有其余区域可以进行分配担保,使用标记-清除、标记-整理算法

    垃圾回收器

    • 新生代

      • Serial
        串行,标记复制算法。客户端默认新生代收集器
      • ParNew
        并行,标记复制算法;只有ParNew和serial可以配合CMS使用
      • Parallel Scavenge
        并行,标记复制算法;吞吐量优先收集器,可控制吞吐量(用户代码运行时间/总时间)
        1. 最大垃圾收集停顿时间参数[绝对值]
        2. 垃圾回收时间占总时间比率参数[相对值]
        3. 自适应调节空间参数[布尔值],开启后可自动调节Eden、Survivor区域大小
    • 老年代

      • Serial Old
        标记整理算法,主要供客户端模式,若用于服务端:与Parallel Scavenge搭配使用;或者作为CMS发生失败后的与预案
      • Parallel Old
        标记整理算法,Parallel Scavenge的老年版
      • CMS
    • CMS

      响应时间优先,标记清除算法。多用于B/S架构的服务端上

      • 四个步骤:
        1. 初始标记:STW,只标记GC Roots直接关联到的对象
        2. 并发标记:与用户线程一起遍历整个对象图
        3. 重新标记:STW,修整并发标记期间,因用户线程导致引用变化的部分(增量更新算法实现)
        4. 并发清除:与用户线程并行,清除已死亡的对象
      • 三个缺点:
        1. 对处理器资源敏感,默认启动线程数(处理器核心数+3)/4
        2. 产生浮动垃圾。并发标记和清除阶段会与用户线程一起运行,所以需要预留一部分空间供用户线程使用,当空间不足时,会临时启用预案:使用serial old进行标记整理
        3. 标记清除算法导致内存碎片。大对象不够分配时,触发full gc耗时。可设置参数,多次清楚后,在下一次full gc前进行一次整理
    • G1

      面向整个堆空间的Region布局形式,每个Region都可以扮演Eden、Survivor或老年代空间。允许设定收集停顿时间

      • 四个步骤:
      1. 初始标记:在minor gc阶段只标记gc Roots引用的对象,所以不存在停顿
      2. 并发标记:与用户线程并发执行,标记完成后处理SATB(原始快照算法)记录在并发时有变动的对象
      3. 最终标记:STW(短暂),处理并发结束后仍遗留的少量SATB记录
      4. 筛选回收:STW,对各Region的回收价值与成本进行排序,根据期望停顿时间指定回收计划。将存活对象复制到空闲Region后,清除原Region

    常见问题

    • finalize() 方法什么时候被调用?它的目的是什么?
      对象被释放前被垃圾回收器调用,目的是释放该对象所持有的资源(对外内存、长连接等),且该方法只会被调用一次。(不建议使用)
    • 为什么要有不同的引用类型?
      由于gc回收时机不可控,且有时候需要适当的控制对象被回收的时机。比如新建 Person类,每次查询信息都需要重新构建实例,对象生命周期过短,引起巨大的消耗。听过软引用和HashMap的结合可以构建高速缓存,提升性能。
  • 相关阅读:
    [Swift]LeetCode910. 最小差值 II | Smallest Range II
    转 关于shell脚本中#!/bin/bash and #!/bin/ksh 的说明
    转 对象继承
    转 PHP编程过程中需要了解的this,self,parent的区别
    转: ORA-06508 could not find program unit being called: "DBSNMP.BSLN_INTERNAL
    Multitenant best Practice clone pdb seed and Clone a Pluggable Database – 12c Edition
    Plugging an Unplugged Pluggable Database issue 3
    日历 php
    Datapatch AND What to do if the status of a datapatch action was not SUCCESS due to finding non-ignorable errors
    oracle中的用户详解 【转】
  • 原文地址:https://www.cnblogs.com/CodeMLB/p/13264264.html
Copyright © 2011-2022 走看看