1-JVM基础

JVM基础

java源码文件,通过javac 转换成class文件。

找到.java文件

词法分析器

tokens流

语法分析器

语义分析器

字节码生成器

转成.class文件

  • 装载

    1. 根据全限定路径名寻找class文件,转换成二进制流。通过ClassLoder.load(String name)(类装载器,name:全限定路径名)不同路径下的类,设置不同路径的类装载器。

      1. Bootstrap ClassLoader(根装载器由C语言编写):加载 $JAVA_HOME 中的 jar/lib/rt.jar 里所有的class或 Xbootclassoath 选项指定的jar包

      2. Extension ClassLoader:加载Java平台中扩展功能的一些jar包,包括 $JAVA_HOMEjar/lib/*.jar-Djava.ext.dirs 指定目录下的 jar

      3. App ClassLoader:加载classpath中指定的jar包及 Djava.class.path 所指定目录下的类和 jar

      4. Custom ClassLoader(自定义装载器改变装载原则,如Tomcat打破双亲委派机制):通过 java.lang.ClassLoader 的子类自定义加载class,属于应用程序根据自身需要自定义的 ClassLoader,如 TomcatJBoss 都会根据j2ee规范自行实现 ClassLoader

    2. 需要把类文件静态存储结构里面对应的内容存储到JVM里面(方法区的运行时数据结构)

      代码里一段方法不宜过长(阿里规范中提到一段方法不应超过80行)。方法存到JVM里的栈帧。内容一旦超过一定行数或者过长时,JVM就会从编译性改变为解释性,从而导致性能下降。

    3. java.lang.Class对象存储到堆内存

  • 链接

    1. 验证

      保证被加载的正确性。验证class文件中的cafe babe。注:class文件中,已cafe babe为开头的是java文件。

    2. 准备

      为类的静态变量分配内存。如:private static int a = 3;//为a分配内存,并且将a初始化默认值,默认值:a=0

    3. 解析

      将类中的符号引用转换为直接引用。静态的转换。

      符号引用:就是class文件中的内容,符合JVM规范的内容,JVM认识的语言。

      直接引用:在Java进程中能够代表真实含义的。JVM更底层,计算机能够认识的语言,并且操作的。比方说为某个静态变量分配了真实的内存。

  • 初始化

    对类的静态变量,进行正真的初始化。就是将上一步中的准备过程中的private static int a = 3;//为a分配内存,并且将a初始化默认值,默认值:a=0。将3正真的赋值 a = 3。

JVM运行时划分的区域:

程序最小单位是进程(数据不安全进程内的数据会被该进程内所有的线程共享),进程包含多个线程。

线程是进程最小的执行单位。

栈:只要符合栈结构,都遵循先进后出的原则

  • 方法区 Method Area(进程)、非堆:类信息【类的创建时间、作者、元数据(数据描述信息)】、常量【final】、静态变量【static】、即时编译器编译后的代码。里面存储的是一些。类类型加载的东西(也就是反射中的.class之后的Class),用于存储已经被虚拟机加载的类的信息、常量、静态变量等。与堆一样,是被线程共享的内存区域,要注意线程安全问题。方法区逻辑上属于堆的一部分。如果存储数据大小超过了比方说1个G就会报OutOfMemoryError(OOM)异常。

  • 堆 Heap(进程):代表某个类的java.lang.Class对象。存储对象、String、数组。如果存储数据大小超过了比方说1个G就会报OutOfMemoryError(OOM)异常。

  • Java虚拟机栈(线程):一个线程当中会有一个私有Java虚拟机栈。生命周期是与线程绑定在一起。存储Java方法。

    在Java方法中如何去调用C语言的方法,通过动态链接去调用完成。

  • 本地方法栈(线程):存储C语言方法。

  • 栈帧(Java虚拟机栈、本地方法栈)Frame:代表是方法的执行。一个栈帧被创建就表示一个方法被执行,方法压栈先进后出。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    //伪代码1
    a(){
    b();
    }
    b(){
    c();
    }
    c(){

    }
    //先进行压栈
    //先往Java虚拟机中压栈存入a(),之后是b(),在之后是c()。
    //之后是出栈
    //在c()方法执行完之后先出,b()执行完再出,最后是a()执行完再出。
    //先进后出的概念。

    //伪代码2
    a(){
    a();
    }
    //如果递归一直调用自己,超出栈的长度或深度,就会报StackOverflowError(栈溢出)。同理如果一个方法内部的调用链非常长或深的话也一样会报StackOverflowError(栈溢出)。
    1. 局部变量表:存储方法中的局部变量

    2. 操作数栈:栈结构,用来描述运算过程当中数据暂时的存储位置

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      //伪代码.java文件
      public static int calc(int op1,int op2){
      op1 = 3;
      int result = op1 + op2;
      return result;
      }
      //.java文件的.class文件翻译的JVM字节码。
      public static int calc(int op1,int op2);
      Code:
      0:iconst_3 // 将3放到操作数栈中
      //局部变量下标:如果是类级别方法【static修饰的方法】下标是从0开始;如果是实例方法下标从1开始,0标识的是这个实例,保留给当前对象的引用this。
      1:istore_0 //将操作数占中的3赋值给局部变量表中的局部变量
      2:iload_0 //将局部变量0值放到操作数栈中
      3:iload_1 //
      4:iadd
      5:istore_2
      6:iload_2
      7:ireturn
    3. 动态链接:符号引用转换为直接引用**。动态的转换。比方说:一个方法调用某个接口或抽象类,这时是不知道这个方法内部具体实现的,只有等到程序运行时才能知道具体是调用了哪个子类或实现类的方法。

    4. 正常或异常方法的返回:

  • 程序计数器(线程):是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。在虚拟机的概念模型里(仅是概念模型,各种虚拟机可能会通过一些更高效的方式去实现),字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。

JAVA 对象内存布局

对象头:

Mark Word:一系列的标记为(哈希码,分代年龄,锁状态标志等)64位系统:8字节

Class Pointer:指向对象对应的类元数据的内存地址 64位系统:8字节

Length数组对象特有:数组长度 4字节

实例数据:

包含了对象的所有成员变量,大小由各个变量类型决定

boolean和byte:1字节

short和char:2字节

int和float:4字节

long和double:8字节

reference:8字节(64位系统)

对齐填充:

为了保证对象的大小为8字节的整数倍,会进行填充字节


1-JVM基础
https://happyloves.cn/20200425/cb6006ce99db.html
作者
赵小胖
发布于
2020年4月25日
许可协议