java - JVM 详解

本文详细介绍了JVM的结构,包括类加载器子系统、双亲委托机制、链接阶段和初始化阶段。重点讨论了运行时数据区的各个部分,如程序计数器、Java栈、本地方法栈、堆和方法区,特别是堆的新生代与老年代划分。此外,还提到了JVM的配置选项,如堆内存的初始和最大大小设置,以及HotSpot虚拟机。文章最后提及了方法区的实现——元空间,以及早期的其他Java虚拟机如ClassicVM和KVM。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

jvm 介绍

jvm 是什么?jvm 是java程序最终运行的地方,jdk默认虚拟机HotSpot,是java能成为跨平台的依仗!(后续介绍到jvm 整体结构的时候会说到是为什么)

大致简略图
在这里插入图片描述

类加载器子系统

在这里插入图片描述

加载阶段

类加载器分两种:引导类加载器与自定义类加载器。因为引导类加载器是C与C++编写的,扩展类与系统类的加载器都是间接性的继承ClassLoader类来实现的类加载器。扩展类加载器包含系统类加载器,虽然是包含但是实际通过代码 查看他们两个是 同级的。

  • 引导类加载器:负责加载 本地库,包名java开头的jar 或者类
  • 扩展类加载器:负责加载jdk\lib\ext 下面的包
  • 系统类加载器:负责加载开发人员编辑创建的类
双亲委托机制

在这里插入图片描述

  • 加载类的时候,首先会逐步向上找到最顶端的加载器
  • 然后加载器根据自身负责加载条件是否满足,满足条件加载,不满足向下传递,让下一个类加载去加载

链接阶段

在这里插入图片描述

初始化阶段

赋值操作 举例子如下:

static int c = 1; //链接阶段 c = 0 ,初始化阶段 c = 1

双亲委托机制是为了防止:重复加载 和 对核心库的保护。举例子创建一个相同的java.lang.String 如果没有双清委托那么,就会错误的加载类 导致 原本正常的代码中String 的使用出现错误进而导致程序崩溃。

运行时数据区

程序计数器

所谓的程序计数器,可以理解为是一个记录下一个字节码指令的记录地址。执行器通过 程序计数器上找到的地址然后去java 栈中执行。

java 栈(简称 栈)

先看下详情的结构图(一个方法对应一个栈帧)
在这里插入图片描述

  • 局部变量表(主要): 记录 非静态类的方法中,隐式关键字this ,形参,局部创建的变量。
  • 操作数栈(主要):每次进行计算机指计算,都会先对值做一次入栈出栈的动作。
  • 方法返回地址:记录每次正常结束或者异常结束之后下一个栈的地址。
  • 动态链接:每次class 文件加载完成之后会有一个常量池,不同的常量对应不同的符号引用,动态链接就是这这些符号引用
  • 一些附加信息:一般是针对内容的附加提示信息,供给给开发人员一定的帮助或者提示。

本地方法栈

负责管理本地方法的库与接口,这些本地方法库是c/c++实现编写的。

结构图:
在这里插入图片描述

堆内存大致分为两部分:第一部分 新生代+ 老年代 称谓 堆、第二部分原空间。

  • 堆空间负责存储示例对象以及数组(从jdk7包括7开始新增了字符串常量与静态变量)
  • 元空间 方法区的具体实现 具体下面 方法中介绍。
  • 新生代分为 :Eden 与 s0(Survivor)和s1(Survivor), s0与s1之间有个规则昵称 ‘from’与’to’ 谁空谁就是to ,反之为from 。
  • Eden 区内部还有一种叫做TLAB 的区域,只有Eden的1%大小,作为线程私有,堆作为共享内存那么就会涉及到线程安全进而涉及到高并发性能 ,TLAB作用就是在一定程度上解决高并发性能的问题。后面会说到怎么解决
  • 默认新生代:老生代 空间大小是 1:2
  • 默认Eden:s0:s1 空间大小是:8:1:1
配置
 -Xms 			堆初始大小
 -Xmx			堆最大
 -XX:NewRatio=1 新生代老年代内存空间比例 默认1:2
 -XX:SurvivorRatio=4 Eden与s0、s1的比例  默认8:1:1(提示需要显示配置才生效.)
 

方法区

本地方法区存储的是(最恰当的一种):类型信息、运行时常量池、静态变量、JIT 代码缓存、域(属性)信息、方法信息。简单理解字节码文件的内容存储到了方法区。

  • 元空间 则是方法区的具体实现。
  • 首先要明白方法区就要看懂 字节码文件如下:
