• JVM第一话 -- JVM入门详解以及运行时数据区分析


    1.JVM是什么

    JVM(Java Virtual Machine) 是Jvm虚拟机的一种规范。

    C:\Users\pc>java -version
    java version "1.8.0_151"
    Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
    Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)
    
    • 1
    • 2
    • 3
    • 4

    当安装了JDK以后,可以看到默认是已安装HotSpot虚拟机的,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,所以Java语言是跨平台语言。

    这也就提现出了Java语言的特点: 一处编译,多处执行,跨平台,安全性(1.摒弃指针 2、GC垃圾回收机制:(自动释放回收长时间不使用对象的内存))

    结构:JDK & JRE & JVM三者结构图
    结构关系图

    • jvm分布结构图
      输入class文件到虚拟机,虚拟机输出不同平台的CPU指令。存储器、控制器、运算器 为JVM运行时数据区
      在这里插入图片描述

    2.一个java文件的编译运行过程

    工具:javac编译、javap反编译

    Java源文件 -> Javac编译成class文件 -> JVM虚拟机运行Class文件实则为转成对应操作系统的机器码 -> 各大操作系统

    2.1 javac

    Javac将源文件转成字节码(词法分析,语法分析,语义分析,字节码生成码)
    class文件 一个存放二进制文件,存放16进制的字节

    2.2 javap

    将class文件反编译为cpu指令

    //原始代码
    static int num = 1;
    static final int a1 = 2;
    int a2 = 2; 
       
    //通过 javap -p -v xxx.class 反编译出来的结果
    //会在堆内存中开辟一个num=0的空间
    static int num;	
     	descriptor: I
     //权限
      flags: ACC_STATIC
    static final int a1;
     	descriptor: I
      flags: ACC_STATIC, ACC_FINAL
      //通知虚拟机给静态变量赋值 意味着准备阶段就已经初始化值了 
      ConstantValue: int 2
      //非static变量的实例变量会随着类变量分配到java堆 在初始化阶段由类构造器初始
    int a2;
     	descriptor: I
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3.类加载机制

    目的:将class类加载进虚拟机内存空间中

    3.1 装载Loading

    找到class文件所在的全路径,然后装载到内存中。具体实现由类加载器实现ClassLoader

    类加载器类型总览

    类加载器功能
    Bootstrap ClassLoader 启动类加载器加载$JAVA_HOME中jre/lib/rt.jar里面所有的class或者Xbootclasspath选项指定的jar包
    ExClassLoader 扩展类加载器加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或者-Djava.ext-dirs指定目录下的包
    AppClassLoader 系统类加载器加载classpath中指定的jar包及Djava.class.path指定目录下的类和jar包
    Custom ClassLoader自定义类加载器通过ClassLoader的子类自定义加载class,属于应用程序根据自身需要自定义的ClassLoader,如Tomcat,Jboss都会根据规范自行实现ClassLoader

    类加载方式

    • 全盘负责
      • 只要由一个加载器负责某个类,那么这个类的父类以及子类通通由这个类加载器负责
    • 父类委托
      • 判断顶层类是否已经加载
    • 缓存机制
      • 同名的类只会被加载一次 所有必须使用到缓存

    自定义类加载器 java实现

    实现ClassLoader,加载类不能放到类路径下(否则会被AppClassLoader加载)

    3.2 链接Linking

    验证Verify:验证类的正确性
    准备Prepare:为类或接口的静态字段赋值默认值

    private static int a =10; 此时会先赋值默认值0

    解析Resolve:动态将运行常量池中的符号引用转为直接引用(物理内存地址)

    3.3 初始化Initalization

    为变量附上真正的值,例如给上面的static int a赋值为10

    什么情况触发初始化

    • new
    • 访问某个类或接口的静态变量
    • 调用类的静态方法
    • 反射
    • 初始化某个类的子类
    • 虚拟机指定启动类

    4.JVM运行时数据区

    根据前面的jvm分布结构图,分为了5个区域,栈、堆、程序计数器、方法区、本地方法栈

    41. 方法区 Method Area

    • java线程共享的区域,生命周期和进程绑定
    • 存储类的元数据信息,模板信息
    • 内存不足时报OutOfMeoryError: Metespace
    • 方法区存放不会轻易改变的内容,在jdk8中又被称为元空间Metaspace
    • 运行时常量池 Run-Time Constant pool
      • String str = “hello” hello在常量池,可以被多引用
      • String str = new String(“hello”) 1.7以前hello在堆内存,1.8后String对象存放堆内存中,hello存放字符串常量池中

    4.2 堆Heap

    • 虚拟机最大的一块线程共享区域,生命周期和进程绑定
    • 内存不足时报OutOfMeoryError: Java heap space,通过-Xms20M -Xmx20M设置堆内存大小
    • 类的或者成员变量的实例对象,例如new String(“hello”) String对象在堆内存
    • JDK1.8已经将String常量池从方法区移入堆中了,那么new String(“hello”) 会生成两个对象,String对象存放堆内存中,hello存放字符串常量池中,如果常量池存在则不创建

    4.3 栈 Stacks

    • 线程私有,通过抢占CPU时间片来执行栈帧
    • 栈帧是最小的工作单位,方式是压栈、出栈,栈结构先进后出、后进先出
    • 递归导致栈深度不够用会报StackOverfolwError,栈帧深度默认大小1M,可以通过-Xss 128k修改
    • 栈帧压栈出栈 thread mian() -> a() ->b() -> c(),mian()一定是最后执行完的,一定是先进后出的
      在这里插入图片描述

    4.4 栈帧的组成结构

    public static int calc(int num1,num2){
        num1 = 2;
        int res = num1 + num2;
        return res;
    }
    calc(1,8);
    //通过javap反编译内容 查看下calc栈帧
    public static int calc(int, int);
        descriptor: (II)I
        flags: ACC_PUBLIC, ACC_STATIC
        Code:
    	 //操作数栈2个 局部变量3个 参数值2个
          stack=2, locals=3, args_size=2
             0: iconst_2	//push一个int值压入操作栈中 2
             1: istore_0	//将操作数栈的值出栈赋值给局部变量的第0个 num1=2
             2: iload_0		//第0个局部变量的值压入操作数栈 2
             3: iload_1		//第1个局部变量的值压入操作数栈 8
             4: iadd		//执行一个相加的操作 10
             5: istore_2	//将操作数栈的值出栈赋值给局部变量的第2个 res = 10
             6: iload_2		//第二个局部变量的值压操作数栈 res
             7: ireturn		//第二个局部变量出操作数栈 返回10 然后通过方法返回地址到main栈帧
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 局部变量表,存储局部变量的
    • 操作数栈,用来保存运算的临时栈
    • 动态链接,将符号引用转为直接引用
    • 方法的返回,继续执行后面的方法

    java中的一行代码,可能是多个汇编指令

    4.5 程序计算器PC Register

    • 和操作系统中的寄存器一样,当线程执行过程中被其他线程抢占时间片先去执行了,需要记录正在执行的方法和位置
    • 线程私有

    4.6 本地方法栈Native Method Stacks

    • 线程执行某个方法过程中需要调用本地Native方法,这类方法是没有JVM没有维护的,所以每个线程都有一个本地方法栈,调用到的地方通过链接过去

    5.创建一个对象在内存中都做了什么?

    1. 先将指定的class文件加载进内存
    2. 执行main方法时,在栈内存中开辟了属于main线程的栈空间,分配给变量p
    3. new关键字向堆内存申请一个实例对象空间,分配一个内存地址值
    4. 在实例对象空间进行属性的空间分配,并进行了默认初始化
    5. 对空间中的属性动态将符号引用转为直接引用
    6. 对实例的构造代码块初始化,调用该实例类的构造方法,进行构造方法初始化
    7. 将首地址赋值给p,p变量就引用了该实例,指向了该对象

    以上就是本章的全部内容了。

    上一篇:MongoDB第二话 – MongoDB高可用集群实现
    下一篇:JVM第二话 – JVM内存模型以及垃圾回收

    人寿几何?逝如朝霜。时无重至,华不再阳

  • 相关阅读:
    4种方法!怎么把电脑上的音频传到苹果手机上?
    二十二、SpringBoot + Jwt + Vue 权限管理系统 (3)
    关于chatGPT对有关Docker Desktop问题的一个回答
    循环神经网络——上篇【深度学习】【PyTorch】【d2l】
    德人合科技——@天锐绿盾 | -文档透明加密系统
    28个团队建设游戏
    并行多核体系结构基础 Yan Solihin 第4章 针对链式数据结构的并行 摘录
    点云采样方法
    Linux - Linux下Java安装路径查找;配置Java环境变量
    金仓数据库KingbaseES客户端编程接口指南-Gokb (4. 执行SQL语句 )
  • 原文地址:https://blog.csdn.net/qq_35551875/article/details/125516634