当前位置: 首页 > news >正文

读JVM(深入理解Java虚拟机)笔记(一)

Java代码的编译和执行的整个过程大概是:

  1. 编译:

1.1. 开发人员编写Java代码(.java文件)

1.2 [javac xxTest.java]->[xxTest.class][maven compile]然后将之编译成字节码(.class文件)

  1. 运行:

2.1. ‘[java xxTest]’(启动一个jvm进程)

2.2. 再然后字节码被java虚拟机(JVM)装入内存

-- (深入理解计算机操作系统-内存里的是什么东西,数字电路基础-内存为什么可以存东西,单片机原理基础-内存怎么存东西)

--(虚拟机把描述类的数据从class文件加载到内存并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制)

2.3. 一旦字节码进入虚拟机,它就会被解释器解释执行,或者是被即时代码发生器有选择的转换成机器码执行

一、字节码(.class)(jvm数据结构)(一字节等于八位)

.java --> javac  --> .class ---> java虚拟机

.groovy --> groovyc --> .class ---> java虚拟机

.rb --> jrubyc --> .class ---> java虚拟机
复制代码

--class文件对应唯一一个类或接口的定义信息,类或接口不一定都定义在文件里(类加载器?(是什么,怎么做)直接生成)

字节码数据类型:

  1. 无符号数--u1,u2,u4,u8,(描述数字,索引引用,数量值,字符串值)
  2. 表--由多个无符号数或者其他表作为数据项构成的复合数据类型'_info';(eg:class文件)

字节码格式:

  1. 魔数与class版本号

  2. 常量池(每一项常量都是一个表,每一项的常量的都有各自的结构,14种常量项)

    1.字面量--文本字符串,final常量值

    2.符号引用--类和接口的全限定名,字段的名称和描述符,方法的名称和描述符

  3. 访问标志 --识别类或接口层次的访问信息(类/接口,is_public,is_abstract,is_final_class)

  4. 类索引(u2)、父类索引(u2)、接口索引集合(List[u2])
    --类单继承多实现,接口多继承?底层原理是结构规范的问题??

    --class文件中由这三项数据确定这个类的继承关系

  5. 字段表集合 --描述接口或者类中声明的变量

  6. 方法表集合(含有“init”方法默认的构造方法,“clinit”方法是类的构造器) --方法里的代码经过编译器编译成字节码指令后,存放在方法属性表集合的"Code"属性(局部变量表)

  7. 属性表集合 --class文件,字段表,方法表都可以有自己的属性表集合,用于描述某些场景专有的信息

字节码指令:...

二、虚拟机类加载机制(生命周期:加载,(验证,准备,解析)--连接,初始化,使用,卸载)

--2.1 (虚拟机把描述类的数据从class文件加载到内存并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制)

--2.2 非数组类的加载阶段是获取类的二进制字节流的动作,加载阶段既可以使用系统提供的引导类加载器来完成,也可以由用户自定义的类加载器完成(重写类加载器loadClass方法[不提倡]或者重写findClass()方法)

--2.3 数组类的加载阶段是数组类本身不通过类加载器创建,由虚拟机直接创建,但数组类的元素类型是由类加载器创建的

--2.4 (class文件的信息进入虚拟机后会怎么处理--类加载过程)

