JVM之二:深入浅出jvm

文章目录

    • 1、jvm模型
    • 2、类加载器
      • 2.1 加载
      • 2.2 验证
      • 2.3 准备
      • 2.4 解析
      • 2.5 初始化
    • 3、双亲委派机制
    • 4、native方法
    • 5、PC寄存器
    • 6、方法区
    • 7、栈:数据结构(先进后出,后进先出)
    • 8、堆
    • 9、JMM

1、jvm模型

JVM之二:深入浅出jvm_第1张图片
JVM之二:深入浅出jvm_第2张图片

2、类加载器

作用: 加载Class文件。

JVM之二:深入浅出jvm_第3张图片

  • 虚拟机自带的加载器
  • 启动类(根)加载器
  • 扩展类加载器
  • 应用程序加载器

类加载机制:

JVM之二:深入浅出jvm_第4张图片

其中类加载的过程包括了加载、验证、准备、解析、初始化五个阶段。在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始。另外注意这里的几个阶段是按顺序开始,而不是按顺序进行完成,因为这些阶段通常都是互相交叉地混合进行的,通常在一个阶段执行的过程中调用或激活另一个阶段。

2.1 加载

类加载主要通过类加载机制完成三件事:

  • ①通过类的全限定名来获取定义的二进制字节流
  • ②将这个字节流所代表的的静态存储结构转化为方法区的运行时数据结构
  • ③在堆中生成一个代表这个类的Class对象,作为方法区中这些数据的访问入口

2.2 验证

针对.class文件检测验证,确保该文件不会危害虚拟机。但是如果能够确保文件是安全的,可以关闭耗时的验证过程。

主要包括以下四个阶段:

  • ①文件格式验证:确保.class文件符合class文件规范,并且能够被当前的虚拟机处理,主要对魔数,主版本号以及常量池等。
  • ②元数据验证:对字节码描述的信息进行语义上的分析,保证描述内容符合java语言的规范要求。比如说验证这个类是不是有父类,类中的字段方法是不是和父类冲突等等。
  • ③字节码验证:主要是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。在元数据验证阶段对数据类型做出验证后,这个阶段主要对类的方法做出分析,保证类的方法在运行时不会做出危害虚拟机安全的事。
  • 符号引用验证:它是验证的最后一个阶段,发生在虚拟机将符号引用转化为直接引用的时候。主要是对类自身以外的信息进行校验。目的是确保解析动作能够完成。

2.3 准备

准备阶段主要为类变量分配内存并设置初始值。这些内存都在方法区分配。在这个阶段我们只需要注意两点就好了,也就是类变量和初始值两个关键词:

  • ① 类变量(static)会分配内存,但是实例变量不会,实例变量主要随着对象的实例化一块分配到java堆中,

  • ② 这里的初始值指的是数据类型默认值,而不是代码中被显示赋予的值。比如

  • //在这里准备阶段过后的value值为0,而不是1。赋值为1的动作在初始化阶段。
    public static int value = 1;
    //在这里准备阶段的value值为1.有final修饰,已经放到常量池中了。
    public static final int value = 1;
    

2.4 解析

解析阶段主要是将常量池的符号引用转变为直接引用。

  • 符号引用:以一组符号来描述所引用的目标,可以是任何形式的字面量,只要是能无歧义的定位到目标就好,就好比在班级中,老师可以用张三来代表你,也可以用你的学号来代表你,但无论任何方式这些都只是一个代号(符号),这个代号指向你(符号引用)
  • 直接引用:直接引用是可以指向目标的指针、相对偏移量或者是一个能直接或间接定位到目标的句柄。和虚拟机实现的内存有关,不同的虚拟机直接引用一般不同。

解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。

2.5 初始化

这是类加载机制的最后一步,在这个阶段,java程序代码才开始真正执行。我们知道,在准备阶段已经为类变量赋过一次值。在初始化阶端,程序员可以根据自己的需求来赋值了。一句话描述这个阶段就是执行类构造器< clinit >()方法的过程。

在初始化阶段,主要为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。在Java中对类变量进行初始值设定有两种方式:

