Java代码的编译和执行的整个过程大概是:
- 编译:
1.1. 开发人员编写Java代码(.java文件)
1.2 [javac xxTest.java]->[xxTest.class][maven compile]然后将之编译成字节码(.class文件)
- 运行:
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文件对应唯一一个类或接口的定义信息,类或接口不一定都定义在文件里(类加载器?(是什么,怎么做)直接生成)
字节码数据类型:
- 无符号数--u1,u2,u4,u8,(描述数字,索引引用,数量值,字符串值)
- 表--由多个无符号数或者其他表作为数据项构成的复合数据类型'_info';(eg:class文件)
字节码格式:
-
魔数与class版本号
-
常量池(每一项常量都是一个表,每一项的常量的都有各自的结构,14种常量项)
1.字面量--文本字符串,final常量值
2.符号引用--类和接口的全限定名,字段的名称和描述符,方法的名称和描述符
-
访问标志 --识别类或接口层次的访问信息(类/接口,is_public,is_abstract,is_final_class)
-
类索引(u2)、父类索引(u2)、接口索引集合(List[u2])
--类单继承多实现,接口多继承?底层原理是结构规范的问题??--class文件中由这三项数据确定这个类的继承关系
-
字段表集合 --描述接口或者类中声明的变量
-
方法表集合(含有“init”方法默认的构造方法,“clinit”方法是类的构造器) --方法里的代码经过编译器编译成字节码指令后,存放在方法属性表集合的"Code"属性(局部变量表)
-
属性表集合 --class文件,字段表,方法表都可以有自己的属性表集合,用于描述某些场景专有的信息
字节码指令:...
二、虚拟机类加载机制(生命周期:加载,(验证,准备,解析)--连接,初始化,使用,卸载)
--2.1 (虚拟机把描述类的数据从class文件加载到内存并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制)
--2.2 非数组类的加载阶段是获取类的二进制字节流的动作,加载阶段既可以使用系统提供的引导类加载器来完成,也可以由用户自定义的类加载器完成(重写类加载器loadClass方法[不提倡]或者重写findClass()方法)
--2.3 数组类的加载阶段是数组类本身不通过类加载器创建,由虚拟机直接创建,但数组类的元素类型是由类加载器创建的
--2.4 (class文件的信息进入虚拟机后会怎么处理--类加载过程)
--2.5 (虚拟机如何加载class文件--类加载器) (ps:当虚拟机启动时,类加载器去读取main()方法初始化主类,然后主类对其他类的主动引用或被动引用,读取这些的全限定名,根据类加载器的执行顺序不同去获取这些类 的二进制字节流,然后字节码文件就被加载在内存的方法区中,验证,解析,初始化。。之后就是执行引擎的执行,把字节码转化为指令,从运行时常量池中查找main()方法的引用)
-
虚拟机的启动
--虚拟机会创建bootstrap类加载器,bootstrap类加载器会创建扩展类加载器和应用程序加载器
--应用程序加载器会首先寻找main()方法作为项目的入口,同时加载主函数的类
-
类加载的时机 --1.加载的时机由虚拟机的具体实现来自由把握(运行时)
--2.初始化,有且只有五种情况必须立即对类进行初始化([加载、验证、准备]需要在此之前开始)(对一个类进行主动引用)
--1.使用new关键字实例化对象、读取或设置这个类的静态字段的时候(被final修饰除外),以及调用这个类的静态方法的时候(new,getstatic,putstatic,invokestatic字节码指令),都会初始化这个类(通过子类引用父类的静态字段,不会导致子类的初始化) --2.使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化 --3.当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化 --4.当虚拟机启动时,用户需要指定一个要执行的主类(main()方法),虚拟机会先初始化这个主类 (war包的主类在哪里,war包在哪里启动的?) --5.java.lang.invoke.MethodHandle方法句柄 (ps:被动使用类--常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类(引用的是字节码描述符,字段描述符在常量池中,没有这个类的符号引用入口)) 复制代码
-
类加载的过程(加载、验证、准备、解析、初始化)
--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”方法的过程)
-
类加载器("通过一个类的全限定名来获取定义此类的二进制字节流"这个动作放到虚拟机外部实现,让应用程序自己决定如何去获取所需要的类)
--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.顺序是由语句在源文件中出现的顺序决定
--2.在静态语句块里,可以访问定义在静态语句块之前的类变量,在静态语句块之后的类变量只可以赋值不能访问(非法向前引用)
--3.虚拟机会保证父类的先执行,再执行子类,所以父类中定义的静态语句块要优先于子类的变量赋值操作
--4.接口的不需要先执行父类,只有当父接口中定义的变量使用时,父接口才会初始化;接口的实现类在初始化时也不会执行"clinit"方法
-
默认的构造方法,完成类的实例化过程
- 类加载器
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上的类库