1 简介
JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。
引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。
常见的虚拟机
-
SUN Classic VM: 第一款商用java虚拟机,1996年1月jdk1.0中带的java虚拟机,只能使用纯解释器的方式来执行java代码 -
Exact VM:准确式内存管理,编译器和解释器混合工作以及两级即时编译 ,只在Solaris平台发布 -
HotSport VM: 即时编译,节约了时间和存储,称霸武林。也是目前JDK8.0的默认虚拟机。 -
KVM:简单,轻量,高可以执行,主要在手机平台使用 -
JRockit:BEA,世界上最快的java虚拟机,专注服务端应用,oracle收购BEA后,JRockit基本上已经死 -
j9:IBM Technology for java virtual Machines IT4J -
dalvik:不能直接指向class文件,寄存器架构,执行dex文件,由class文件转化而来 -
MicrosoftJvm:只能在windows平台运行
JVM 内存模型共分为5个区:
- 堆(Heap)
- 方法区(Method Area)
- 程序计数器(Program Counter Register)
- 虚拟机栈(VM Stack)
- 本地方法栈(Native Method Stack)
JVM(hotspot)结构概览如下图所示:
- 灰色部分(Java栈,本地方法栈和程序计数器)是线程私有,不存在线程安全问题,橙色部分(方法区和堆)为线程共享区。
2 类加载器
类加载器将class文件字节码内容加载到内存中,并将这些内容转换成方法区中的运行时数据结构。
ClassLoader只负责class文件的加载,至于它是否可以运行,则由执行引擎Execution Engine决定
类加载器分类
**启动类加载器(BootstrapClassLoader):**也叫根加载器,是虚拟机自带的加载器,用于加载$JAVA_HOME/jre/lib/rt.jar包内的class文件。rt.jar是Java基础类库,包含Java运行环境所需的基础类。
**拓展类加载器(ExtClassLoader):**虚拟机自带的加载器,由Java语言实现,用于加载$JAVA_HOME/jre/lib/ext/.jar目录下的class文件。
应用程序类加载器(AppClassLoader): 虚拟机自带的加载器,用于加载当前应用的classpath的所有类,也就是我们自己写的那些Java代码。
System.out.println(Test.class.getClassLoader());
用户自定义加载器: 通过继承Java.lang.ClassLoader抽象类自定义一个类加载器。
加载器的关系图:
它们的关系是一种父子关系,通过代码验证:
public class Test {
public static void main(String[] args) {
Class<Test> testClass = Test.class;
System.out.println(testClass.getClassLoader());
System.out.println(testClass.getClassLoader().getParent());
System.out.println(testClass.getClassLoader().getParent().getParent());
}
}
--------------------------
运行结果:
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@61bbe9ba
null
加载步骤:
类的加载过程分为三个步骤:
(1)加载(Loading) 通过一个类的全类名获取其二进制字节流,将这个二进制流代表的静态存储结构转化为方法区的运行时数据结构,然后在内存中生成一个代表这个类的java.lang.Class对象,作为方法区中这个类的各种数据的访问入口。
(2) 链接Linking 该过程又可以分为三个阶段:验证Verfication,准备Preparation和解析Resolution)。
- 验证阶段: 用于确保加载的Class文件的字节流包含的信息是否符合虚拟机要求,保证其正确性合法性
- 准备阶段: 为类变量(static修饰的变量)分配内存并根据对象类型设置相应的默认初始值(比如int类型为0,Integer类型为null)
(这里不包含常量,因为常量在编译的时候分配,准备阶段会显示初始化。类的实例变量不会在这个阶段准备初始化。) - 解析阶段: 用于将符号引用转换为直接引用。关于符号引用看下示例
实例代码:
public class Test {
public static void main(String[] args) {
String str = "hello";
System.out.println(str);
}
}
使用 javap -v Test.class 命令查看其字节码: 可以看到常量池中有许多符号引用(比如#2),解析阶段就是将其解析为直接引用(比如#2表示字符串常量hello)的过程。
|