--2.5 (虚拟机如何加载class文件--类加载器) (ps:当虚拟机启动时,类加载器去读取main()方法初始化主类,然后主类对其他类的主动引用或被动引用,读取这些的全限定名,根据类加载器的执行顺序不同去获取这些类 的二进制字节流,然后字节码文件就被加载在内存的方法区中,验证,解析,初始化。。之后就是执行引擎的执行,把字节码转化为指令,从运行时常量池中查找main()方法的引用)

  1. 虚拟机的启动

    --虚拟机会创建bootstrap类加载器,bootstrap类加载器会创建扩展类加载器和应用程序加载器

    --应用程序加载器会首先寻找main()方法作为项目的入口,同时加载主函数的类

  2. 类加载的时机 --1.加载的时机由虚拟机的具体实现来自由把握(运行时)

    --2.初始化,有且只有五种情况必须立即对类进行初始化([加载、验证、准备]需要在此之前开始)(对一个类进行主动引用)

     --1.使用new关键字实例化对象、读取或设置这个类的静态字段的时候(被final修饰除外),以及调用这个类的静态方法的时候(new,getstatic,putstatic,invokestatic字节码指令),都会初始化这个类(通过子类引用父类的静态字段,不会导致子类的初始化)
     --2.使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化
     --3.当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化
     --4.当虚拟机启动时,用户需要指定一个要执行的主类(main()方法),虚拟机会先初始化这个主类
     (war包的主类在哪里,war包在哪里启动的?)
     --5.java.lang.invoke.MethodHandle方法句柄
    
     (ps:被动使用类--常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类(引用的是字节码描述符,字段描述符在常量池中,没有这个类的符号引用入口))
    复制代码
  3. 类加载的过程(加载、验证、准备、解析、初始化)

    --1. 加载阶段

     --1.通过一个类的全限定名来获取定义此类的二进制字节流(ZIP包中读取(jar,ear,war)、网络获取、运行时计算生成(动态代理)、jsp、数据库)
     --2.将这个字节流所代表的静态存储结构转化为方法去的运行时数据结构
     --3.在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
    复制代码

    --2. 验证(检验字节码的信息是否符合当前虚拟机的要求)

     --1.文件格式验证(验证字节流是否符合class文件格式规范)
     --2.元数据验证(字节码描述的信息进行语义分析)
     --3.字节码验证
     --4.符号引用验证
    复制代码

    --3. 准备(正式为类变量(static)分配内存并设置类变量初始值即数据类型的零值)(类变量即类的静态变量)

    --4. 解析(将字节码文件的常量池内的符号引用替换为直接引用的过程)

     --1.直接引用(直接指向目标的指针,相对偏移量,一个能间接定位到目标的句柄)
     --2.主要解析类或接口,字段,类方法,接口方法,方法类型,方法句柄,调用点限定符7类符号引用
     	--解析类或接口,在类D中把一个从未解析过的符号引用N解析为一个类或接口C的直接引用,虚拟机会把N的全限定名传递给D的类加载器去加载这个类C
    复制代码

    --5. 初始化(执行字节码即真正开始执行类中定义的Java代码,也可以说是执行类构造器“clinit”方法的过程)

  4. 类加载器("通过一个类的全限定名来获取定义此类的二进制字节流"这个动作放到虚拟机外部实现,让应用程序自己决定如何去获取所需要的类)

    --1.类与类加载器(两个类是否相等[(equals()、isAssignableFrom()、isInstance()、instanceof)],由在同一个虚拟机中同一个类加载器加载)

    --2.双亲委派模型(bootstrap类加载器、扩展类加载器、应用程序加载器)

三、虚拟机字节码执行引擎(解释执行,编译执行)

......

  • 接口的加载过程和类的加载有一些不同

--接口没有static语句块,但编译器会为接口生成"clinit()"类构造器,初始化接口的成员变量

--接口初始化时不要求父接口的初始化,在真正使用父接口的是才会初始化(引用接口中定义的常量)

重载 -- 特征签名

java编码层面 -- 方法名 + 参数类型 + 参数顺序

JVM层面 -- 方法名 + 参数类型 + 参数顺序 + 返回值类型 + (异常)

  • 描述符:jvm层次上的,针对class文件定义的

