学习JVM基础分区

139 阅读13分钟

-java-

..1 java代码的执行

  1. 编译阶段

    • Java源代码(.java文件)首先被Java编译器(javac)编译成字节码(.class文件)。
    • 这个编译过程是必要的,因为它将高级的Java语言转换成一种平台无关的中间代码——字节码。
    • 字节码是一种低级、与平台无关的表示形式,它设计上是为了优化执行速度和便于移植。
  2. 解释阶段

    • 字节码随后被Java虚拟机(JVM)加载。
    • JVM中的解释器(Interpreter)读取字节码并将其转换成机器码,这一过程是即时发生的,即在程序运行时进行。
    • 除了解释器之外,现代JVM通常还包含即时编译器(JIT,Just-In-Time Compiler),JVM会跟踪每个方法的调用次数。如果一个方法被频繁调用,它就被标记为“热”方法,当一个方法被认为是“热”的,JIT编译器会将该方法对应的字节码编译成机器码,编译后的机器码会被存储在代码缓存中,以便该方法下次被调用时可以直接执行,而无需重新编译
    • JVM还负责内存管理、垃圾回收以及其他运行时环境的任务。

..2 spring的启动代码

E:\java_1\java\bin\java.exe 
-XX:TieredStopAtLevel=1 -Dspring.output.ansi.enabled=always 
-Dcom.sun.management.jmxremote 
-Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain 
-Dspring.application.admin.enabled=true "-Dmanagement.endpoints.jmx.exposure.include=*" 
-Dhxl.spring.invoke.port=33333 "-javaagent:D:\home\.idea\IntelliJ IDEA 2023.3.2\lib\idea_rt.jar=61821:D:\home\.idea\IntelliJ IDEA 2023.3.2\bin"
-Dfile.encoding=UTF-8 -classpath E:\java_1\javaproj\A_TestMoudle\arthas_sf\target\classes;
E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\data\spring-data-keyvalue\2.6.4\spring-data-keyvalue-2.6.4.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\data\spring-data-commons\2.7.2\spring-data-commons-2.7.2.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\spring-beans\5.3.22\spring-beans-5.3.22.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\spring-context\5.3.22\spring-context-5.3.22.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\spring-aop\5.3.22\spring-aop-5.3.22.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\spring-expression\5.3.22\spring-expression-5.3.22.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\spring-tx\5.3.22\spring-tx-5.3.22.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\slf4j\slf4j-api\1.7.36\slf4j-api-1.7.36.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\google\code\gson\gson\2.8.9\gson-2.8.9.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\google\guava\guava\30.1.1-jre\guava-30.1.1-jre.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\google\guava\failureaccess\1.0.1\failureaccess-1.0.1.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\google\guava\listenablefuture\9999.0-empty-to-avoid-conflict-with-guava\listenablefuture-9999.0-empty-to-avoid-conflict-with-guava.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\google\code\findbugs\jsr305\3.0.2\jsr305-3.0.2.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\checkerframework\checker-qual\3.8.0\checker-qual-3.8.0.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\google\errorprone\error_prone_annotations\2.5.1\error_prone_annotations-2.5.1.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\google\j2objc\j2objc-annotations\1.3\j2objc-annotations-1.3.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\jsoup\jsoup\1.15.3\jsoup-1.15.3.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\boot\spring-boot-starter\2.7.2\spring-boot-starter-2.7.2.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\boot\spring-boot\2.7.2\spring-boot-2.7.2.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\boot\spring-boot-starter-logging\2.7.2\spring-boot-starter-logging-2.7.2.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\ch\qos\logback\logback-classic\1.2.11\logback-classic-1.2.11.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\ch\qos\logback\logback-core\1.2.11\logback-core-1.2.11.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\apache\logging\log4j\log4j-to-slf4j\2.17.2\log4j-to-slf4j-2.17.2.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\apache\logging\log4j\log4j-api\2.17.2\log4j-api-2.17.2.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\slf4j\jul-to-slf4j\1.7.36\jul-to-slf4j-1.7.36.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\jakarta\annotation\jakarta.annotation-api\1.3.5\jakarta.annotation-api-1.3.5.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\yaml\snakeyaml\1.30\snakeyaml-1.30.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\spring-core\5.3.22\spring-core-5.3.22.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\spring-jcl\5.3.22\spring-jcl-5.3.22.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\javax\xml\bind\jaxb-api\2.3.0\jaxb-api-2.3.0.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\boot\spring-boot-starter-web\2.7.2\spring-boot-starter-web-2.7.2.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\boot\spring-boot-starter-json\2.7.2\spring-boot-starter-json-2.7.2.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\fasterxml\jackson\datatype\jackson-datatype-jdk8\2.13.3\jackson-datatype-jdk8-2.13.3.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\fasterxml\jackson\datatype\jackson-datatype-jsr310\2.13.3\jackson-datatype-jsr310-2.13.3.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\fasterxml\jackson\module\jackson-module-parameter-names\2.13.3\jackson-module-parameter-names-2.13.3.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\boot\spring-boot-starter-tomcat\2.7.2\spring-boot-starter-tomcat-2.7.2.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\apache\tomcat\embed\tomcat-embed-core\9.0.65\tomcat-embed-core-9.0.65.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\apache\tomcat\embed\tomcat-embed-el\9.0.65\tomcat-embed-el-9.0.65.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\apache\tomcat\embed\tomcat-embed-websocket\9.0.65\tomcat-embed-websocket-9.0.65.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\spring-web\5.3.22\spring-web-5.3.22.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\spring-webmvc\5.3.22\spring-webmvc-5.3.22.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\io\jsonwebtoken\jjwt\0.9.1\jjwt-0.9.1.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\fasterxml\jackson\core\jackson-databind\2.13.3\jackson-databind-2.13.3.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\fasterxml\jackson\core\jackson-annotations\2.13.3\jackson-annotations-2.13.3.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\fasterxml\jackson\core\jackson-core\2.13.3\jackson-core-2.13.3.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\mysql\mysql-connector-java\8.0.29\mysql-connector-java-8.0.29.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\alibaba\druid-spring-boot-starter\1.2.8\druid-spring-boot-starter-1.2.8.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\alibaba\druid\1.2.8\druid-1.2.8.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\javax\annotation\javax.annotation-api\1.3.2\javax.annotation-api-1.3.2.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\boot\spring-boot-autoconfigure\2.7.2\spring-boot-autoconfigure-2.7.2.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\baomidou\mybatis-plus-boot-starter\3.5.1\mybatis-plus-boot-starter-3.5.1.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\baomidou\mybatis-plus\3.5.1\mybatis-plus-3.5.1.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\baomidou\mybatis-plus-extension\3.5.1\mybatis-plus-extension-3.5.1.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\baomidou\mybatis-plus-core\3.5.1\mybatis-plus-core-3.5.1.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\baomidou\mybatis-plus-annotation\3.5.1\mybatis-plus-annotation-3.5.1.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\github\jsqlparser\jsqlparser\4.3\jsqlparser-4.3.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\mybatis\mybatis\3.5.9\mybatis-3.5.9.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\mybatis\mybatis-spring\2.0.6\mybatis-spring-2.0.6.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\boot\spring-boot-starter-jdbc\2.7.2\spring-boot-starter-jdbc-2.7.2.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\zaxxer\HikariCP\4.0.3\HikariCP-4.0.3.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\springframework\spring-jdbc\5.3.22\spring-jdbc-5.3.22.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\com\alibaba\fastjson2\fastjson2\2.0.28\fastjson2-2.0.28.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\apache\commons\commons-pool2\2.11.1\commons-pool2-2.11.1.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\apache\commons\commons-lang3\3.12.0\commons-lang3-3.12.0.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\projectlombok\lombok\1.18.24\lombok-1.18.24.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\cn\hutool\hutool-all\5.8.18\hutool-all-5.8.18.jar;E:\conf_a\apache-maven-3.9.6\mvn_repo\org\jetbrains\annotations\26.0.1\annotations-26.0.1.jar;C:\Users\zhlx\.config\.cool-request\request\lib\spring-invoke-starter.jar 
com.example.demo.DemoApplication
  1. Java执行命令:

    • E:\java_1\java\bin\java.exe: 这是Java虚拟机的可执行文件路径,用于启动Java应用程序。
  2. JVM启动参数:

    • -XX:TieredStopAtLevel=1: 这是一个JVM的非标准参数,用于调试和性能分析,它限制了JIT编译器的优化级别。

    • -Dspring.output.ansi.enabled=always: 这是设置Spring框架在控制台输出时使用ANSI颜色代码的参数。

    • -Dcom.sun.management.jmxremote: 启用JMX远程管理。

    • -Dspring.jmx.enabled=true: 启用Spring的JMX支持。

    • -Dspring.liveBeansView.mbeanDomain: 设置Spring Live Beans视图的MBean域。

    • -Dspring.application.admin.enabled=true: 启用Spring应用的管理功能。

    • "-Dmanagement.endpoints.jmx.exposure.include=*": 暴露所有的JMX管理端点。

    • -Dhxl.spring.invoke.port=33333: 设置一个自定义的系统属性,可能是用于应用程序的配置。

    • "-javaagent:D:\home.idea\IntelliJ IDEA 2023.3.2\lib\idea_rt.jar=61821:D:\home.idea\IntelliJ IDEA 2023.3.2\bin": 指定一个Java代理,这里用于IntelliJ IDEA的远程调试功能。 -以下是一些用于调整Java堆大小的JVM参数:

      • -Xms:设置堆的初始大小。
      • -Xmx:设置堆的最大大小。
      • -Xmn:设置新生代的大小(剩余部分自动分配给老年代)。
      • -XX:SurvivorRatio:设置Eden区与Survivor区的比例。
      • -XX:MaxMetaspaceSize:设置元空间的最大大小(Java 8及以后)。
  3. Java系统属性:

    • -Dfile.encoding=UTF-8: 设置文件编码为UTF-8。
  4. 类路径(Classpath) :

    • -classpath E:\java_1\javaproj\A_TestMoudle\arthas_sf\target\classes;...;C:\Users\zhlx.config.cool-request\request\lib\spring-invoke-starter.jar: 这是类路径,指定了JVM在查找和加载类时搜索的路径和JAR文件。
  5. 主类(Main Class) :

    • com.example.demo.DemoApplication: 这是应用程序的主类,包含main方法的入口点,用于启动应用程序。

