JVM_6 之 GC基础_1

这是我参与8月更文挑战的第2天,活动详情查看:8月更文挑战

GC

引子(掌握技能)

熟悉GC常用算法,熟悉常见垃圾回收器,具有实际JVM调优实战经验

GC的基础知识

什么是垃圾

没有任何引用指向的一个对象或者多个对象(循环引用)

垃圾回收(java vs c++)

  1. java
  • GC 处理垃圾
  • 开发效率高,执行效率低
  1. C++
  • 手工处理垃圾
  • 开发效率低,执行效率高

自动内存回收,编程上简单,系统不容易出错,手动释放内存,容易出两种类型的问题:

  1. 忘记回收(内存泄漏)
  2. 多次回收(非法访问)

如何定位垃圾

  1. 引用计数(ReferenceCount)

不能解决循环引用问题
2. 根可达算法(RootSearching)

1628748804.jpg

常见的垃圾回收算法

  1. 标记清除(mark sweep)
  • 位置不连续、容易产生碎片、效率偏低
  • 两遍扫描:第一次扫描标记不可回收的对象,第二遍扫描清除可回收对象
  • 【存活对象比较多的时候效率高】
  1. 拷贝算法 (copying)
  • 没有碎片,浪费空间,移动复制对象,需要调整对象的引用
  • 【适用于存活对象比较少的情况】
  • 只扫描一次,效率提高,适合Eden区

1628749967.jpg

1628749885.jpg

  1. 标记压缩(mark compact)
  • 没有碎片,不会产生内存减半,方便对象分配
  • 两遍扫描,需要移动对象,指针需要调整,效率偏低

1628750028.jpg

JVM内存分代模型(用于分代垃圾回收算法)

1628751061.jpg

一个对象从出生到消亡

1628751713.jpg

GC 的概念

1628751847.jpg

  1. 部分垃圾回收器使用的模型

    除Epsilon ZGC Shenandoah之外的GC都是使用逻辑分代模型

    G1是逻辑分代,物理不分代

    除此之外不仅逻辑分代,而且物理分代

  2. 新生代 + 老年代 + 永久代(1.7)Perm Generation/ 元数据区(1.8) Metaspace

    1. 永久代 元数据 – Class
    2. 永久代必须指定大小限制 ,元数据可以设置,也可以不设置,无上限(受限于物理内存)
    3. 字符串常量 1.7 – 永久代,1.8 – 堆
    4. MethodArea逻辑概念 – 永久代、元数据
  3. 新生代 = Eden + 2个suvivor区

    1. YGC回收之后,大多数的对象会被回收,活着的进入s0
    2. 再次YGC,活着的对象eden + s0 -> s1
    3. 再次YGC,eden + s1 -> s0
    4. 年龄足够 -> 老年代 (15 CMS 6)
    5. s区装不下 -> 老年代
  4. 老年代

    1. 顽固分子
    2. 老年代满了FGC Full GC
  5. GC Tuning (Generation) 调优

    1. 尽量减少FGC
    2. MinorGC = YGC
    3. MajorGC = FGC
  6. 对象分配过程图

  7. 动态年龄:(不重要)
    www.jianshu.com/p/989d3b06a…

  8. 分配担保:(不重要)
    YGC期间 survivor区空间不够了 空间担保直接进入老年代
    参考:cloud.tencent.com/developer/a…

对象分配

  • 栈上分配

    • 线程私有小对象
    • 无逃逸
    • 支持标量替换
    • 无需调整
  • 线程本地分配TLAB(Thread Local Allocation Buffer)

    • 占用eden,默认1%(每个线程在eden区私有的一块地方,创建线程的时候就被分配)
    • 多线程的时候不用竞争eden就可以申请空间,提高效率
    • 小对象
    • 无需调整
  • 老年代

    • 大对象
  • eden

    • 新生的对象

1628754310.jpg

模拟实例代码

package com.mashibing.jvm.c5_gc;

//-XX:-DoEscapeAnalysis -XX:-EliminateAllocations -XX:-UseTLAB -Xlog:c5_gc*
// 逃逸分析 标量替换 线程专有对象分配

public class TestTLAB {
    class User {
        int id;
        String name;

        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }
    }

    void alloc(int i) {
        //该对象没有其它地方引用,不会产生逃逸
        new User(i, "name " + i);
    }

    public static void main(String[] args) {
        TestTLAB t = new TestTLAB();
        long start = System.currentTimeMillis();
        for(int i=0; i<1000_0000; i++) t.alloc(i);
        long end = System.currentTimeMillis();
        System.out.println(end - start);
    }
}
复制代码

对象何时进入老年代

1628753975.jpg

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享