--字段数据类型描述符 String/对象 --> Ljava/lang/String ; long --> J ; void --> V ; int[] --> [I

--方法描述符 String getName(int) --> (I)Ljava/lang/String

  • 类构造器"clinit"和默认的构造方法"init"的生成过程和作用
  1. 类构造器,是初始化阶段要执行的方法,为类的静态变量赋初始值(定义的值),执行静态语句块,只会初始化一次

    --1.顺序是由语句在源文件中出现的顺序决定

    --2.在静态语句块里,可以访问定义在静态语句块之前的类变量,在静态语句块之后的类变量只可以赋值不能访问(非法向前引用)

    --3.虚拟机会保证父类的先执行,再执行子类,所以父类中定义的静态语句块要优先于子类的变量赋值操作

    --4.接口的不需要先执行父类,只有当父接口中定义的变量使用时,父接口才会初始化;接口的实现类在初始化时也不会执行"clinit"方法

  2. 默认的构造方法,完成类的实例化过程

  • 类加载器

1.Bootstrap ClassLoader启动类加载器(JAVA_HOME\lib、-Xbootclasspath参数所指定的路径)--虚拟机识别,请求委派使用

2.Extension ClassLoader扩展类加载器(JAVA_HOME\lib\ext、java.ext.dirs系统变量所指定的路径中的所有类库)--开发者可直接使用

3.Application ClassLoader应用程序类加载器(jdk源码中sun/misc/Launcher$AppClassLoader)--加载用户类路径classpath上的类库

转载于:https://juejin.im/post/5cc125545188252dd6530cca

相关文章:

  • Vue之坑
  • flask 第七章 简陋版智能玩具 +MongoDB初识和基本操作
  • 4-1 requests库的安装
  • 学起来:Flutter将支持桌面应用开发
  • 基于binlog方式搭建MySQL主从
  • C# 虹软SDK视频人脸识别和注册
  • redis-主从复制
  • 再见 异步回调, 再见 Async Await, 10 万 个 协程 的 时代 来 了
  • 007《loom》 Chrome翻录网页视频神器
  • 03python面向对象编程1
  • 前端知识杂锦(二)
  • 大传媒时代报业如何驾驭新媒体
  • ELK日志收集(一)-Elasticsearch安装
  • gulp+es6 配置
  • 因为看见,所以发现:QBotVariant谢绝落幕
  • 【Leetcode】101. 对称二叉树
  • 230. Kth Smallest Element in a BST
  • DOM的那些事
  • echarts的各种常用效果展示
  • emacs初体验
  • javascript数组去重/查找/插入/删除
  • JAVA并发编程--1.基础概念
  • JS正则表达式精简教程(JavaScript RegExp 对象)
  • KMP算法及优化
  • Linux CTF 逆向入门
  • Linux学习笔记6-使用fdisk进行磁盘管理
  • Lucene解析 - 基本概念
  • sessionStorage和localStorage
  • Unix命令
  • 基于axios的vue插件,让http请求更简单
  • 那些被忽略的 JavaScript 数组方法细节
  • 前端_面试
  • 浅谈Kotlin实战篇之自定义View图片圆角简单应用(一)
  • 如何编写一个可升级的智能合约
  • 《码出高效》学习笔记与书中错误记录
  • ​二进制运算符:(与运算)、|(或运算)、~(取反运算)、^(异或运算)、位移运算符​
  • # centos7下FFmpeg环境部署记录
  • #[Composer学习笔记]Part1:安装composer并通过composer创建一个项目
  • #Linux(权限管理)
  • #鸿蒙生态创新中心#揭幕仪式在深圳湾科技生态园举行
  • (01)ORB-SLAM2源码无死角解析-(66) BA优化(g2o)→闭环线程:Optimizer::GlobalBundleAdjustemnt→全局优化
  • (八)光盘的挂载与解挂、挂载CentOS镜像、rpm安装软件详细学习笔记
  • (八十八)VFL语言初步 - 实现布局
  • (附源码)springboot猪场管理系统 毕业设计 160901
  • (考研湖科大教书匠计算机网络)第一章概述-第五节1:计算机网络体系结构之分层思想和举例
  • (六)软件测试分工
  • (一)python发送HTTP 请求的两种方式(get和post )
  • (原创) cocos2dx使用Curl连接网络(客户端)
  • (转)visual stdio 书签功能介绍
  • .net redis定时_一场由fork引发的超时,让我们重新探讨了Redis的抖动问题
  • .NET 将混合了多个不同平台(Windows Mac Linux)的文件 目录的路径格式化成同一个平台下的路径
  • .NET中统一的存储过程调用方法(收藏)
  • /etc/fstab 只读无法修改的解决办法
  • @基于大模型的旅游路线推荐方案
  • [Android]一个简单使用Handler做Timer的例子