JVM整体结构

1.Java语言的跨平台特性

image-20211216155841437

2.JVM整体结构及内存模型

image-20211216160726718

image-20211216173824033

JVM是由类装载子系统,运行时数据区,java执行引擎三部分组成的
这三部分是相辅相成共同工作了,不能单纯的说只有执行完某一个部分才由下一个部分来工作,从JVM运行开始,三个类加载器就要开始创建,并且类加载器的一些信息也要放在运行时数据区

1.类装载子系统

.class文件通过类装载系统进行加载,验证等一系列操作,在这个过程中将需要存储到内存的数据放到运行时数据区
类装载子系统也是之前的类加载机制介绍的

2.运行时数据区

1. 虚拟机栈栈:  线程独享  每个线程栈存放执行的方法,每个方法为一个栈帧
2. 程序计数器:  线程独享  存放每个线程中指令执行到了哪一个
3. 本地方法栈:  线程独享  类似于虚拟机栈,只不过这里存放的是Native(调用C++)的方法
4. 堆:  线程共享   对象存放位置
5. 方法区:  线程共享    类信息,常量,静态变量等  
    类信息(C++的一些对象)    静态变量,如果指针指向的是一个对象,存放的依然是堆中对象的地址

虚拟机栈的结构

每个线程的虚拟机栈中有大量的栈帧,表示每个方法,如main方法等
栈帧中:
​
局部变量表:  存放每个方法中的局部变量,用一张表存储,当一个变量指向对象时,存放的是堆空间中对象的地址
操作数栈:    当我们进行数值操作时,先将数字压入操作数栈,再进行运算,无论是更新局部变量表还是进行数值运算
            例如: a=1,将常量1压入操作数栈,再将1赋值给局部变量表的a
            c = b+a,将b,a的值从局部变量表取出,压入操作数栈,弹出操作数栈交给cpu运算,结果压入操作数栈,再赋值给局部变量表
动态链接:  在程序运行的过程中,动态的找寻方法区中方法名所所在的内存地址
方法出口:  存放当前方法应该返回到调用者的哪里

方法区结构

宏观来看,主要我们关注的信息有这些

image-20211223165845438

微观看,如下这些信息.

image-20211223195514910

1. 类进行加载解析等以后,创建出类元信息和静态变量和常量放入到方法区中
2. 运行时常量池  Class文件中的资源仓库
3. 域信息,成员变量信息
4. 方法信息,方法的参数,返回值,局部变量等信息
当类被加载到内存以后.class文件就被打碎,分配到方法区中的各个部分,方法区中不再存放.class的源代码,如下,是把源代码每个部分分开
  Last modified 2020-4-22; size 1626 bytes
  MD5 checksum 69643a16925bb67a96f54050375c75d0
  Compiled from "MethodInnerStrucTest.java"
  //类型信息会被加载到方法区
public class com.atguigu.java.MethodInnerStrucTest extends java.lang.Object // 类的全限定名以及父类
implements java.lang.Comparable<java.lang.String>, java.io.Serializable //类实现的接口信息
  minor version: 0
  major version: 51
  flags: ACC_PUBLIC, ACC_SUPER // 类的权限修饰符
Constant pool:
   #1 = Methodref          #18.#52        // java/lang/Object."<init>":()V
   #2 = Fieldref           #17.#53        // com/atguigu/java/MethodInnerStrucTest.num:I
   #3 = Fieldref           #54.#55        // java/lang/System.out:Ljava/io/PrintStream;
   ...
{
  //域信息会被加载到方法区
  public int num; // 域名称
    descriptor: I // 域类型
    flags: ACC_PUBLIC // 域权限
​
  private static java.lang.String str;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC
​
  //方法信息会被加载到方法区
  public com.atguigu.java.MethodInnerStrucTest(); // 方法名称
    descriptor: ()V //方法参数及方法返回值类型
    flags: ACC_PUBLIC //方法权限修饰符
    Code: //方法对应的字节码
      stack=2, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: aload_0
         5: bipush        10
         7: putfield      #2                  // Field num:I
        10: return
      LineNumberTable:
        line 10: 0
        line 12: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      11     0  this   Lcom/atguigu/java/MethodInnerStrucTest;
            ......
}

3.java执行引擎