①声明类变量时指定初始值

②使用静态代码块为类变量指定初始值

JVM初始化步骤

1、假如这个类还没有被加载和连接,则程序先加载并连接该类

2、假如该类的直接父类还没有被初始化,则先初始化其直接父类

3、假如类中有初始化语句,则系统依次执行这些初始化语句

类初始化时机:只有当对类的主动使用的时候才会导致类的初始化,类的主动使用包括以下六种:

  • 创建类的实例,也就是new的方式
  • 访问某个类或接口的静态变量,或者对该静态变量赋值
  • 调用类的静态方法
  • 反射(如 Class.forName(“com.shengsiyuan.Test”))
  • 初始化某个类的子类,则其父类也会被初始化
  • Java虚拟机启动时被标明为启动类的类( JavaTest),直接使用 java.exe命令来运行某个主类

3、双亲委派机制

作用:更加安全

  • 类加载器收到类加载的请求
  • 将这个情蛊向上委托给父类加载器去完成,一直向上委托,直到启动类加载器
  • 启动加载器检查是否能够加载当前这个类,能加载就结束,使用当前的加载器,否则抛出异常,通知子加载器进行加载。
  • 重复步骤3

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YQZRLTa5-1594192847632)(C:\Users\刀刀和阳\AppData\Roaming\Typora\typora-user-images\image-20200708145940842.png)]

4、native方法

  • native: 凡是带了native关键字,说明java的作用方位达不到了,会去调用底层C语言的库。
  • 会进入本地方法栈
  • 调用本地方法本地接口 JNI
  • JNI作用:扩展java的使用,融合不同的编程语言为java所用。
  • java诞生时,它在内存区域专门开辟了一块标记区域:native method stack。登记native方法,在最终执行的时候,加载本地方法库中的方法通过JNI

5、PC寄存器

程序计数器:Program Counter Register

每个线程都有一个程序计数器,是线程私有的,就是一个指针,指向方法区中的方法字节码(用来存储指向一条指令的地址,也即将要执行的指令代码),在执行引擎读取下一条指令,是一个非常小的内存空间,几乎可以忽略不计。

6、方法区

  • 方法区是被所有线程共享,所有字段和方法字节码,以及一些特殊方法,如构造函数,接口代码也在此定义,简单说,所有定义的方法的信息都保存在该区域,此区域属于共享区间
  • 静态变量、常量、类信息(构造方法,接口定义)、运行时的常量池存在方法区中,但是实例变量存在堆内存中,和方法区无关

7、栈:数据结构(先进后出,后进先出)

主管程序的运行, 生命周期和线程同步。线程结束,栈内存释放,对于栈而言,不存在垃圾回收问题

一旦线程结束,栈就结束。

栈:8大基本类型+对象引用+实例的方法

8、堆

Heap,一个JVM只有一个堆内存,堆内存大小是可以调节的。

类加载器读取了类文件后,一般会把什么东西放到堆中?类,方法,常量池,实例对象。

存储区域 存储内容 优点 缺点 回收
基本类型的变量和对象的引用变量 存取速度比堆要快,仅次于寄存器,栈数据可以共享 存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。栈中主要存放一些基本类型的变量 当超过变量的作用域后,Java会自动释放掉该变量,内存空间可以立即被另作他用
由new等指令创建的对象和数组 可以动态地分配内存大小,生存期也不必事先告诉编译器 由于要在运行时动态分配内存,存取速度较慢 由Java虚拟机的自动垃圾回收器来回收不再使用的数据

9、JMM

JVM之二:深入浅出jvm_第5张图片

Java内存模型的主要目标是定义程序中变量的访问规则。

这里的变量是指实例字段,静态字段,构成数组对象的元素,但是不包括局部变量和方法参数(因为这是线程私有的)。这里可以简单的认为主内存是java虚拟机内存区域中的堆,局部变量和方法参数是在虚拟机栈中定义的。但是在堆中的变量如果在多线程中都使用,就涉及到了堆和不同虚拟机栈中变量的值的一致性问题了。

你可能感兴趣的:(Java,JVM,java,JVM)