[深入理解JVM] 第二章 垃圾收集器与内存分配策略

ref: https://github.com/Snailclimb/JavaGuide/blob/master/docs/java/jvm/JVM垃圾回收.md

在这里插入图片描述

  • JVM内存分配及回收
    Java 堆是垃圾收集器管理的主要区域,因此也被称作GC 堆(Garbage Collected Heap).从垃圾回收的角度,由于现在收集器基本都采用分代垃圾收集算法,所以 Java 堆还可以细分为:新生代和老年代:再细致一点有:Eden 空间、From Survivor、To Survivor 空间等。进一步划分的目的是更好地回收内存,或者更快地分配内存。
    [外链图片转存失败(img-fj8SYWAw-1564190737239)(leanote://file/getImage?fileId=5d36d4d22657b84172000009)]

  • 判断对象已经死亡
    [外链图片转存失败(img-HDh3BFSL-1564190737242)(leanote://file/getImage?fileId=5d36d6272657b8417200000a)]

    1. 引用计数法

      • 给对象中添加一个引用计数器,每当有一个地方引用它,计数器就加 1;当引用失效,计数器就减 1;
      • 任何时候计数器为 0 的对象就是不可能再被使用的。
      • 这个方法实现简单,效率高,但是目前主流的虚拟机中并没有选择这个算法来管理内存,其最主要的原因是它很难解决对象之间相互循环引用的问题。
        [外链图片转存失败(img-AaxSvJe1-1564190737242)(leanote://file/getImage?fileId=5d36d8162657b8417200000b)]
    2. 可达性分析

      • 这个算法的基本思想就是通过一系列的称为 “GC Roots” 的对象作为起点,从这些节点开始向下搜索,节点所走过的路径称为引用链,当一个对象到 GC Roots 没有任何引用链相连的话,则证明此对象是不可用的。
      • 可以作为GC Root的对象:
        1. 虚拟机栈中引用的对象;
        2. 方法区中类静态属性引用的对象;
        3. 方法区总常量引用的对象;
        4. 本地方法栈中JNI(Native方法)引用的对象;
          [外链图片转存失败(img-e6MEH29L-1564190737243)(leanote://file/getImage?fileId=5d36d8922657b8417200000c)]
  • 引用:强引用、软引用、弱引用、虚引用

    1. 强引用
      • 以前我们使用的大部分引用实际上都是强引用,这是使用最普遍的引用。
      • 如果一个对象具有强引用,那就类似于必不可少的生活用品,垃圾回收器绝不会回收它。当内存空 间不足,Java 虚拟机宁愿抛出 OutOfMemoryError 错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
    2. 软引用
      • 如果一个对象只具有软引用,那就类似于可有可无的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。
      • 软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,JAVA 虚拟机就会把这个软引用加入到与之关联的引用队列中。
    3. 弱引用
      • 如果一个对象只具有弱引用,那就类似于可有可无的生活用品。
      • 弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它 所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程, 因此不一定会很快发现那些只具有弱引用的对象。
      • 弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java 虚拟机就会把这个弱引用加入到与之关联的引用队列中
    4. 虚引用
      • 虚引用"顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。
      • 虚引用主要用来跟踪对象被垃圾回收的活动。
      • 为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知;
    • 特别注意,在程序设计中一般很少使用弱引用与虚引用,使用软引用的情况较多,这是因为软引用可以加速 JVM 对垃圾内存的回收速度,可以维护系统的运行安全,防止内存溢出(OutOfMemory)等问题的产生。
  • 宣告一个对象死亡

    • 即使在可达性分析法中不可达的对象,也并非是“非死不可”的,这时候它们暂时处于“缓刑阶段”,要真正宣告一个对象死亡,至少要经历两次标记过程;可达性分析法中不可达的对象被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行 finalize 方法。当对象没有覆盖 finalize 方法,或 finalize 方法已经被虚拟机调用过时,虚拟机将这两种情况视为没有必要执行。
    • 被判定为需要执行的对象将会被放在一个队列中进行第二次标记,除非这个对象与引用链上的任何一个对象建立关联,否则就会被真的回收。
  • 回收方法区

    • 废弃常量
      假如在常量池中存在字符串 “abc”,如果当前没有任何 String 对象引用该字符串常量的话,就说明常量 “abc” 就是废弃常量,如果这时发生内存回收的话而且有必要的话,“abc” 就会被系统清理出常量池
    • 废弃的类
      • 类需要同时满足下面 3 个条件才能算是 “无用的类” :
        1. 该类所有的实例都已经被回收,也就是 Java 堆中不存在该类的任何实例。
        2. 加载该类的 ClassLoader 已经被回收。
        3. 该类对应的 java.lang.Class 对象没有在任何地方被引用,无法在任何地方通过反射访问该类的方法。
      • 虚拟机可以对满足上述 3 个条件的无用类进行回收,这里说的仅仅是“可以”,而并不是和对象一样不使用了就会必然被回收。
  • 垃圾收集算法

    1. 标记清除算法: 先标记再清除,会导致内存碎片化;
    2. 复制算法(Eden): 空出一片区域,用于在清楚后将剩余的内容整齐排列其中,从而避免碎片化;(Eden:Survivor:Survivor=8:1:1)
    3. 标记-整理算法(老年代):每回收一次都会排列整齐;
    4. 分代收集算法;
  • 垃圾收集器

    • Serial收集器: “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( “Stop The World” ),直到它收集结束
      [外链图片转存失败(img-xopDcHfk-1564190737244)(leanote://file/getImage?fileId=5d3ba3f12657b84172000010)]
    • ParNew 收集器:是 Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。
      [外链图片转存失败(img-KA4hXsFt-1564190737245)(leanote://file/getImage?fileId=5d3ba4192657b84172000011)]
    • Parallel Scavenge 收集器:Parallel Scavenge 收集器关注点是吞吐量(高效率的利用 CPU)。CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值。
      [外链图片转存失败(img-KkPgurE6-1564190737246)(leanote://file/getImage?fileId=5d3ba4772657b84172000012)]
    • Serial Old收集器:Serial 收集器的老年代版本,它同样是一个单线程收集器
    • Parallel Old收集器:Parallel Scavenge 收集器的老年代版本。使用多线程和“标记-整理”算法。
    • CMS收集器:CMS(Concurrent Mark Sweep)收集器是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。
      [外链图片转存失败(img-rr25sK1i-1564190737247)(leanote://file/getImage?fileId=5d3ba5082657b84172000013)]
  • 内存分配和回收策略

    • 总的来讲,就是往堆上分配内容;
    • 对象优先分配在Eden区,但空间不足时,发生一次Minor GC;
    • GC之后,可能存活的对象大于Survivior空间的大小,需要老年代进行分配担保,把Survivor无法容纳的对象直接进入老年代,但如果老年代的空间无法容纳这些内容,需要发起一次Full GC来腾出更多空间;
    • 大对象直接进入老年代(如长字符串和数组);
    • 长期存活的对象进入老年代(每经过一次minor GC年龄就+1),增长到一定程度后就进入老年代;
    • 动态对象年龄判定:Survivir空间中相同年龄所有对象大小的总和大于Survivor空间的一半,年龄大于或等于该年龄的对象进入老年代;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值