前言
只有光头才能变强
JVM在准备面试的时候就有看了,一直没时间写笔记。现在到了一家公司实习,闲的时候就写写,刷刷JVM博客,刷刷电子书。
学习JVM的目的也很简单:
- 能够知道JVM是什么,为我们干了什么,具体是怎么干的。能够理解到一些初学时不懂的东西
- 在面试的时候有谈资
- 能装逼
声明:全文默认指的是HotSpot VM
一、简单聊聊JVM
1.1先来看看简单的Java程序
现在我有一个JavaBean:
public class Java3y {
// 姓名
private String name;
// 年龄
private int age;
//.....各种get/set方法/toString
}
一个测试类:
public class Java3yTest {
public static void main(String[] args) {
Java3y java3y = new Java3y();
java3y.setName("Java3y");
System.out.println(java3y);
}
}
我们在初学的时候肯定用过javac
来编译.java
文件代码,用过java
命令来执行编译后生成的.class
文件。
Java源文件:
在使用IDE点击运行的时候其实就是将这两个命令结合起来了(编译并运行),方便我们开发。
生成class文件
解析class文件得到结果
1.2编译过程
.java
文件是由Java源码编译器(上述所说的java.exe)来完成,流程图如下所示:
Java源码编译由以下三个过程组成:
- 分析和输入到符号表
- 注解处理
- 语义分析和生成class文件
1.2.1编译时期-语法糖
语法糖可以看做是 编译器实现的一些“小把戏”,这些“小把戏”可能会使得 效率“大提升”。
最值得说明的就是泛型了,这个语法糖可以说我们是经常会使用到的!
- 泛型只会在Java源码中存在,编译过后会被替换为原来的原生类型(Raw Type,也称为裸类型)了。这个过程也被称为:泛型擦除。
有了泛型这颗语法糖以后:
- 代码更加简洁【不用强制转换】
- 程序更加健壮【只要编译时期没有警告,那么运行时期就不会出现ClassCastException异常】
- 可读性和稳定性【在编写集合的时候,就限定了类型】
了解泛型更多的知识:
1.3JVM实现跨平台
至此,我们通过java.exe
编译器编译我们的.java
源代码文件生成出.class
文件了!
这些.class
文件很明显是不能直接运行的,它不像C语言(编译cpp后生成exe文件直接运行)
这些.class
文件是交由JVM来解析运行!
- JVM是运行在操作系统之上的,每个操作系统的指令是不同的,而JDK是区分操作系统的,只要你的本地系统装了JDK,这个JDK就是能够和当前系统兼容的。
- 而class字节码运行在JVM之上,所以不用关心class字节码是在哪个操作系统编译的,只要符合JVM规范,那么,这个字节码文件就是可运行的。
- 所以Java就做到了跨平台--->一次编译,到处运行!
1.4class文件和JVM的恩怨情仇
1.4.1类的加载时机
现在我们例子中生成的两个.class
文件都会直接被加载到JVM中吗??
虚拟机规范则是严格规定了有且只有5种情况必须立即对类进行“初始化”(class文件加载到JVM中):
- 创建类的实例(new 的方式)。访问某个类或接口的静态变量,或者对该静态变量赋值,调用类的静态方法
- 反射的方式
- 初始化某个类的子类,则其父类也会被初始化
- Java虚拟机启动时被标明为启动类的类,直接使用java.exe命令来运行某个主类(包含main方法的那个类)
- 当使用JDK1.7的动态语言支持时(....)
所以说:
- Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到jvm中,至于其他类,则在需要的时候才加载。这当然就是为了节省内存开销。
1.4.2如何将类加载到jvm
class文件是通过类的加载器装载到jvm中的!
Java默认有三种类加载器:
各个加载器的工作责任:
- 1)Bootstrap ClassLoader:负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类
- 2)Extension ClassLoader:负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下的jar包
- 3)App ClassLoader:负责记载classpath中指定的jar包及目录中class
工作过程:
- 1、当AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。
- 2、当ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。
- 3、如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;
- 4、若ExtClassLoader也加载失败,则会使用AppClassLoader来加载
- 5、如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException
其实这就是所谓的双亲委派