/**下面是源码*/ 
public class CctvTest {
    public static void main(String[] args) {
        CctvTest cctvTest = new CctvTest();
        int z = 19;
        cctvTest.heoll("hl");
    } 
    
    public void heoll(String str) {
        System.out.println("欢迎光临" + str);
        int i = heoll2(10);
    } 
    
    public int heoll2(int i) {
        int c = 1;
        int b = 3;
        String str = heoll3("hl");
        return c + b + i;
    }

    public String heoll3(String hl) {
        int c = 1;
        int b = 3;
        return hl + "";
    }
}

/**下面是反编译class文件拿到的字节码文件*/
//文件信息
Classfile /F:/MyProject/学习开始/houliang/爬虫/NDA/cloud-alibaba-nda/alibaba-data-acquisition/target/test-classes/CctvTest.class
  Last modified 2021-2-22; size 1252 bytes
  MD5 checksum 3fdf47090d6d5e62d4c3ed083dc76af8
  Compiled from "CctvTest.java"
  //类信息
public class CctvTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER //修饰关键字
Constant pool:  //运行时常量池 是数组结构 #1、#2可以理解为下标一样的东西,
//其实可以简单理解,今天要做晚饭 n个菜,我们就需要采购 例如盐味精 白菜 香葱、
//我们不是说 盐都会用到所以按每个菜去买一份盐,而是一份盐 每个菜都用。
//因为方法区只是存储这些信息,具体的实现是元空间所以,想说的是,这里只需要把
//盐、白菜、红酒...等等写好就好了 元空间去实现
   #1 = Methodref          #16.#45        // java/lang/Object."<init>":()V
   #2 = Class              #46            // CctvTest
   #3 = Methodref          #2.#45         // CctvTest."<init>":()V
   #4 = String             #42            // hl
   #5 = Methodref          #2.#47         // CctvTest.heoll:(Ljava/lang/String;)V
   #6 = Fieldref           #48.#49        // java/lang/System.out:Ljava/io/PrintStream;
   #7 = Class              #50            // java/lang/StringBuilder
   #8 = Methodref          #7.#45         // java/lang/StringBuilder."<init>":()V
   #9 = String             #51            // 欢迎光临
  #10 = Methodref          #7.#52         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #11 = Methodref          #7.#53         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #12 = Methodref          #54.#55        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #13 = Methodref          #2.#56         // CctvTest.heoll2:(I)I
  #14 = Methodref          #2.#57         // CctvTest.heoll3:(Ljava/lang/String;)Ljava/lang/String;
  #15 = String             #58            //
  #16 = Class              #59            // java/lang/Object
  #17 = Utf8               <init>
  #18 = Utf8               ()V
  #19 = Utf8               Code
  #20 = Utf8               LineNumberTable
  #21 = Utf8               LocalVariableTable
  #22 = Utf8               this
  #23 = Utf8               LCctvTest;
  #24 = Utf8               main
  #25 = Utf8               ([Ljava/lang/String;)V
  #26 = Utf8               args
  #27 = Utf8               [Ljava/lang/String;
  #28 = Utf8               cctvTest
  #29 = Utf8               z
  #30 = Utf8               I
  #31 = Utf8               heoll
  #32 = Utf8               (Ljava/lang/String;)V
  #33 = Utf8               str
  #34 = Utf8               Ljava/lang/String;
  #35 = Utf8               i
  #36 = Utf8               heoll2
  #37 = Utf8               (I)I
  #38 = Utf8               c
  #39 = Utf8               b
  #40 = Utf8               heoll3
  #41 = Utf8               (Ljava/lang/String;)Ljava/lang/String;
  #42 = Utf8               hl
  #43 = Utf8               SourceFile
  #44 = Utf8               CctvTest.java
  #45 = NameAndType        #17:#18        // "<init>":()V
  #46 = Utf8               CctvTest
  #47 = NameAndType        #31:#32        // heoll:(Ljava/lang/String;)V
  #48 = Class              #60            // java/lang/System
  #49 = NameAndType        #61:#62        // out:Ljava/io/PrintStream;
  #50 = Utf8               java/lang/StringBuilder
  #51 = Utf8               欢迎光临
  #52 = NameAndType        #63:#64        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #53 = NameAndType        #65:#66        // toString:()Ljava/lang/String;
  #54 = Class              #67            // java/io/PrintStream
  #55 = NameAndType        #68:#32        // println:(Ljava/lang/String;)V
  #56 = NameAndType        #36:#37        // heoll2:(I)I
  #57 = NameAndType        #40:#41        // heoll3:(Ljava/lang/String;)Ljava/lang/String;
  #58 = Utf8
  #59 = Utf8               java/lang/Object
  #60 = Utf8               java/lang/System
  #61 = Utf8               out
  #62 = Utf8               Ljava/io/PrintStream;
  #63 = Utf8               append
  #64 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #65 = Utf8               toString
  #66 = Utf8               ()Ljava/lang/String;
  #67 = Utf8               java/io/PrintStream
  #68 = Utf8               println
{
	//默认构造函数 <init>
  public CctvTest();		//方法昵称
    descriptor: ()V			//参数即返回
    flags: ACC_PUBLIC		//修饰关键字
    Code:					//字节码指令集
      //stack 操作数栈 深度
      //locals 局部变量表
      //args_size 参数个数
      stack=1, locals=1, args_size=1
      //指令下标:操作指令    运行时常量池符号引用
         0: aload_0					
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:		//java 代码行 与字节码指令行引用
        line 3: 0
      LocalVariableTable:	//局部变量表
        Start  Length  Slot  Name   Signature
            0       5     0  this   LCctvTest;
	//以下结构相同
  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=3, args_size=1
         0: new           #2                  // class CctvTest
         3: dup
         4: invokespecial #3                  // Method "<init>":()V
         7: astore_1
         8: bipush        19
        10: istore_2
        11: aload_1
        12: ldc           #4                  // String hl
        14: invokevirtual #5                  // Method heoll:(Ljava/lang/String;)V
        17: return
      LineNumberTable:
        line 5: 0
        line 6: 8
        line 7: 11
        line 8: 17
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      18     0  args   [Ljava/lang/String;
            8      10     1 cctvTest   LCctvTest;
           11       7     2     z   I

  public void heoll(java.lang.String);
    descriptor: (Ljava/lang/String;)V
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=3, args_size=2
         0: getstatic     #6                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: new           #7                  // class java/lang/StringBuilder
         6: dup
         7: invokespecial #8                  // Method java/lang/StringBuilder."<init>":()V
        10: ldc           #9                  // String 欢迎光临
        12: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: aload_1
        16: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        19: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        22: invokevirtual #12                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        25: aload_0
        26: bipush        10
        28: invokevirtual #13                 // Method heoll2:(I)I
        31: istore_2
        32: return
      LineNumberTable:
        line 12: 0
        line 13: 25
        line 14: 32
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      33     0  this   LCctvTest;
            0      33     1   str   Ljava/lang/String;
           32       1     2     i   I

  public int heoll2(int);
    descriptor: (I)I
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=5, args_size=2
         0: iconst_1
         1: istore_2
         2: iconst_3
         3: istore_3
         4: aload_0
         5: ldc           #4                  // String hl
         7: invokevirtual #14                 // Method heoll3:(Ljava/lang/String;)Ljava/lang/String;
        10: astore        4
        12: iload_2
        13: iload_3
        14: iadd
        15: iload_1
        16: iadd
        17: ireturn
      LineNumberTable:
        line 17: 0
        line 18: 2
        line 19: 4
        line 20: 12
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      18     0  this   LCctvTest;
            0      18     1     i   I
            2      16     2     c   I
            4      14     3     b   I
           12       6     4   str   Ljava/lang/String;

  public java.lang.String heoll3(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=4, args_size=2
         0: iconst_1
         1: istore_2
         2: iconst_3
         3: istore_3
         4: new           #7                  // class java/lang/StringBuilder
         7: dup
         8: invokespecial #8                  // Method java/lang/StringBuilder."<init>":()V
        11: aload_1
        12: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        15: ldc           #15                 // String
        17: invokevirtual #10                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        20: invokevirtual #11                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        23: areturn
      LineNumberTable:
        line 25: 0
        line 26: 2
        line 27: 4
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      24     0  this   LCctvTest;
            0      24     1    hl   Ljava/lang/String;
            2      22     2     c   I
            4      20     3     b   I
}
SourceFile: "CctvTest.java"

后面会讲解整体的java 类执行过程就能更好的理解

执行引擎

本地方法接口

本地方法库

java 代码 执行过程

其他虚拟机

早期java 出过:

  • Classic VM 已淘汰
  • Exact VM 已淘汰
  • HotSpot VM 目前jdk 默认虚拟机
  • KVM 几乎淘汰,因为java ME 做终于移动应用方面,早期洛基亚 塞班系统上门的游戏java 就是运行在KVM 虚拟机上现在…这部分市场…还有吗…

当然还有其他虚拟机例如:

  • JRockit (BEA公司)后来被oracle收购,然后差不多jdk8已经完成了 HotSpot 整合
  • J9 VM (IBM公司)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值