JVM学习笔记(一)——内存模型,类加载过程
JVM整体架构
字节码文件编译到Class loader system。
其中方法区和堆(黄颜色)是线程共享的,其他三个是每个线程独有一份的。
Java代码执行过程
因为操作系统不识别高级语言,只识别机器指令,所以需要从高级语言=》汇编语言=》机器指令。
Java文件从编译器到字节码文件,字节码文件从类加载器到校验器最后到编译执行。
JIT编译器可以将多次执行的热点代码缓存起来,下次可以直接使用,提高了效率。
JVM的生命周期
JVM发展历程
Sun Classic VM:世界上第一款商用Java虚拟机。
这款虚拟机内部只提供解释器。
Exact VM
Hot Spot:现在的jdk都是基于该虚拟机。
BEA的JRockit:这款虚拟机内部没有解释器,就通过即时编译器。是世界上最快的JVM。
IBM的J9
Apache的Harmony
解释器和JIT即时编译器,可以不是同时存在的。
早期的java虚拟机就只有解释器,但是这样就会导致效率很低下,比如循环里一段重复的代码要重复,所以加上JIT即时编译器,就会将一些热点代码缓存起来,配合使用,效率更快。
类加载器子系统
class load system:加载字节码文件,分为三大步骤。
1、加载阶段。
2、链接阶段。
3、初始化阶段。
类的加载过程
1、加载
2、链接
变量在prepare只是生成默认的初始值,在后面的inital初始化才会给它赋值。
常量,final修饰的,在prepare就会进行初始化。
3、初始化
< clinit >() 就是类里静态赋值的过程。如上图。
上述先linking中的prepare将各个变量做初始化操作。
再由< clinit >() 方法自上而下做赋值操作。
变量num: 先给1,在给2.
变量number: 先给20,在给10.
所以,当定义在static下面时,此时打印出的是下面的值。
< init >() 是指构造函数里的赋值过程。
java每个类都必须有构造函数,所以肯定也会有< init >() 方法。
但,如果没有给static之类的类变量,则可以没有< clinit >() 。
如果存在继承关系
如上图所示。main用到了son这个类,所以去引用它,引用之前发现son用到father,所以要先初始化father(加载,链接,初始化)一系列操作后,A=2,所以son去引用时,就是2这个值。可以看下面的字节码源码。
一个类的< clinit >() 方法在多线程下会在被同步加锁
上述可以看出,两个类在runnable里都要初始化这个类,一个线程进来后锁住了,另一个进不来,导致阻塞。
类加载器分类
引导类加载器:是由C/C++编写的,在java里获取不到,为null,主要加载java的核心类库。
扩展类加载器,系统类加载器:都是由java写的。可以通过getParent()获取。我们自己写的java类也是由该加载器进行加载。
双亲委派机制
如果你创建一个包名叫java.lang的包,下面创建一个叫做String的类。这时候,你新建一个测试类去new这个String,会出现什么问题呢?会加载吗?
其实这时候会将委托抛给上层的父类,逐级往上,到引导类的时候,引导类刚好是加载核心java类下的(lang包归他管)所以他就加载系统String了。
如果父类没完成加载任务,子加载器才会自己去尝试加载。