1. 类加载器和内存空间(运行时数据区)

1.1 类加载器 java.lang.ClassLoader

..1.1.1 作为解释的环节

  1. 引导类加载器(Bootstrap ClassLoader)

    • 引导类加载器是用原生代码(如C/C++)实现的,它负责加载Java的核心库,这些库位于<JAVA_HOME>/jre/lib目录(比如rt.jar文件)或者被-Xbootclasspath参数指定的路径中。
    • 引导类加载器是JVM实现的一部分,它没有对应的Java类,因此无法在Java程序中直接引用。
    • 它用来加载那些在JVM启动时就已经确定要加载的类,例如java.lang包下的类。
    • 引导类加载器通常使用C++编写,并且是特定于平台的。
  2. 扩展类加载器(Extension ClassLoader)

    • 扩展类加载器是sun.misc.Launcher$ExtClassLoader类的实例,它负责加载<JAVA_HOME>/lib/ext目录或者由系统属性java.ext.dirs指定的路径中的类库。
    • 它是引导类加载器的子类加载器,并且是Java代码实现的。
    • 扩展类加载器主要用于加载JVM扩展的类库。
  3. 系统类加载器(System ClassLoader)

    • 系统类加载器(也称为应用类加载器)是sun.misc.Launcher$AppClassLoader类的实例,它是扩展类加载器的子类加载器。
    • 它负责加载当前应用的类路径(Classpath)中的类库,这通常是通过java.class.path系统属性来指定的。
    • 系统类加载器是程序中默认的类加载器,也是Java应用的类加载器。

