12.JVM内存模型深度剖析与优化

84 阅读5分钟

课程内容

1、JDK体系结构与跨平台特性介绍

2、JVM内存模型深度剂析

3、从jvisualvm来研究下对象内存流转模型

4、讲透Gc Root与STW机制

5、日均百万级订单交易系统JVM参数如何设置

6、JVM参数设置通用模型

JVM整体结构及内存模型

JVM内存模型.png



public class Math{

    public static final int initData = 666;
    public static User user = new User();

    public int compute() {
        int a = 1;
        int b = 2;
        int c = (a + b) * 10;
        return c;
    }
    public static void main(String[] args) {
        Math math = new Math();
        math.compute();
        System.out.println("test");
    }
}

//javap -c .\Math.class > Math.txt
//理解为jvm的汇编语言,参照JVM指令手册
  public int compute();
    Code:
       0: iconst_1
       1: istore_1
       2: iconst_2
       3: istore_2
       4: iload_1
       5: iload_2
       6: iadd
       7: bipush        10
       9: imul
      10: istore_3
      11: iload_3
      12: ireturn
//使用



JVM的组成结构:①类装载子系统(c++实现)、②运行时数据区(内存模型)、③字节码执行引擎(c++实现)

从代码执行(java Math.class)角度来看。

①把字节码文件载入②,再由③来执行。


JVM内存模型主要组成

堆和方法区共享内存,其它每个线程单独分配内存空间

    • new 的对象存放区域
    • 1/3的新生代(8/10Eden + 1/10Survivor0 + 1/10Survivor1) + 2/3老年代
      • 触发minor gc时,非垃圾对象Eden→Survivor;Survivor(存活次数达到阈值)→ 老年代;Survivor区域清空1边,存活的对象copy至另外一边,存活次数+1
    • 堆由以下几个部分组成
      • 新生代(Young Generation)
        • 这个区域存放新创建的对象。可以划分为Eden空间和Survivor空间。大多数对象都会首先在Eden区分配,一次Minor GC后存活的对象会移到Survivor区。
      • 老年代(Old Generation)
        • 这个区域存放经过多次GC仍然存活的老对象。它比新生代的空间更多,GC也更少。
      • 永久代(Permanent Generation)
        • 这个区域存放Class的元数据信息,如类名、方法名、字段等。这部分内存回收效率低,但可以通过回收无用的类提高空间利用率。
      • 元数据空间(Metaspace)
        • 类似永久代,用于存储类的元信息,但它不再是堆的一部分,而是使用本机内存。
      • 直接内存(Direct Memory)
        • 这部分内存直接分配在物理内存上,不受JVM内存回收管理,一般使用在NIO操作中。
  • 栈(线程)
    • 不同线程分配不同的内存空间
      • 一个方法分配独立的栈帧内存空间
      • 栈帧的数据结构特征与程序执行顺序相同。
      • 栈帧的主要组成
        • 局部变量表(类似于数组结构)
        • 操作数栈
          • 操作数栈的作用是存储计算过程中的临时数据
        • 动态链接
          • 动态链接的作用是支持方法调用时的动态查找和连接
          • 把符号引用转换成方法区的地址
        • 方法出口
          • 执行完方法后,返回到其他方法的位置
  • 本地方法栈
    • 在Java中,除了大部分由Java语言编写的方法,还可以使用Native关键字调用本地语言(比如C/C++)编写的方法。这些Native方法不能像Java方法那样运行在栈帧中,因为它们的编译方式不同。所以JVM使用本地方法栈来执行Native方法
  • 方法区
    • 主要包含常量、静态变量、类信息。
  • 程序计数器
    • 记录即将运行代码的地址。多线程CPU挂起后继续执行的标识。
    • 每次执行完jvm指令,都会修改这个值。

理解了JVM整体结构及内存模型,对程序执行的了解变得深刻


GC 触发 STW 是指垃圾回收器执行垃圾回收时,会暂停应用程序的运行,造成 Stop The World (STW)。

Java 堆内存用于存放对象,当对象不再被使用时,垃圾回收器会重新回收堆内存。但回收过程中需要暂停正在运行的线程,使应用停止工作,这段时间称为Stop The World。之所以需要 STW,是因为:

  1. GC 需要保证内存一致性,不能有对象被改动
  2. 垃圾回收同时也要改动内存指针,不能有程序同时在使用
  3. 仅暂停最小的线程范围,可以显著提高效率所以为了内存一致性和效率,选择短暂暂停整个应用程序。

JVM内存参数设置

clipboard (1).png

java -Xms2048M -Xmx2048M -Xmn1024M -Xss512K -XX:MetaspaceSize=256M -XX:MaxMetaspaceSize=256M -jar microservice-eureka-server.jar

-Xss:每个线程的栈大小

-Xms:设置堆的初始可用大小,默认物理内存的1/64

-Xmx:设置堆的最大可用大小,默认物理内存的1/4

-Xmn:新生代大小

-XX:NewRatio:默认2表示新生代占年老代的1/2,占整个堆内存的1/3。

-XX:SurvivorRatio:默认8表示一个survivor区占用1/8的Eden内存,即1/10的新生代内存。

关于元空间的JVM参数有两个:-XX:MetaspaceSize=N和 -XX:MaxMetaspaceSize=N

-XX:MaxMetaspaceSize: 设置元空间最大值, 默认是-1, 即不限制, 或者说只受限于本地内存大小。

-XX:MetaspaceSize: 指定元空间触发Fullgc的初始阈值(元空间无固定初始大小), 以字节为单位,默认是21M左右,达到该值就会触发full gc进行类型卸载, 同时收集器会对该值进行调整: 如果释放了大量的空间, 就适当降低该值; 如果释放了很少的空间, 那么在不超过-XX:MaxMetaspaceSize(如果设置了的话) 的情况下, 适当提高该值。这个跟早期jdk版本的**-XX:PermSize**参数意思不一样,-XX:PermSize代表永久代的初始容量。

结论:

-Xss设置越小count值越小,说明一个线程栈里能分配的栈帧就越少,但是对JVM整体来说能开启的线程数会更多

JVM调优的目的,减少full gc