Java跨平台:
Java是高级语言,但是真正的执行需要最终转换成机器指令才能执行,计算机并不认识高级语言,了解JVM相关的知识也能
更好帮助我们了解Java语言的执行流程。
首先.java文件经过编译成为.class文件(字节码文件),然后将编译好的字节码文件放到虚拟机中就可以执行了。
Java虚拟机:
1、Java虚拟机是一台执行Java字节码的虚拟计算机,它拥有独立的运行机制,其运行的Java字节码也未必由Java语言编译而成。
2、JVM平台的各种语言可以共享Java虚拟机带来的跨平台性、优秀的垃圾回器,以及可靠的即时编译器。
3、Java技术的核心就是Java虚拟机(JVM,Java Virtual Machine),因为所有的Java程序都运行在Java虚拟机内部。
4、Java虚拟机就是二进制字节码的运行环境,负责装载字节码到其内部,解释/编译为对应平台上的机器指令执行。
每一条Java指令,Java虚拟机规范中都有详细定义,如怎么取操作数,怎么处理操作数,处理结果放在哪里。
作用:
Java虚拟机就是二进制字节码的运行环境,负责装载字节码到其内部,解释/编译为对应平台上的机器指令执行。
每一条Java指令,Java虚拟机规范中都有详细定义,如怎么取操作数,怎么处理操作数,处理结果放在哪里。
特点:
1、一次编译,到处运行
2、自动内存管理
3、自动垃圾回收功能 GC
JVM位置:
JVM是运行在操作系统之上的,它与硬件没有直接的交互
JVM整体结构:
1、 HotSpot VM是目前市面上高性能虚拟机的代表作之一。
2、它采用解释器与即时编译器并存的架构。
3、在今天,Java程序的运行性能早已脱胎换骨,已经达到了可以和C/C++程序一较高下的地步。
字节码文件:Java字节码类文件(.class)是Java编译器编译Java源文件(.java)产生的“目标文件”。
类加载器子系统
虚拟机栈:每个线程在创建时都会创建一个虚拟机栈,其内部保存一个个的栈帧(stack Frame) ,对应着一次次的Java方法调用。一次方法的调用,就是栈帧入栈到出栈的过程
Class Files(字节码文件) 要加载到运行时数据区(Runtime Data Area)
类加载器子系统的作用就是将字节码文件加载到JVM里面
方法区:就是字节码文件加载到JVM后,存放的地方
堆:new的对象,就在堆中
虚拟机栈:就是每当调用一个方法的时候,就要压栈,方法中的变量也存放在里面。
程序计数器:就是控制如何一条一条的执行指令
虚拟机的启动:
Java虚拟机的启动是通过引导类加载器(bootstrap class loader)创建一个初始类(initial class)来完成的,这个类是由虚拟机的具体实现指定的。
虚拟机的退出:
有如下的几种情况:
加载阶段只会选一个类加载器去加载
验证:验证字节流中包含信息的 格式是否正确
准备:为类变量(static)分配内存并且设置该类变量的默认初始值
解析:将符号引用转化为直接引用
初始化:给静态变量、静态代码块,赋初始值
public class HelloLoader {
public static void main(String[] args) {
System.out.println("谢谢ClassLoader加载我....");
System.out.println("你的大恩大德,我下辈子再报!");
}
}
它的加载过程是怎么样的呢?
1、执行 main() 方法(静态方法)就需要先加载承载类 HelloLoader
2、加载成功,则进行链接、初始化等操作,完成后调用 HelloLoader 类中的静态方法 main
3、加载失败则抛出异常
完整的流程图如下所示: 加载 --> 链接(验证 --> 准备 --> 解析) --> 初始化
加载流程:
加载class文件的方式:
4. 从本地系统中直接加载
5. 通过网络获取,典型场景:Web Applet
6. 从zip压缩包中读取,成为日后jar、war格式的基础
7. 运行时计算生成,使用最多的是:动态代理技术
8. 由其他文件生成,典型场景:JSP应用从专有数据库中提取.class文件,比较少见
9. 从加密文件中获取,典型的防Class文件被反编译的保护措施
链接分为三个子阶段:验证 --> 准备 --> 解析
验证(Verify):
准备(Prepare):
public class HelloApp {
private static int a = 1;
public static void main(String[] args) {
System.out.println(a);
}
}
解析(Resolve):
在编译的时候一个每个java类都会被编译成一个class文件,但在编译的时候虚拟机并不知道所引用类的地址,所以就用符号引用来代替,而在这个解析阶段就是为了把这个符号引用转化成为真正的地址的阶段。
String str = “abc”; 编译时候没有分配内存,不知道在内存哪里所以先用符号代替,解析阶段把符号引用替换为直接引用。
当我们代码中包含static变量的时候,就会有clinit方法
1、无 static 变量:
2、有static变量
3、构造器方法中指令按语句在源文件中出现的顺序执行
静态变量 number 的值变化过程如下
1、prepare准备阶段时:0
2、 执行静态变量初始化:10
3、 执行静态代码块:20
静态变量 number 的值变化过程如下
1、准备阶段时:0
2、执行静态代码块:20
3、执行静态变量初始化:10
4、若该类具有父类,JVM会保证子类的 () 执行前,父类的 () 已经执行完毕
加载流程如下:
1、首先,执行 main() 方法需要加载 ClinitTest5 类
2、 获取 Son.B 静态变量,需要加载 Son 类
3、Son 类的父类是 Father 类,所以需要先执行 Father 类的加载,再执行 Son 类的加载
类加载器的分类:
规范定义: 所有派生于ClassLoader的类加载器都划分为自定义类加载器,所以ExtClassLoader和AppClassLoader也归类到自定义类加载器。
BootstrapClassLoader是用C、C++语言写的,其余都是Java写的。
public class ClassLoaderTest {
public static void main(String[] args) {
//获取系统类加载器AppClassLoader 也叫应用类加载器
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//获取其上层:扩展类加载器 ExtensionClassLoader
ClassLoader extClassLoader = systemClassLoader.getParent();
System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@45ee12a7
//获取其上层:获取不到引导类加载器 Bootstrap ClassLoader
ClassLoader bootstrapClassLoader = extClassLoader.getParent();
System.out.println(bootstrapClassLoader);//null (C、C++写的,我们拿不到)
//对于用户自定义类来说:默认使用系统类加载器进行加载
ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
//String类使用引导类加载器进行加载的。---> Java的核心类库都是使用引导类加载器进行加载的。
ClassLoader classLoader1 = String.class.getClassLoader();
System.out.println(classLoader1);//null
}
}
输出结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@45ee12a7
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null
举例 1 :代码:我们自己建立一个 java.lang.String 类,写上 static 代码块
因为双亲委派机制,所以要先自底向上去去检查有没有加载 java.lang.String,然后再自顶向下去尝试加载,由于Bootstrap启动类加载器里面有java.lang.String这个包,所以直接就在Bootstrap启动类加载器加载了,应用类加载器根本就没有机会去加载 java.lang.String
案例2:在我们自己的 String 类中整个 main() 方法
举例 3 :在 java.lang 包下整个 Robot类
通过上面的例子,我们可以知道,双亲机制可以1. 避免类的重复加载2. 保护程序安全,防止核心API被随意篡改 1. 自定义类:java.lang.String 没有用 2. 自定义类:java.lang. Robot(报错:阻止创建 java.lang开头的类)