likes
comments
collection
share

Java篇| JVM模型

作者站长头像
站长
· 阅读数 67

一 、前言

  • 本文主要是对JVM的机制模型进行简单表述。理解JVM一些基本的运行原理。
  • 主要以JDK8分析

二、 思考的问题

1. 你将会怎样设计一门语言。

作为开发人员,通常会发现编程语言的语法相通性与差异性。比如学过C的会发现和C++之间的相通性。C与GO之间的相通性,如果是面向对象性语言C++,OC,Java,C#等等,无外乎一些语法的使用方式差别。而终究的核心是对计算机资源(网络,磁盘,内存,CPU等)的调度使用。而设计一门语言就是为了怎么调度或者更好的方式使用这些资源。

2. 理解 程序=数据结构+算法。

假设:对程序语言设计的定义 = 对计算机资源的调度使用的设计。 对计算资源的调度使用 = 数据元素的组织,计算存储和管理(数据结构) 设计 = 如何设计处理数据元素步骤(算法 ) 而最终程序语言设计产物也是程序 那么:数据结构+算法=程序

而我将JVM的设计称为对内存的操作设计

三、JVM虚拟机

一种以软件方式模拟硬件功能的计算系统。是物理机通过软件程序的实现。运行在隔离的独立环境。所以编写的JAVA代码编译后能够在JVM的环境上运行,而不用担心在不同硬件环境或系统平台上运行不通过。

1. JVM体系结构

Java篇| JVM模型

2. JVM运行时组成

简单上一张核心图,第四节会举例分析一下代码方法在JVM中的执行过程。 Java篇| JVM模型 JVM运行时数据区包含以下几个部分:

1. 程序计数器(Program Counter Register)

  • 用于记录下一条指令的地址,是线程私有的内存区域。
  • 当线程执行本地方法时,程序计数器的值为Undefined。

2. Java栈(Java Virtual Machine Stacks)

  • 用于存储Java方法执行的数据和指令,每个方法在执行时都会创建一个栈帧,栈帧在方法执行结束后被销毁。
  • Java虚拟机栈也是线程私有的内存区域。

3. 本地方法栈(Native Method Stack)

用于执行Native方法(即使用非Java语言编写的方法),与Java虚拟机栈的作用类似,也是线程私有的内存区域。

4. Java堆(Java Heap)

  • 用于存储对象实例和数组,是JVM中最大的一块内存区域,也是所有线程共享的内存区域。
  • 通常JVM进行GC回收的主要也是这块区域。

5. 元空间

  • 在JDK 8及以上版本的HotSpot虚拟机中,方法区不再使用永久代(PermGen)实现,而是引入了元空间(Metaspace)。也是所有线程共享的内存区域。

  • 方法区和元空间并不完全相同。可以将方法区看作是元空间的一个子集。

  • 可以动态调整大小,而且使用的内存不在虚拟机限制之内。Metaspace 的大小可以通过参数 -XX:MaxMetaspaceSize 进行设置。

  • 方法区(Method Area):主要包括虚拟机加载的类信息、类加载器信息、运行时常量池、静态变量、方法代码、常量、异常信息等数据。它也是所有线程共享的内存区域。如此多不同类型的数据,可以看出他的职责非常的杂多。


    1.  运行时常量池: 运行时常量池是每个类或接口中的常量池的运行时表示形式,与类共生死,包含类的常量池和静态变量池中的所有常量。
    2.  类信息: 类信息用于描述类的结构信息,如类名、父类名、实现的接口、字段及方法等。其中字段信息包括字段名称、修饰符、类型以及初始值等;方法信息包括方法名称、修饰符、返回值类型、参数列表以及方法体等。
    3.  静态变量: 静态变量是用 static 关键字声明的变量,它属于类而不是对象,只会被初始化一次。在方法区中存储着各类的静态变量,这些变量的值在类装载过程中被初始化,并可以被访问、修改。
    4.  方法代码: 方法代码是指经过编译后的字节码指令,保存在方法区中,包括了方法体中的所有Java虚拟机指令。
    5.  常量: 常量是指被 static final 修饰的基本类型和字符串变量,也包括了通过 final String 常量定义的字符串常量。
    6.  异常信息: 异常信息用于描述类中涉及的异常类型信息。
    7.  类加载器信息: 类加载器是负责从文件系统或网络等位置读取 Java 类文件,并把它们转换为 JVM 运行时数据结构 Class 对象的程序实体。方法区中存储着每个类加载器的信息,包括了类加载器的名称、父加载器、已经加载的类等。
    8.  符号引用:在 Java 虚拟机规范中,符号引用的解析过程被称为链接(Linking),该过程包括了验证、准备、解析三个阶段。提供了一种间接访问目标的方式,而在运行时期,这些符号引用将被解析成为对应的直接引用。方法区中存储着所有的符号引用,它们的解析工作会在类被加载时完成。
    

