JVM第二话 -- JVM内存模型以及垃圾回收

1.JVM内存模型

1.1 Java对象布局

在这里插入图片描述

对齐填充:为了保证对象的大小为8字节的整数倍,自动对齐,避免多次加载

1.2 Heap堆内存分布

  • 首先先使用jdk/bin/jvisualvm.exe的工具,也可cmd窗口直接输jvisualvm。打开以后在工具-插件安装Visual GC,监控一下当前已启动的java服务
    在这里插入图片描述

从图中可以分为Metaspace元数据(非堆)、Old区老年代、Eden(Survivor0、Survivor1)区新生代也简称Young区

1.2.1 Old区老年代

  • 每经过一次GC回收,对象年龄+1,大于15次没有被回收掉的则加入老年代中
  • 对象内存大小大于新生代,则直接加入到老年代中

1.2.2 Young区新生代

因为新生代绝大多数对象生命周期比较短,经过回收会导致Young区空间不连续,造成空间碎片的问题。
在这里插入图片描述
当给需要多个内存格的对象进行分配时无法分配,则会造成GC回收导致和CPU抢时间片。

于是将Young区在分成Eden区和Survivor区

  • Eden区只存放新生的对象
  • 只要经过回收的就放入S区,Eden区就能够相对连续了,但是再次回收后的S区也会出现空间碎片问题,还是会导致对象放不下
  • 于是区分了S0(Survivor From)、S1(Survivor To),每次回收都转移到下一区域,永远保证S0或者S1有一个为空,由此解决碎片问题
  • Eden区和S0、S1的比例是8:1:1
  • 万一新生成的对象比较大,S0、S1内存不够了,需要向老年区借用内存,这称为担保机制

在这里插入图片描述
始终会保证S0或者S1有一个为空

2.JVM 垃圾回收

2.1 Young GC

包含了Eden、S区,最小的GC有称为Minor GC

2.2 Old GC

Major GC,通常会伴随着Minor GC,相当于Full GC

2.3 Full GC

Young GC+ Old GC (+MateSpace GC)

会导致 stop the work , 要尽可能减少Full GC的频率
允许一定范围的Young GC

2.4 模拟Heap区 OOM

  • 首先准备代码
List<User> str = new ArrayList<>();

@GetMapping("/while")
public void while1() {
    while (true){
        str.add(new User());
    }
}
  • 设置JVM堆内存大小
-Xms40M -Xmx40M
  • 堆内存分布和日志截图
    在这里插入图片描述
    上图垃圾回收过程:
  • 新生成一个对象进入Eden区,发现Eden区内存不足,则触发Young GC
  • Young GC首先对S1进行回收,未被回收的分代年龄+1,转移到S0或者Old区(满足分代年龄的)
  • 然后再对Eden区进行回收,未被回收的转到S0,从而给新对象预留空间
  • 经过回收后,Eden区或者S0或者Old区空间不够均会导致OOM

2.5 模拟Metaspace区 OOM

Java8使用Metaspace来替代永久代。Metaspace是方法区在Hotspot中的实现,并不在虚拟机内存中而是直接使用本地内存。

在Java8中,Class、Metadata被存储在Metaspace元数据中,永久代(Java8后被Metaspace取代)存放了一下信息:
1.虚拟机加载的类信息
2.常量池
3.静态变量
4.即时编译后的代码

如要模拟Metaspace空间溢出,可以不断的生成代理类往元空间加,那么久会触发Metaspace OOM

  • java 代码
public class MetaspaceTest {

    static class User {
        String name;
    }

    public static void main(String[] args) {
        int i = 0;
        try {
            while (true) {
                i++;
                Enhancer enhancer = new Enhancer();
                enhancer.setSuperclass(User.class);
                enhancer.setUseCache(false);
                enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> methodProxy.invokeSuper(o, args));
                enhancer.create();
            }
        } catch (Throwable e) {
            System.out.println("i=" + i);
            e.printStackTrace();
        }
    }
}
  • jvisualvm工具监控和日志
    在这里插入图片描述
    至于Metaspace为什么没有填满,因为在Java8中Metaspace并不在虚拟机内存中而是直接使用本地内存