1.2 线程共享--方法区

JVM加载完成 后的1.类型信息,2.常量,3.静态变量 , 方法区也可以被垃圾回收,但条件非常严苛,必须在该类没有任何引用的情况下

1.2.1类型信息包含了类的所有结构信息,主要包括以下内容:

  1. 类的基本信息

    • 类的全限定名(Fully Qualified Name)
    • 父类的全限定名
    • 类访问修饰符(public, abstract, final等)
    • 接口信息(实现的接口列表)
    • 类型标志(类或接口)
  2. 字段信息

    • 字段名称
    • 字段类型
    • 字段访问修饰符(public, private, protected, static, final, volatile, transient等)
  3. 方法信息

    • 方法名称
    • 方法的返回类型
    • 方法参数类型列表
    • 方法的访问修饰符
    • 方法的字节码(Code属性)
    • 异常处理(Exception属性)
  4. 构造器信息

    • 构造器名称
    • 构造器参数类型列表
    • 构造器访问修饰符
  5. 属性信息

    • Code属性:包含方法的字节码、局部变量表、操作数栈大小、异常表等信息
    • ConstantValue属性:用于静态变量赋值
    • Exceptions属性:方法抛出的异常
    • InnerClasses属性:内部类信息
    • Synthetic属性:标识字段或方法是编译器生成的
    • Signature属性:用于泛型类型的签名信息
    • Deprecated属性:标记类、方法或字段已过时

