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