6. 直接内存(Direct Memory)

  • JVM也可以使用操作系统的内存来作为堆外内存,与Java堆不同,这部分内存不受JVM管理,不受 Java 堆大小限制,需要手动进行内存的分配和释放。也就是C里面的malloc()free()
  • 它是 JVM 使用 NIO ,利用 Native 函数库直接分配堆外内存的一种方式,主要用于提高 IO 操作的性能。 需要注意的是

直接内存不是Java虚拟机规范中规定的运行时数据区域之一,但是它又通常被归类为运行时数据区的一部分,因为它与前面几种类型之间存在着密切的关系,同时也会影响JVM运行时内存的使用和调优策略.

总体看来,JVM运行时数据区的不同部分在内存大小、作用、生命周期等方面都有所不同,开发者需要了解和掌握这些区域的特点和使用方法,以便更好地理解Java程序的执行过程。

四、 简单Java程序JVM执行分析

栈区

每个栈可理解为一个线程,每个线程上的方法运行时都有拥有栈帧。每个栈帧上又有局部变量,操作数栈,方法出口等特征。 Java篇| JVM模型

Fun程序Fun.java

public class Fun {

    public static int FLAG = 100;
    public static User user = new User();

    public int number() {
        int a = 1;
        int b = 2;
        int c = (a + b) * 5;
        return c;
    }

    public static void main(String[] args) {
        Fun fun = new Fun();
        fun.number();
        System.out.println("Fun!");
    }
}

使用javac Fun.java生成Fun.class,然后javap -c Fun.class反汇编,得到内容


    Compiled from "Fun.java"                                                                                                                                                          
    public class Fun {                                                                                                                                                                
      public static int FLAG;                                                                                                                                                         
                                                                                                                                                                                      
      public static User user;                                                                                                                                                        
                                                                                                                                                                                      
      public Fun();                                                                                                                                                                   
        Code:                                                                                                                                                                         
           0: aload_0                                                                                                                                                                 
           1: invokespecial #1                  // Method java/lang/Object."":()V                                                                                               
           4: return                                                                                                                                                                  
                                                                                                                                                                                      
      public int number();                                                                                                                                                            
        Code:                                                                                                                                                                         
           0: iconst_1                                                                                                                                                                
           1: istore_1                                                                                                                                                                
           2: iconst_2                                                                                                                                                                
           3: istore_2                                                                                                                                                                
           4: iload_1                                                                                                                                                                 
           5: iload_2                                                                                                                                                                 
           6: iadd                                                                                                                                                                    
           7: iconst_5                                                                                                                                                                
           8: imul                                                                                                                                                                    
           9: istore_3                                                                                                                                                                
          10: iload_3                                                                                                                                                                 
          11: ireturn                                                                                                                                                                 
                                                                                                                                                                                      
      public static void main(java.lang.String[]);                                                                                                                                    
        Code:                                                                                                                                                                         
           0: new           #2                  // class Fun                                                                                                                          
           3: dup                                                                                                                                                                     
           4: invokespecial #3                  // Method "":()V                                                                                                                
           7: astore_1                                                                                                                                                                
           8: aload_1                                                                                                                                                                 
           9: invokevirtual #4                  // Method number:()I                                                                                                                  
          12: pop                                                                                                                                                                     
          13: getstatic     #5                  // Field java/lang/System.out:Ljava/io/PrintStream;                                                                                   
          16: ldc           #6                  // String Fun!                                                                                                                        
          18: invokevirtual #7                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V                                                                           
          21: return                                                                                                                                                                  
                                                                                                                                                                                      
      static {};                                                                                                                                                                      
        Code:                                                                                                                                                                         
           0: bipush        100                                                                                                                                                       
           2: putstatic     #8                  // Field FLAG:I                                                                                                                       
           5: new           #9                  // class User                                                                                                                         
           8: dup                                                                                                                                                                     
           9: invokespecial #10                 // Method User."":()V                                                                                                           
          12: putstatic     #11                 // Field user:LUser;                                                                                                                  
          15: return                                                                                                                                                                  
    }         

简要分析number函数在栈上的操作,其他以此类推

下图表示了调用函数过程中,局部变量和操作数栈的变化过程。

