JVM类加载机制
类加载机制
流程:
jvm通过类加载器将Class文件通过二进制数据读取到内存中,放到方法区中,并且在堆中创建类的对象,方法区中存的相当于这个类运行时的数据结构,这写数据结构是通过堆中类对象进行访问的。
类的生命周期
加载-->验证-->准备-->解析-->初始化-->使用-->卸载
其中 加载,验证,准备,初始化是按照顺序执行的,解析可能在某种情况下在初始化之后执行,这也是为了支持Java的动态绑定
动态绑定
/**
*
* @author QuinnNorris
* 域不具有多态性
*/
public class polymorphics {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
Super sup = new Sub();//这儿的类型的Super
System.out.println("sup.field = " + sup.field + ", sup.getField() = "
+ sup.getField());
Sub sub = new Sub();
System.out.println("sub.field = " + sub.field + ", sub.getField() = "
+ sub.getField() + ", sub.getSuperField() = "
+ sub.getSuperField());
}
}
class Super {
public int field = 0;
public int getField() {
return field;
}
}
class Sub extends Super {
public int field = 1;
public int getField() {
return field;
}
public int getSuperField() {
return super.field;
}
}
结果:
sup.field = 0, sup.getField() = 1
sub.field = 1, sub.getField() = 1, sub.getSuperField() = 0
也就是普通方法是根据实例来执行的,但是属性和静态方法是根据类型来执行的。
加载
- 通过全限定类名将其转换为二进制流
- 将二进制流转换为方法区中的运行时数据结构
- 堆中生成一个类,用来访问方法区中的运行时数据结构验证
验证
确保加载的类的正确性
- 文件格式验证 开头有个标识
- 元数据验证
- 字节码验证
- 符号引用验证
准备
为类的静态变量分配内存,将其初始化为默认值
默认值是根据类型来确定的比如 int是0,String是null
解析
解析是将类中的符号引用转换为直接引用(符号引用和直接引用)
- 符号引用:一组符号来描述目标,可以是任何字面量
- 直接引用:指针,偏移量,或者指向目标的句柄
初始化
上面准备是将静态变量赋予了默认值,初始化就是赋予正确的值。
初始值设定了两种方式:
- 声明类变量的时候指定初始值
- 使用静态代码块为类变量设置初始值
jvm初始化步骤
- 先加载类
- 看这个类是否有父类,有的话先初始化其父类
- 依次执行类中的初始化语句
类初始化
- new一个实例的时候(包括clone,反序列化等)
- 调用静态方法的时候
- 调用静态属性的时候
- 实例化子类的时候
- 反射(Class.forName("全限定类名"))
- 作为启动类的时候
补充一个初始化方法的执行顺序
/**
* @program: threadpooltest
* @Date: 2020/1/8 14:36
* @Author: lvjiangpeng
* @Description:
*/
public class Test {
static {
System.out.println("启动类 static method");
}
{
System.out.println("启动类 method");
}
public Test(){
System.out.println("启动类 construction");
}
public static void main(String[] args) throws ClassNotFoundException {
Baby baby = new Baby();//实例化子类,
// Father.say();//调用静态方法
// Class.forName("com.lvjiangpeng.threadpooltest.threadpooltest.jvmDay01.Father");
System.out.println(Father.name);//调用静态属性
}
}
class Father{
public static String name = "猪妈妈";
public static void say(){
System.out.println("Father static say method");
}
static {
System.out.println("Father static method");
}
public Father(){
System.out.println("Father construction");
}
{
System.out.println("Father method");
}
}
class Mather{
static {
System.out.println("Mather static method");
}
public Mather(){
System.out.println("Mather construction");
}
{
System.out.println("Mather method");
}
}
class Baby extends Mather{
static {
System.out.println("baby static method");
}
{
System.out.println("baby method");
}
public Baby(){
System.out.println("Baby construction");
}
}
输出:
启动类 static method
Mather static method
baby static method
Mather method
Mather construction
baby method
Baby construction
Father static method
猪妈妈
- 启动类的静态代码块
- 父类的静态代码块
- 子类静态代码块
- 父类的代码块
- 父类的构造函数
- 子类的代码块
- 子类的构造函数
结束生命周期
- 程序正常执行结束
- 遇到异常导致程序终止
- 程序出错导致虚拟机终止
- 执行System.exit()方法
类加载器
- 启动类加载器 Bootstrap ClassLoader(C++)
- 加载JDK\jre\lib 和 -Xbootclasspath指定目录下的类
- 无法直接被java程序引用
- 扩展类加载器 ExtClassLoader(Java)
- 加载JDK\jre\lib\ext 或者 java.ext.dirs系统变量指定路径的类(如javax开头的类)
- 应用类加载器 AppClassLoader(Java)
- 加载用户类路径指定的类(ClassPath),程序的默认类加载器
- 自定义类加载器
JVM加载机制
- 全盘负责,一个类加载器加载某个类的时候,这个类依赖和引用的其他类也由其负责,除非指定另一个加载器来载入。
- 双亲委派,先由父类加载,父类加载不了自己再尝试加载
- 缓存机制,每个加载的类都会被缓存,使用类的时候都是先去缓存查找,缓存没有再加载然后缓存,所以更改了Class之后需要重启jvm。
类的加载
- 启动程序的时候由JVM初始化加载
- Class.forName()反射加载
- ClassLoader loader = 类.class.getClassLoader()
- loader.loaderClass();
public class BaseTest {
public static void main(String[] args) throws ClassNotFoundException {
/**
* ClassLoader.loaderClass() 只是将类加载到jvm中,不会执行静态代码块,只有newInstance的时候才会执行
* Class.forName()将类加载到jvm中,并且进行解释,执行静态代码块
* Class.forName(name,initializa,loader)将类加载到jvm中,带的参数可以控制是否执行静态代码块
*/
ClassLoader loader = HelloWorld.class.getClassLoader();
/**
*Process finished with exit code 0 不会执行初始化块
*/
loader.loadClass("com.lvjiangpeng.threadpooltest.threadpooltest.bastTest.Test2");
/**
* Test2 static code 会执行初始化块
*/
// Class.forName("com.lvjiangpeng.threadpooltest.threadpooltest.bastTest.Test2");
/**、
*指定类加载器,初始化时不执行静态块
*/
// Class.forName("com.lvjiangpeng.threadpooltest.threadpooltest.bastTest.Test2",false,loader);
Class.forName("com.lvjiangpeng.threadpooltest.threadpooltest.bastTest.Test2",true,loader);
}
}
class HelloWorld {
static {
System.out.println("helloworld static code");
}
public HelloWorld(){
System.out.println("helloworld construction");
}
}
class Test2 {
static {
System.out.println(" Test2 static code");
}
public Test2(){
System.out.println("Test2 构造函数");
}
}
注:这是学习笔记 通过学习 纯洁的微笑 JVM相关文章以及网上相关文章做的笔记,有不对的地方还望多多指教。