「后端」JVM最优详解(内存模型、堆、GC、直接内存、性能调优)

JVM 内存模型结构图

jdk1.8 结构图(极简)

jdk1.8 结构图(简单)

JVM(Java虚拟机):

jdk1.7 结构图(详细)


JVM 内存模型组成元素

Java 内存模型主要包含线程私有程序计数器java虚拟机栈本地方法栈线程共享堆空间元数据区直接内存

Java 程序内存 = JVM 内存 + 本地内存

堆外内存


JVM 堆及各种 GC 详解

JVM 中的堆,一般分为三大部分:新生代、老年代、永久代( Java8 中已经被移除)

新生代、MinorGC(Young GC)

新生代

MinorGC

老年代、MajorGC(Old GC)

老年代

永久代、元数据区(元空间)、常量池

永久代(PermGen)

元数据区(元空间、Metaspace)

元空间替换永久代的原因分析:

类常量池、运行时常量池、字符串常量池

拓展

Full GC 、Major GC(Old GC)

Minor GC、Major GC、Full GC 的区别

Full GC 触发机制:

堆空间分成不同区的原因

堆不是对象存储的唯一选择(逃逸分析)

如果经过逃逸分析后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配。这样无需在堆上分配内存。也无须进行垃圾回收了。

逃逸分析概述: 一种可以有效减少 Java 程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法。

逃逸分析的基本行为就是分析对象动态作用域:


GC(垃圾回收)

System.gc()


如何检测应用程序正在进行 System.gc()?


如何禁止GC显式调用或调整调用GC的频率?

如果就是想避免程序员显式调用GC,避免不成熟的程序员在不合适时间调用GC,避免人为造成的GC崩溃,可以通过如下方法:


STW(Stop The World)事件

stop-the-world,简称 STW,指的是 GC 事件发生过程中,会产生应用程序的停顿。停顿产生时整个应用程序线程都会被暂停,没有任何响应,有点像卡死的感觉,这个停顿称为 STW。

可达性分析算法中枚举根节点(GC Roots)会导致所有 Java 执行线程停顿。

被 STW 中断的应用程序线程会在完成 GC 之后恢复,频繁中断会让用户感觉像是网速不快造成电影卡带一样,所以需要减少 STW 的发生。

STW 事件和采用哪款 GC 无关,所有的 GC 都有这个事件。哪怕是 G1 也不能完全避免 Stop-the-world 情况发生,只能说垃圾回收器越来越优秀,回收效率越来越高,尽可能地缩短了暂停时间。

STW 是 JVM 在后台自动发起和自动完成的。在用户不可见的情况下,把用户正常的工作线程全部停掉。

开发中除非特殊情况,不要用 system.gc() 进行手动 GC,会导致 stop-the-world 的发生。


GC 常用算法


直接内存(Direct Memory)详解

文件的读写过程

演示案例(运行并比较时间后可以发现,尤其是读写大文件时使用 ByteBuffer 的读写性能非常高):

// 演示ByteBuffer作用
public class Demo {
    static final String FORM = "D:asdasd.mp4"; // 选比较大的文件,比如200多兆
    static final String TO = "D:asd.mp4";
    static final int _1Mb = 1024 * 1024;

    public static void main(String[] args) {
        // io 用时:3187.41008(大概用了3秒),多跑几遍,多比较,跑一次不算。
        io();
        // directBuffer 用时:951.114625(不到1秒)
        derectBuffer();
    }

    private static void deirectBuffer() {
        long start = System.nanoTime();
        try (FileChannel from = new FileInputStream(FROM).getChannel();
            FileChannel to = new FileOutputStream(TO).getChannel();
        ) {
            ByteBuffer bb = ByteBuffer.allocateDirect(_1Mb); // 读写的缓冲区(分配一块儿直接内存)
            while (true) {
                int len = from.read(bb);
                if (len == -1) {
                    break;
                }
                bb.flip();
                to.write(bb);
                bb.clear();
            }

        }catch (IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        print("directBuffer用时:" + (end - start) / 1000_000.0);
    }

    // 用传统的io方式做文件的读写
    private static void io() {
        long start = System.nanoTime();
        try ( // 网友1:写到try()括号里就不用手动close了
            FileInputStream from = new FileInputStream(FROM);
            FileOutPutStream to = new FileOutputStream(TO);
        ) {
            byte[] buf = new byte[_1Mb];// byte数组缓冲区(与上面的读写缓冲区设置大小一致,比较时公平)
            while (true) {
                int len = from.read(buf);// 用输入流读
                if (len == -1) {
                    break;
                }
                to.write(buf, 0, len);// 用输出流写
            }
        }catch(IOException e) {
            e.printStackTrace();
        }
        long end = System.nanoTime();
        print("io用时:" + (end - start) / 1000_000.0);
    }
}

直接内存的分配和回收

直接内存的分配和释放是 Java 通过 UnSafe 对象来管理的,并且回收需要主动调用 freeMemory() 方法,不直接受 JVM 内存回收管理。

ByteBuffer 底层分配和释放直接内存的大概情况

演示案例(演示直接内存溢出)

// 演示直接内存溢出
public class Demo {
    static int _100Mb = 1024 * 1024 * 100;
    
    public static void main(String[] args) {
        List list = new ArrayList<>();
        int i = 0; 
        try {
            while (true) {
                ByteBuffer byteBuffer = ByteBuffer.allocateDirect(_100Mb);// 每次分配100兆内存
                list.add(byteBuffer);// 把这玩意放到List中,一直循环
                i++;
            }
        }finally {
            print(i);
        }
    }
}

使用 System.gc() 间接进行直接内存的回收可能存在的问题


JVM 的性能调优

调优参数

配置方式


内存参数:


垃圾回收器参数

JVM给了三种选择:串行收集器、并行收集器、并发收集器。串行收集器只适用于小数据量的情况。


元空间参数:


辅助参数

JVM提供了大量命令行参数,打印信息,供调试使用。商业项目上线的时候,不允许使用。一定使用 loggc。主要有以下一些:


调优建议

文章来源:墨鸦_https://developer.aliyun.com/article/1165868

展开阅读全文

页面更新:2024-05-04

标签:内存   线程   详解   虚拟机   模型   对象   大小   垃圾   性能   年代   参数   方法   空间

1 2 3 4 5

上滑加载更多 ↓
推荐阅读:
友情链接:
更多:

本站资料均由网友自行发布提供,仅用于学习交流。如有版权问题,请与我联系,QQ:4156828  

© CopyRight 2020-2024 All Rights Reserved. Powered By 71396.com 闽ICP备11008920号-4
闽公网安备35020302034903号

Top