Java篇| JVM模型 查阅指令文档The Java® Virtual Machine Specification (oracle.com)进行操作解释


        在第 0 行,使用 iconst_1 指令将常量 1 压入操作数栈中。
        在第 1 行,使用 istore_1 指令将操作数栈顶的数值存储到局部变量表的索引为 1 的位置中。此时,局部变量表中的第 1 个元素值为 1。
        在第 2 行,使用 iconst_2 指令将常量 2 压入操作数栈中。
        在第 3 行,使用 istore_2 指令将操作数栈顶的数值存储到局部变量表的索引为 2 的位置中。此时,局部变量表中的第 2 个元素值为 2。
        在第 4 行,使用 iload_1 指令将局部变量表索引为 1 的元素值(即 1)加载到操作数栈中。
        在第 5 行,使用 iload_2 指令将局部变量表索引为 2 的元素值(即 2)加载到操作数栈中。
        在第 6 行,使用 iadd 指令将操作数栈顶的两个数值相加,结果为 3,将其压入操作数栈中。
        在第 7 行,使用 iconst_5 指令将常量 5 压入操作数栈中。
        在第 8 行,使用 imul 指令将操作数栈顶的两个数值相乘,即计算出 3 * 5 = 15,将其压入操作数栈中。
        在第 9 行,使用 istore_3 指令将操作数栈顶的数值存储到局部变量表的索引为 3 的位置中。此时,局部变量表中的第 3 个元素值为 15。
        在第 10 行,使用 iload_3 指令将局部变量表索引为 3 的元素值(即 15)加载到操作数栈中。
        在第 11 行,使用 ireturn 指令将操作数栈顶的元素值作为方法的返回值。

程序计数器在过程中的作用

假设我们在上面线程main线程中运行到number()方法第5行,系统切换了其他线程,如果没有程序计数器记录当前线程的运行位置,那么再次回到main线程时候,程序就将不知道从什么地方继续执行。

五、 堆内存垃圾回收机制

1. 堆内存模型

Java篇| JVM模型

新生代

  • 新生代是对象创建后被分配的内存区域,通常包含 Eden 区、Survivor From 和 To 两个区域。其中,Eden 区是对象创建时分配的内存区,大部分新生对象都在 Eden 区中分配。
  • 当 Eden 区没法容纳所有新生对象时,此时会触发一次垃圾回收,将存活的对象移动到 Survivor 区 From 区,并清空 Eden 区。之后,若再次触发垃圾回收,便将存活的对象从 Survivor 区 From 区移动到 Survivor 区 To 区,并清空 Survivor 区 From 区。经过多次垃圾回收,存活下来的对象会被移到老年代。

老年代

  • 老年代用于存储存活时间较长的对象,通常包含较多的 Java 对象。
  • 当 Eden 区和 Survivor 区无法容纳当前对象时,这些对象会被直接分配到老年代。老年代内的对象不太容易被回收,因此需要特殊的垃圾收集算法进行回收。

大小比例

  • 通常情况下新生代和老年代的比例为1:2或1:3
  • Eden:From : To 为 8:1:1

这些比例大小都是可以通过JVM参数进行调整。合适的大小和分配比例可以归类为JVM调优的范畴,主要在运行Java后台服务的机器上需求较多,而对于移动Android或客户端平台,调优需求比较少。不同的业务逻辑和硬件条件都可能会影响到触发GC的频率,不同的垃圾回收算法也会对应适用不同的场景。

2. Minor GC和 Full GC

Minor GC : 或者叫Young GC,只会清理新生代内存区域,比较频繁,将存活时间较长的对象移到老年代

Full GC : 或者Major GC,会清理老年代、新生代、甚至元空间。Full GC 的触发条件可以是多种多样的,比如老年代空间不足、永久代或者元空间满、System.gc() 方法显式调用等。

通常会有一些辅助工具如Java VisualVM等可以帮助我们更好的了解GC信息。

3. STW机制

Java 中的 STW,全称为 Stop-The-World,是垃圾回收器进行垃圾回收时暂停应用程序的一种机制。在这个过程中,JVM 会挂起所有的 Java 线程,包括垃圾回收线程和用户线程,直到垃圾回收完成。STW 机制可以保证垃圾回收的正确性和一致性,但也会对应用程序性能造成一定的影响。

  • Minor GC(短)和Full GC(长)一般STW的时间不同
  • 不同垃圾回收算法STW一般情况下时间也不相同

六、 结尾

  • 通过本篇文章,或多或少对了解JVM机制和GC机制有所启发。
  • 待补充介绍GC的几种算法。
转载自:https://juejin.cn/post/7236413212093694010
评论
请登录