1.2.2常量池(Constant Pool)

1.2.2.1 Class文件内容里的常量池
  • 定义:每个Java类文件都包含一个常量池,这是一个表结构,用于存储类和接口的常量信息。
  • 内容:它包含了字面量(如字符串字面量、final常量值)和对类型、字段和方法的符号引用。这些符号引用在类加载过程中会被解析为直接引用。
  • 作用:常量池是Class文件的一部分,它为Java虚拟机提供了运行时所需的符号和常量信息。
1.2.2.2. 运行时常量池(Runtime Constant Pool)
  • 定义:当类被Java虚拟机加载后,它的常量池信息会被存储到方法区中的运行时常量池。
  • 内容:运行时常量池包含了类文件常量池中的所有内容,但是它可能会在运行期间动态添加新的常量,比如String.intern()方法可能会将字符串添加到运行时常量池中。
  • 作用:运行时常量池是方法区的一部分,它在类和接口的运行时解析符号引用时使用。
1.2.2.3. 各个包装类型里实现的常量池,例如String类里面的字符串常量池(String Pool)
  • 定义:Java中的某些包装类(如Integer、Boolean)和String类实现了自己的常量池,用于缓存常用的对象实例。

  • 内容

    • Integer常量池:缓存了-128到127之间的整数对象。
    • Boolean常量池:缓存了TRUE和FALSE两个对象。
    • String常量池:缓存了所有使用双引号创建的字符串字面量,以及通过String.intern()方法显式添加的字符串。
  • 作用:这些包装类型的常量池有助于节省内存,因为相同的对象实例可以被多个引用共享。例如,使用==比较两个字符串字面量时,如果它们都在字符串常量池中,则比较结果为true。

1.2.3静态变量(也称为类变量)存储在方法区中,主要包括以下内容:

  1. 静态字段

    • 由static关键字修饰的字段
    • 在类加载时分配空间,类的所有实例共享这些变量
    • 初始值在类加载的准备阶段分配,如果是final修饰的静态字段,则会在此时赋予ConstantValue属性指定的值

1.3 线程共享--堆