用于执行栈中指令,一条一条的指令读取,类似一个cpu,我们的虚拟机栈中操作数栈弹栈进行运算的运算就是由执行引擎进一步完成的
执行引擎执行的并不是.class的源代码,源代码被打碎分配以后,执行引擎如何执行?
​
我们.class被加载以后,首先会将main方法压入栈中,去执行main方法的方法信息,需要运行时常量池信息就去常量池中取,进行一步一步的局部变量赋值等操作,当执行到其他方法调用时,就会动态链接去寻找这个方法在方法区中的方法信息,一步一步执行.
​
例如: main函数里面有compete方法,当执行到compete方法时,main函数里面的动态链接会找寻compete方法存放的方法区地址(动态链接),然后为compete创建栈帧,在栈帧中为其局部变量分配在局部变量表中,右执行器执行

1.分代GC模型

image-20211216201838212

2.JVM内存参数分配

img

堆:  JVM内存分配,在我们JVM启动的参数或者由JVM自动分配的一个大小,这个大小在JVM启动以后就不会改变了
    使用JVM启动时的默认最大和最小不一样的,可以根据使用情况自动扩缩容,但是最大是不会超过默认上限的,超出OOM(内存溢出)
    
方法区: 在直接内存分配,比如默认16g硬件内存,除去系统开销可以一直分配的10几G
    但是可以调整方法区触发FGC的大小,默认比较小,也会根据FGC的情况弹性扩缩容调整FGC触发的大小
    我们可以使用参数MetaspaceSize将触发FGC大小调大并且设置为固定值,也是调优的方式
​
栈: 在直接内存分配,也是可以分配10几G,栈是线程独立,多个线程就会产生多个栈空间,我们不能控制所有线程栈总和的大小
    但是我们可以通过参数调整每单个线程栈的大小,-Xss,把这个调小了那么我们在同等硬件内存大小的情况下,就能运行更多的线程
    每个线程栈中有栈帧的概念,栈帧的大小是不能确定的,每个方法一个栈帧
    -Xss设置越小,说明一个线程栈里能分配的栈帧就越少,但是对JVM整体来说能开启的线程数会更多
Spring Boot程序的JVM参数设置格式(Tomcat启动直接加在bin目录下catalina.sh文件里):
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代表永久代的初始容量。
由于调整元空间的大小需要Full GC,这是非常昂贵的操作,如果应用在启动的时候发生大量Full GC,通常都是由于永久代或元空间发生了大小调整,基于这种情况,一般建议在JVM参数中将MetaspaceSize和MaxMetaspaceSize设置成一样的值,并设置得比初始值要大,对于8G物理内存的机器来说,一般我会将这两个值都设置为256M。

4.整体工作模型

public class Math {
    public static final int initData = 666;
    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();
        User user = new User();
        while (true){
            math.compute();
        }
    }
}
class User{
    
}
1. 将Math.class放入到类装载子系统,进行加载,验证等,将二进制字节码放入到内存中,变成方法区的运行时常量,方法信息等,并mian压栈
2. 执行器执行main方法的信息
3. 执行到new Math,创建Math的对象分配在堆中
4. 执行到new User时,检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载
5. 如果没有加载,进行双亲委派加载
# 用到那个类将哪个类进行加载,将.class打碎,每个部分按照自己的类型放到运行时常量,静态变量,常量,类元信息等放入到方法区
# 通过栈完成整个代码的执行过程,执行器执行栈中信息

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
抱歉,我无法提供图片或图表。但是,我可以为您描述JDK的JVM结构。 JDK(Java Development Kit)是Java开发工具包,其中包含了JRE(Java Runtime Environment)和一系列的开发工具。JVMJava Virtual Machine)是JDK中的一个重要组成部分。 JVM是一个虚拟机,它负责解释和执行Java字节码文件。它是Java程序运行的环境。JVM内部包含了多个组件,包括类加载器、解释器、即时编译器、垃圾收集器等。 类加载器负责加载Java类文件,并将其转换为JVM可以理解的格式。解释器负责解释字节码文件,并逐行执行其中的指令。即时编译器可以将热点代码(经常执行的代码)编译成本地机器码,以提高执行效率。垃圾收集器负责回收不再使用的内存空间。 JVM结构可以简单描述为:JVM包含了类加载器、解释器、即时编译器和垃圾收集器等组件,它们共同协作来解释和执行Java字节码文件。 希望这个描述对您有所帮助。如果您需要更详细的信息,请参考JDK的官方文档。 #### 引用[.reference_title] - *1* *2* *3* [JDK、JRE、JVM 之间的关系](https://blog.csdn.net/m0_56673710/article/details/126093658)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值