3.JVM垃圾回收器

3.1 怎么确定是垃圾对象

  1. 引用计数,可能会存在循环引用问题而导致两个对象都不能被回收
  2. 可达性分析,确定一个GC Root,由它出发,检查某个对象是否可达,不可达的则为垃圾对象

3.2 GC Root

  • 虚拟机栈中的局部变量表( 栈->来源于方法区正在执行的变量)
  • 方法区的常量或者static变量,本地方法栈中的变量
  • 类加载器
  • Thread

3.3 回收算法

1.标记清除算法(存活对象、标记对象、未被使用空间)

  • 扫描过程中确定为垃圾对象,则打上标记,下次扫描才被回收
  • 标记和清除都扫描整个堆内存空间,比较耗时,清除后导致空间碎片不连续

2.标记复制算法

  • 划分出一块区域,在标记后的同时将存活的对象复制到新的区域中,然后直接清空整个标记区域,等同于s0/1
  • 虽说达到空间连续了,但也比较浪费空间

2.标记整理算法

  • 在标记后进行清除的过程中移动存活的对象

3.4 垃圾收集器

对上述算法的应用,将不同的回收算法适用于不同的代
新生代:标记复制算法,老年代:标记清除、整理算法

3.4.1 Serial GC

-XX:+UseSerialGC

  • 单线程垃圾收集器,比较适用单核CPU,内存空间比较少
  • 适用新老年代
  • 缺点:会暂停业务代码线程,因为堆中的内存地址会变动,不能让线程使用到已经不存在的对象

3.4.2 Parallerl GC

-XX:+UseParallerlGC

  • 多线程,但会使应用程序暂停,适用新老年代

3.4.3 Concurrent Mark Sweep(CMS)

-XX:+UseConcMarkSweepGC

  • 使用的是标记清除算法,存在空间碎片问题,只适用于老年代
  • 应发类的垃圾收集器,用户线程和垃圾回收线程同时进行,低停顿时间

3.4.4 G1 Garbage Collector

-XX:+UseG1GC

  • 尽可能满足用户设置的停顿时间目标,很好的解决了空间碎片问题,吞吐量高
  • 用的标记整理算法,适用新老年代
  • 在CMS回收器的基础上再加了一个最终标记,然后再跟据回收时间随机回收

如果倾向于G1,尽量满足以下几点

  • 堆内存使用率超过了50%
  • 竞争比较高
  • 希望停顿时间低的

3.4.5 Z GC

-XX:+UseZGC

  • 非常小的停顿时间,但是会牺牲一些吞吐量

4. JVM调优纬度

基于业务来确定使用最合理的垃圾回收期,主要有以下几个纬度

  • 串行:只有一个垃圾收集器,Serial
  • 并行:多个垃圾收集器,业务代码线程会停顿 Parallerl GC,比较关注吞吐量
  • 并发:多个垃圾收集器,业务代码共同运行,CMS、G1、ZGC,比较关注停顿时间

4.1 停顿时间

服务接口的响应时间,垃圾收集器进行垃圾回收和Client执行响应的时间

4.2 吞吐量

时间段内完成的请求数或者更新数,运行用户代码时间 /运行用户代码和垃圾收集总时间

5.垃圾回收器搭配图

在这里插入图片描述
当你指定了某个区使用某个垃圾收集器时,其他区域会采用配套的垃圾收集器

以上就是本章的全部内容了。

上一篇:JVM第一话 – JVM入门详解以及运行时数据区分析
下一篇:JVM第三话 – JVM性能调优实战和高频面试题记录

书山有路勤为径,学海无涯苦作舟

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值