.. 1.3 堆区负责存放对象实例,几乎所有通过new关键字创建的对象都会存储在堆上 Java堆是垃圾回收器(Garbage Collector, GC)的主要工作区域,GC负责回收不再被引用的对象所占用的内存 堆的存取方式为管道类型,先进先出,在程序运行中, 可以动态的分配堆的内存大小。

1.4 线程私有--虚拟机栈(JVM栈,VM Stack)

new方法在堆上创建对象,虚拟机栈中保持对对象的引用,由GC处理. 基本数据类型在超过作用域后,会自动释放掉

1.4.1 栈帧-局部变量表

入口 : 1. 方法被调用时,调用者传递的参数值会被依次存储在局部变量表中 2. 方法体中声明的局部变量会在局部变量表中分配相应的变量槽

public int add(int a, int b) {
    int result; // 局部变量,存储在局部变量区
    result = a + b; // 将参数a和b的值相加,并将结果赋值给局部变量result
    return result; // 返回局部变量result的值
}

在这个例子中,局部变量区的情况如下:

  1. 参数

    • int a:这是方法的第一个参数,它将被存储在局部变量区的第一个槽位(Slot 0)。
    • int b:这是方法的第二个参数,它将被存储在局部变量区的第二个槽位(Slot 1)。
  2. 局部变量

    • int result:这是方法内部定义的局部变量,用于存储计算结果,它将被存储在局部变量区的第三个槽位(Slot 2)。 局部变量表的生命周期与栈帧相同,当方法调用结束时,对应的局部变量表也会随之销毁 存放reference引用和基本数据类型

局部变量区的大小在编译时就已经确定,并且方法执行期间不会改变 结构:

  • 变量槽(Slot) :局部变量表以变量槽为单位,每个变量槽可以存放一个32位的数据值。对于64位的数据类型(如long和double),需要占用两个连续的变量槽。
  • 索引:局部变量表中每个变量槽都有固定的索引编号,从0开始递增。

1.4.2 操作数栈区

1.4.3 运行环境区

1.4.4 其他

OOM SOF

1.5 线程私有-- 本地方法栈

native修饰的方法

1.6 线程私有-- 程序记数器

字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。 JVM的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,为了各条线程之间的切换后计数器能恢复到正确的执行位置,所以每条线程都会有一个独立的程序计数器。唯一无任何OOM的区域 当线程执行Java方法时,程序计数器记录的是正在执行的虚拟机字节码指令的地址。由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)只会执行一个线程中的指令。因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器,各条线程之间的计数器互不影响,独立存储。

假设有一个简单的Java方法:

public static void main(String[] args) {
    int a = 10;
    int b = 20;
    int sum = a + b;
    System.out.println(sum);
}

当线程执行这个方法时,程序计数器会按顺序指向以下字节码指令的地址:

image.png

0 bipush 10 // 将int值10加载到操作数栈 
2 istore_1 // 将操作数栈顶的int值(10)存储到局部变量Slot 1(变量a) 
3 bipush 20 // 将int值20加载到操作数栈
5 istore_2 // 将操作数栈顶的int值(20)存储到局部变量Slot 2(变量b)
6 iload_1 // 将局部变量Slot 1的值(变量a)加载到操作数栈 
7 iload_2 // 将局部变量Slot 2的值(变量b)加载到操作数栈
8 iadd // 将操作数栈顶的两个int值相加,并将结果压回操作数栈 
9 istore_3 // 将操作数栈顶的值(a + b的结果)存储到局部变量Slot 3(变量sum)
10 getstatic #2 // 获取System.out静态字段,并将其引用压入操作数栈 // #2 是指向java/lang/System.out的符号引用的常量池索引
13 iload_3 // 将局部变量Slot 3的值(变量sum)加载到操作数栈
14 invokevirtual #3 // 调用PrintStream.println(int)方法 // #3 是指向java/io/PrintStream.println方法的符号引用的常量池索引
17 return // 方法结束,返回void