Java GC

作者: bowen_wu | 来源:发表于2021-09-16 21:34 被阅读0次

    概述

    GC => 垃圾回收 = 回收可用空间 + 压缩内存

    内存管理

    1. 手动内存管理 => C | C++
    2. 自动内存管理 => 几乎所有的现代编程语言

    自动内存管理

    引用计数 Reference Counting

    内存被引用了一次,引用计数为1
    引用计数管理内存:清除引用计数为 0 的内存,之后被清除的内存引用的内存引用计数减 1
    引用计数的致命缺点:没有办法处理循环引用

    标记清除 Mark and Sweep

    从一个不是垃圾的根节点开始,能够访问到的所有的对象都不是垃圾,其余都是垃圾
    从一组预先定义好的根节点开始,把不是垃圾的都打上标记,之后把没有标记都清除掉
    标记清除算法 => Java | JavaScript | Python

    内存碎片化

    长期工作的内存会出现碎片化 => 总可用空间仍然足够,但是无法分配大对象

    引用的类型

    • 强引用 => Strong Reference => Developer 用到的都是强引用 => GC 的时候回收的都是强引用
    • 软引用 => Soft Reference => 内存不够时会回收它们 => SoftReference<Integer> soft = new SoftReference<>(1);
    • 弱引用 => Weak Reference => 一 GC 就回收它们 => WeakReference<Integer> weak = new WeakReference<>(1);
    • 影子引用 => Phantom Reference => 只能拿到影子,拿不到引用本身

    Java GC

    GC 的本质就是从 GC 开始沿着引用链去找到所有的可以达到的对象,这个过程叫可达性分析

    GC Roots

    • 活的线程 => java.lang.Thread
    • 类的静态成员
    • 线程方法栈所引用的对象
    • JNI 引用的对象 => JNI == Java Native Interface => Java 通过一些接口来调用 C 的代码,Java 的内存管理不管理这部分,所以将其引用的对象都算作 GC Root
    • 分代 GC 时其他代的对象

    GC 发生的什么

    每时每刻堆都会发生很多改变,可能有新的对象产生,有旧的对象消失,在此过程中可达性分析如何处理?

    1. STW => Stop The World => 把当前 JVM 中所有运行的线程都停下来,等待标记结束,在执行线程 => 一般以毫秒计
    2. 清除垃圾
    3. 压紧(compact)内存 | 拷贝(一边回收一边拷贝到另外一块内存,拷贝相比压紧更快一点,但是占用了两倍的空间)

    对象的分代假设

    研究表明:绝大多数对象的生命周期都很短

    内存分代 => HotSpot

    • 年轻代 => Young Generation
    • 老年代 => Old Generation | Tenured
    • 永久代 => Permanent Generation

    Young Generation

    • Eden => 伊甸园 => 可以分为多个 Thread Local Allocation Buffer => TLAB => 当 new 一个新的对象时会进入伊甸园,如果对象特别特别大,会直接进入老年代
    • Survivor (S0 | S1 | From & To) => Java 虚拟机并没有定义如何去实现 & 命名,这里都是 HotSpot 实现细节 => 当伊甸园满了之后执行一次年轻代 GC,将剩余对象存放到 Survivor 中,一共有两个 Survivor,两个 Survivor 一个是有对象的,一个是空的,当伊甸园满了的时候,进行 GC,将伊甸园和 Survivor 的剩余对象放置在另一个 Survivor 中,之前 Survivor 空了 => 超过 15 次 GC Cycles 的对象会被放置在 Tenured 中 => 有可能没有到 15 次的对象也被提升到了 Tenured,可能的原因是该对象过大,Survivor 中放不下 => 过早的提升对于程序的健康是一个损害,在 GC 的日志中可以查看到 => 解决方式:将 Young Generation 空间调大

    伊甸园和 Survivor 的大小是可配置的 => Search Key: jvm eden survivor ratio => -XX:SurvivorRatio
    以及超过多少次 GC Cycles 会被提升至 Tenured 中也是可配置的 => Search Key: jvm young promote tenured threshold => MaxTenuringThreshold=0

    Tenured

    • 老年代相对较大
    • 存储足够年老的对象
    • 发生 GC 的频率较低 => 清除垃圾 + 压紧内存

    永久代 & 元空间

    Java 8 之前:永久代

    • 是堆的一部分
    • 存储类数据、字符串常量
    • OOM => Out of Memory => Permgen Space => 产生原因大致两个 => 自定义 Classloader + 动态字节码生成

    Java 8+:元空间

    • 不是堆的一部分
    • 除非特别指定,否则没有上限
    • 设置 -XX:MaxMetaspaceSize 限制,此时可能出现 OOM:metaspace

    GC 种类

    1. Minor GC/Young GC
    • 总是发生在 Eden 满的时候
    • 会发生 STW
    1. Major GC vs Full GC
    • 这两个概念没有明确的定义
    • Major GC => 清除老年代
    • Full GC => 清除整个堆 => 发生条件:程序中有一个对象哪里都放不下 => 不应该发生,如果发生,表明程序处于不健康的状态 => 发生 Full GC 的过程要耗费 100ms 左右,取决于堆的大小,会引起程序严重性的下降

    GC 算法

    GC Algorithm

    JVM options

    -XX:+UseXXX
    
    • -X => 证明这个参数通用,不仅 HotSpot 可以使用,其他虚拟机也可以使用
    • -XX => HotSpot 专属
    • + => 开启
    • - => 关闭
    • UseXXX => 相关功能

    G1

    G1 => Garbage First => 优先收集垃圾 => 设计的主要目的:提供一个可以预测的停顿时间 => Example: 1s 内 GC 的时间不能超过 5ms

    知识点

    1. 在 Java 中所有的对象只能通过地址来访问,不算 Native + JNI
    2. 时间概念
    • SSD => 100微秒
    • HDD => 1 ~ 10 ms
    • HTTP => 100 - 500 ms => 大于 500 ms 人类就能感知到了
    • GC => < 100 ms
    1. 多线程加锁会导致性能下降
    2. Thread State TERMINATED 是需要 GC 的线程,其余的是活的


      Thread State

    相关文章

      网友评论

          本文标题:Java GC

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