1.JVM 简介
JVM 全称是Java Virtual Machine ,Java 虚拟机,也就是在计算机上再虚拟一个计算机。
这和我们使用 VMWare 不一样,那个虚拟的东西你是可以看到的,这个JVM 你是看不到的,它存在内存中。
计算机的基本构成是:运算器、控制器、存储器、输入和输出设备。
JVM 也是有这成套的元素:
运算器 -当然是交给硬件CPU 还处理了
为了适应“一次编译,随处运行”的情况,需要做一个翻译动作,于是就用了JVM 自己的命令集。
这与汇编的命令集有点类似,每一种汇编命令集针对一个系列的CPU ,
比如8086 系列的汇编也是可以用在8088 上的,但是就不能跑在8051 上。
JVM 的命令集则是可以到处运行的,因为JVM根据不同的CPU ,翻译成不同的机器语言。
存储器 - JVM 是一个内存中的虚拟机,那它的存储器就是内存了。
我们写的所有类、常量、变量、方法都在内存中。
2.JVM的组成部分
JVM 是运行在操作系统之上的,它与硬件没有直接的交互:
JVM 构成图,整个JVM 分为四部分:
1) Class Loader 类加载器
类加载器的作用是加载类文件到内存,
比如编写一个HelloWord.java 程序,然后通过javac 编译成class 文件,
那怎么才能加载到内存中被执行呢?Class Loader 承担的就是这个责任。
Class Loader 只管加载,只要符合文件结构就加载,
至于说能不能运行,则不是它负责的,那是由Execution Engine 负责的。
2) Execution Engine 执行引擎
也叫做解释器(Interpreter) ,负责解释命令,提交操作系统执行。
3) Native Interface 本地接口
本地接口的作用是融合不同的编程语言为Java 所用,它的初衷是融合C/C++ 程序
目前该方法使用的已经比较少见了,因为现在的异构领域间的通信很发达,
比如可以使用Socket 通信,也可以使用Web Service 等等
4) Runtime data area 运行数据区
运行数据区是整个JVM 的重点。我们所有写的程序都被加载到这里,之后才开始运行
整个JVM 框架由加载器加载文件,然后执行器在内存中处理数据,需要与异构系统交互是可以通过本地接口进行,一个完整的系统诞生了。
3.JVM加载class文件的原理机制
1. Java中的所有类,必须被装载到jvm中才能运行。
这个装载工作是由jvm中的类装载器完成的,类装载器所做的工作实质是把类文件从硬盘读取到内存中。
2. Java中的类大致分为三种:
1) 系统类
2) 扩展类
3) 由程序员自定义的类
3. 类装载方式,有两种:
1) 隐式装载。 程序在运行过程中当碰到通过new 等方式生成对象时,
隐式调用类装载器加载对应的类到jvm中。
2) 显式装载。 通过class.forname()等方法,显式加载需要的类。
隐式加载与显式加载的区别:
4.类加载的动态性体现:
一个应用程序总是由n多个类组成,Java程序启动时,并不是一次把所有的类全部加载后再 运行.
它总是先把保证程序运行的基础类一次性加载到jvm中,其它类等到jvm用到的时候再加载.
这样的好处是节省了内存的开销.
5. Java类装载器:
Java中的类装载器实质上也是类,功能是把类载入jvm中.
值得注意的是jvm的类装载器并不是一个,而是三个。
为什么要有三个类加载器,一方面是分工,各自负责各自的区块,另一方面为了实现委托模型。
Bootstrap Loader - 负责加载系统类(用C++语言写的) 搜索路径:sun.boot.class.path
|
ExtClassLoader - 负责加载扩展类 搜索路径:java.ext.dirs
|
AppClassLoader - 负责加载应用类 搜索路径:java.class.path
6. 类加载器之间是如何协调工作的:
在这里java采用了委托模型机制.
类装载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入,
如果Parent 找不到,那么才由自己依照自己的搜索路径搜索类。
7. 预先加载(pre-loading)与依需求加载(load-on-demand):
1)在 JRE 运行的开始会将 Java 运行所需要的基本类采用预先加载( pre-loading )的方法全部加载要内存当中.
主要包括 JRE 的 rt.jar 文件里面所有的 .class 文件
2) 当 java.exe 虚拟机开始运行以后,它会找到安装在机器上的 JRE 环境,然后把控制权交给 JRE .
JRE 的类加载器会将 lib 目录下的 rt.jar 基础类别文件库加载进内存.
3) 相对于预先加载,我们在程序中需要使用自己定义的类的时候就要使用依需求加载方法
( load-on-demand ),就是在 Java 程序需要用到的时候再加载,以减少内存的消耗.
8.自定义类加载机制:
一般我们都是调用系统的 类加载器来实现加载的,其实我们是可以自己定义类加载器的。
利用 Java 提供的 java.net.URLClassLoader 类就可以实现。
try {
URL url = new URL( "file:/d:/test/lib/" );
URLClassLoader urlCL = new URLClassLoader( new URL[]{url});
Class c = urlCL.loadClass("TestClassA" );
TestClassA object = (TestClassA)c.newInstance();
object.method();
}catch (Exception e){
e.printStackTrace();
}
9.类加载器的阶层体系:
1)Java 的类加载器的工作原理:
当执行 java ***.class 的时候, java.exe 会帮助我们找到 JRE
接着找到位于 JRE 内部的 jvm.dll ,这才是真正的 Java 虚拟机器
最后加载动态库,激活 Java 虚拟机器
虚拟机器激活以后,会先做一些初始化的动作,比如说读取系统参数等。
一旦初始化动作完成之后,就会产生第一个类加载器―― Bootstrap Loader
Bootstrap Loader 是由 C++ 所撰写而成
Bootstrap Loader 所做的初始工作中,除了一些基本的初始化动作之外,
最重要的就是加载 Launcher.java 之中的 ExtClassLoader ,并设定其 Parent 为 null
代表其父加载器为 BootstrapLoader
然后 Bootstrap Loader 再要求加载 Launcher.java 之中的 AppClassLoader ,
并设定其 Parent 为之前产生的 ExtClassLoader 实体
*Launcher$ExtClassLoader.class 与 Launcher$AppClassLoader.class 都是由 Bootstrap Loader 所加载,
所以 Parent 和由哪个类加载器加载没有关系。
2)类加载器之间的关系
BootstrapLoader <---(Extends)----AppClassLoader <---(Extends)----ExtClassLoader
这三个加载器就构成我们的 Java 类加载体系
3) 他们分别从以下的路径寻找程序所需要的类:
BootstrapLoader : sun.boot.class.path
ExtClassLoader: java.ext.dirs
AppClassLoader: java.class.path
这三个系统参量可以通过 System.getProperty() 函数得到具体对应的路径。