一、JVM如何加载一个类?
最直观简单的方式就是通过图片来学习
下面我们来通过三道面试题来针对性学习下:
面试题常见的问题
1:这个class文件,谁来负责加载到内存中?
类加载器
- 类加载器负责从文件系统或者网络中加载Class文件,Class文件在开头(16进制)有特定的文件标记符 CA FE BA BE
- 类加载器(ClassLoader)只负责Class文件的加载,至于它是否可以运行,则由执行引(Execution Engine)决定。
(举个有趣的栗子:你朋友[ClassLoader]给你(Execution Engine)介绍对象,能不能成功看你自己)
2:class文件,存在内存哪个位置?
类加载器从class文件中抽取类信息并放在了方法区中,类信息:方法代码、变量名、方法名、访问权限、返回值等等。
3:Class对象存储在哪里?
这里说的是Class对象,不是class文件。
每当加载器从class文件中加载一个类的时候,都会加载类型信息到方法区中,同时生成一个Class对象,Class对象new多个对象实例。
Class对象存在堆内存当中。
二、一个类进入jvm之后,经历了什么?
先思考另外一个问题:一个类什么时候进入jvm?
3个时间节点加载
- 虚拟机启动时,执行main()方法的时候
- new对象的时候
- 读取静态字段或静态方法的时候
类从被加载道虚拟机内存中开始,到GC卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。
其中,加载、验证、准备、初始化、卸载这个五个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始。
另外这7个阶段通常是互相交叉的混合式进行的,通常会在一个阶段执行的过程中调用或激活另外一个阶段。
如下图所示:
三、面试题:jvm如何初始化一个类
在学习初始化之前,我们先要学会看类的字节码,在idea安装jclasslib插件
类的初始化
先安装一个工具jclasslib,在idea的插件市场可以直接安装。
jclasslib是一个可视化的class文件工具,它提供了工具库,供开发人员查看class文件的内容。
步骤1:先安装jclasslib插件
步骤2:查看字节码
方法:
用于对象实例初始化时被调用到。每次new对象的时候,都会调用该方法。
方法:
用于静态变量或静态块的初始化。当类class初始化的时候,调用该方法。
思考一个问题(这是前面的问题):
一个类什么时候被类加载器加载进jvm?也就是类什么时候被初始化,什么时候会执行
- 虚拟机启动时,执行main()方法的时候。
- new对象的时候。
- 读取静态字段或静态方法的时候(初始化静态变量以及静态块的时候)
也就是说在编译阶段(使用编译器编译的时候,不是把class编译给机器,这两个编译阶段是要区分开来的),仅仅把java文件编译成class文件的时候,并没有执行class的初始化
只有使用到这个class的时候才会真正去加载这个类到jvm,存放在方法区中,并生成class对象的时候执行。
笔试题:
运行下面代码,输出的结果是?
package com.oyb.jvm.test01;
public class Singleton {
private static Singleton instance = new Singleton();
public static int x = 0;
public static int y;
private Singleton() {
x ++;
y ++;
}
public static Singleton getInstance() {
return instance;
}
public static void main(String[] args) {
Singleton singleton = getInstance();
System.out.println(x);
System.out.println(y);
}
}
输出的结果是:
分析:
我们知道在执行main方法的时候,类加载器就会加载这个Singleton这个class文件,并初始化这个class对象。
一个类被加载进jvm中之后,有一个阶段叫做准备的阶段,在准备的阶段,为类变量赋值上默认值。
此时,instance = null; x = 0; y =0;
之后进入初始化这个class的阶段,会执行
在执行
执行到instance = new Signleton();这行指令的时候,就会调用Singleton的构造方法,就是会调用
这时候,就会执行下一行指令:x = 0;这时候就是显式赋值了,把x的值变成了0;而y没有被显式赋值,就会使用默认值,也就是前面自增之后的1;
最后输出的结果就是x=0;y = 1;
public class Singleton {
private static Singleton instance = new Singleton();
public static int x = 0;
public static int y;
private Singleton() {
x ++;
y ++;
}
}
经典笔试题:剖析类的初始化顺序
public class Father {
static{
System.out.println("Initialize class father");
}
}
public class Son extends Father {
public static int width = 60;
static{
System.out.println("Initialize class son");
width = 30;
}
public Son() {
System.out.println("Son init ... ");
}
}
public class Main {
static {
System.out.println("Main static init ...");
}
public static void main(String[] args) {
System.out.println("Main main begin");
Son a = new Son();
System.out.println(Son.width);
Son b = new Son();
}
}
输出的结果如下: