JVM学习笔记(6)运行时数据区之堆

一.堆核心概述

在这里插入图片描述
在这里插入图片描述

如图所示,开启了两个进程
在这里插入图片描述
代码一样,在JDK自带的jvisualvm.exe工具中可以查看到两个进程分别拥有一个堆空间,
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

内存细分

目前堆空间主要是由新生代和老年代组成的,元空间相当于编外人员, 元空间的具体实现是由方法区来实现的.

在这里插入图片描述

可使用此jvm参数打印GC参数 -XX:+printGCDetails
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述


二.设置堆的大小与OOM

堆空间大小设置

在这里插入图片描述

开发中建议将默认堆大小和最大堆大小设置成一样的, 比如手动设置 -Xms600m -Xmx600m
原因是: 默认值如果设置小了.后面堆回去申请内存,直到到达最大值.因为系统的GC其实也挺消耗资源的,没必要再让堆的扩容和回收来浪费系统资源,主要是减轻系统运行的资源压力.
在这里插入图片描述

在这里插入图片描述

通过在jvm参数中添加-XX:+printGCDetails 打印堆内存信息可查看和cmd命令行查看是一样的
在这里插入图片描述
在这里插入图片描述

OutOfMemorry(堆空间溢出)举例

在这里插入图片描述

public class OOMTest {

    public static void main(String[] args) throws InterruptedException {
        ArrayList<Picture> list = new ArrayList<Picture>();

        while (true) {
            Thread.sleep(20);
            list.add(new Picture(new Random().nextInt(1024 * 1024)));
        }

    }
}


class Picture {
    private byte[] picels;

    public Picture(int length) {
        this.picels = new byte[length];
    }
}

一刻不停的在造对象
可以在java VisualVM(这个软件在jdk的bin目录中)中查当前java进程的内存占用情况
在这里插入图片描述
在这里插入图片描述


三.年轻代与老年代

对象刚刚创建的时候是在伊甸园区的,当进行GC的时候,如果发现对象还存活的话,就会将对象放到幸存者区(from或者to区<也叫1区或者0区,因为from或者to是不断交换的>),经过一定的时间之后,如果对象还是存活,就会放入到老年区.

在这里插入图片描述

在这里插入图片描述

如果同时使用了-XX:SurvicirRatio和-Xmn两个参数,其中以-Xmn为准,因为已经显式指明了大小,一般不会直接使用这个参数设置大小
在这里插入图片描述

设置比例已经生效,需要显式加参数
在这里插入图片描述
在这里插入图片描述

通过jinfo -flag NewRatio 30512 查看指定的进程的老年代和新生代比例
在这里插入图片描述

在这里插入图片描述


四.图解对象分配过程

概述:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结:

在这里插入图片描述

特殊情况

在这里插入图片描述

在这里插入图片描述

常用调优工具

在这里插入图片描述

JProfiler v11.1.5 64位 免费特别版(附注册码+安装教程) : https://www.jb51.net/softs/608640.html#downintro2
https://blog.csdn.net/qq_43012792/article/details/107437471


五.Minor GC,Major GC , Full GC

在这里插入图片描述

最简单的分代式GC策略的触发条件

年轻代GC(Minor GC) 触发机制

在这里插入图片描述
在这里插入图片描述

老年代GC(Major GC / Full GC) 触发机制

在这里插入图片描述

Full GC 触发机制(简单描述)

在这里插入图片描述

示例:

JVM参数: -Xms9m -Xmx9m -XX:+PrintGCDetails 设置堆空间的大小和打印GC的日志

public class GCTest {
    public static void main(String[] args) {

        int i = 0;
        try {
            List<String> list = new ArrayList<String>();
            String a = "huqi";
            while (true) {
                list.add(a);
                a = a + a;
                i++;
            }

        }catch (Throwable e) {
            e.printStackTrace();
            System.out.println("遍历次数是: " + i);
        }
    }
}

在这里插入图片描述


六.堆空间的分代思想

在这里插入图片描述
在这里插入图片描述


七.内存分配策略(对象提升(promotion)规则)

在这里插入图片描述
在这里插入图片描述

示例:

-Xms60m -Xmx60m -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:+PrintGCDetails
在这里插入图片描述
在这里插入图片描述


八.为对象分配内存:TLAB

对象分配过程: TLAB

TLAB是在新生代中每个线程独有分配的空间

为什么会有TLAB(Thread Local Allocation Buffer)

在这里插入图片描述

什么是TLAB

在这里插入图片描述
在这里插入图片描述

TLAB的再说明

在这里插入图片描述

TLAB 默认是开启的
在这里插入图片描述

首先将字节码文件进行加载,遇到了new对象的,首先看这个对象能不能在TLAB分配的空间中存放,可以的话就会直接进行实例化,如果无法放入,就会尝试放入伊甸园的公共内存空间,如果还是不能进行存放,则说明这个对象是个大对象,则将对象放入老年区中进行初始化.
在这里插入图片描述


九.小结堆空间的参数设置

官网说明
在这里插入图片描述
在这里插入图片描述

 * 测试堆空间中常用的JVM参数
 * -XX:+PrintFlagsInitial  -> 查看所有的参数的默认初始值
 * -XX:+PrintFlagsFinal     -> 查看所有的参数的最终值(可能会存在修改,不再是初始值)
 *     具体查看某个参数的指令:  jps: 查看当前运行中的进程
 *                           jinfo -flag SurvivorRatio 进程id
 * -Xms  -> 初始堆空间内存(默认为物理内存的1/64)
 * -Xmx  -> 最大堆空间内存(默认为物理内存的1/4)
 * -Xmn  -> 设置新生代的大小(初始值及最大值)
 * -XX:NewRatio  -> 配置新生代与老年代在堆结构的占比
 * -XX:SurvivorRatio  -> 设置新生代中Eden和S0/S1 空间的比例
 * -XX:MaxTenuringThreshold  -> 设置新生代垃圾的最大年龄
 * -XX:+PrintGCDetails  -> 输出详细的GC处理日志(包含GC记录和内存分配信息)
 *      打印GC简要信息:: 1> -XX:+PrintGC   2> -verbose:gc
 * -XX:HandlePromotionFailure -> 是否设置空间分配担保

在这里插入图片描述

在设置新生代中伊甸园区和幸存者区的比例的时候,如果伊甸园区的比例设置的过大,幸存者区很容易就满了,这个时候容易导致yGC失去意义,如果将伊甸园区设置的过小,会频繁的进行YGC,影响性能.

-XX:HandlePromotionFailure -> 是否设置空间分配担保

在这里插入图片描述


十.堆是分配对象的唯一选择吗

在这里插入图片描述

逃逸分析概述:

在这里插入图片描述
在这里插入图片描述

StringBuffer给返回来,对象逃逸了,使用toString()之后,返回的是一个新的String对象, sb对象留在了方法内部,没有进行逃逸,所以可以进行栈上分配
在这里插入图片描述
如何快速判断是否发生了逃逸 -> 就看new 的对象实体是否可能载类外部进行调用
如果obj是static的依然会发生逃逸
在这里插入图片描述

参数设置:

在这里插入图片描述
在这里插入图片描述

逃逸分析:代码优化

在这里插入图片描述

栈上分配

在这里插入图片描述

可以使用JVM参数 : -Xmx1G -Xms1G -XX:+DoEscapeAnalysis -XX:+PrintGCDetails
-XX:+DoEscapeAnalysis + 和 - 来控制是否开启逃逸分析,在7之后是默认开启的,可以使用-来进行关闭

通过开启和关闭逃逸分析可以看出,开启之前需要77ms才能执行完,开启之后只需4ms就可以执行完,打开vm工具查看堆内存的占用情况,发现内存中的对象由之前的10000000个减少到3000+个,大大减少了,而且伊甸园区的占用也大减少

public class StackAllocation {
    public static void main(String[] args) throws InterruptedException {
        long start = System.currentTimeMillis();

        for (int i = 0; i < 10000000; i++) {
            alloc();
        }

        // 查看执行时间
        long end = System.currentTimeMillis();
        System.out.println("花费的时间是 : " + (end - start) + "ms");

        Thread.sleep(1000000);

    }

    private static void alloc() {
        User user = new User();
    }

    static class  User{

    }
}

同步省略(消除)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

标量替换

在这里插入图片描述
在这里插入图片描述

例子

-Xmx100m -Xms100m -XX:+DoEscapeAnalysis -XX:+PrintGC -XX:-EliminateAllocations
设置堆空间大小 开启逃逸分析(默认即开启) 打印GC日志 关闭标量替换

public class ScalarReplace {
    public static class User {
        public int id;
        public String name;
    }

    public static void alloc() {
        User user = new User();
        user.id = 5;
        user.name = "www.huqi.com";
    }

    public static void main(String[] args) {
        long start = System.currentTimeMillis();

        for (int i = 0; i < 10000000; i++) {
            alloc();
        }
        long end = System.currentTimeMillis();

        System.out.println("时间 : " + (end - start) + "ms");
    }
}

关闭标量替换的结果:
在这里插入图片描述

打开标量替换的结果, 没有发生GC
在这里插入图片描述

在这里插入图片描述

逃逸分析并不是特别的成熟,所以,其实Hotspot没有使用逃逸分析…
所以,对象实例都是分配在堆上是没有问题的
在这里插入图片描述

小结

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值