由一道面试题理解类加载机制

前言
不了解JVM的类加载机制你也可以coding,但是当你了解之后,可以让你在coding的时候避免很多坑,本文将以一道常见的面试题去剖析一下。本文参考 深入理解Java虚拟机(第2版) 。

 1public class ClassLoadTest {
 2    private static ClassLoadTest test = new ClassLoadTest();
 3
 4    static int x;
 5    static int y = 0;
 6
 7    public ClassLoadTest() {
 8        x++;
 9        y++;
10    }
11
12    public static void main(String[] args) {
13        System.out.println(test.x);
14        System.out.println(test.y);
15    }
16}

这里大家可以先猜测一下答案,可能结果会出乎你的意料~

类加载过程

先用一个图简单的描述一下类加载的这个过程

加载
这个过程相当于从本地或者网络端去读取一个字节流,然后将一些静态储存结构转换成方法区中运行时期的数据,最后生成一个代表这个类的Class对象,作为方法区访问这个类的入口。

例如:

  • 咱们可以通过一个类的全限定名去加载类
  • 通过jar、war包去加载类
  • 通过http请求去第三方平台上拉取指定的类来加载
  • 运行时计算生成,例如Cglib动态代理等等

针对上述例子,这里是加载一个?ClassLoadTest.class?对象。

验证

要理解这个环节并不是很难,一个东西要放到JVM上去运行,咱们肯定得对其进行一些过滤,不能啥都往上丢,这里的验证简单的举几个例子:

  • 文件格式的验证:?
    ①是否以魔数0xCAFEBABE开头;?
    ②主次版本号是否在当前虚拟机处理范围内;?
    ③常量池中的常量是否有不被支持的常量类型等等。
  • 元数据的验证:?
    ①这个类是否有父类;?
    ②这个类的父类是否继承了不被允许继承的类(final修饰的类);?
    ③这个类不是抽象类,是否实现了所有接口中要实现的方法等等。
  • 字节码的验证:
    ①保证跳转指令不会跳转到方法体以外的字节码指令上;?
    ②保证方法体中的类型转换是有效的等等。
  • 符号引用的验证:?
    ①能否通过类的全限定名去找到对应的类;?
    ②符号引用中的类、字段、方法是否可以被当前类访问等等。

准备过程

这个过程相当于给类变量分配内存并设置变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。

针对上述例子:

1test = null;
2x = 0;
3y = 0;

注意:这里有个特殊情况,如果该字段被?final?修饰,那么在准备阶段改字段就会被设置成咱们自定义的值。?public static final int?value?= 11?,在准备阶段就会直接赋值11,并不是该变量的初始值。

解析过程

将符号引用转换成直接引用的过程。这里有两个名词?符号引用?和?直接引用?。

  • 符号引用:符号引用与虚拟机的布局无关,甚至引用的目标不一定加载到了内存中。符号可以是任何形式的字面量,只要使用时能够准确的定位到目标即可。
  • 直接引用:直接引用可以直接指向目标的指针、相对偏移量或是一个能间接定位到目标的句柄。直接引用与虚拟机布局有关,如果有了直接引用,那么引用的目标必定已经在内存中存在。

而解析过程又会针对类、字段、方法进行解析,解析失败则会抛出相应的异常。例如在解析时发现没有访问权限会抛出?java.lang.IllegalAccessException?异常,查询不到引用字段会抛出?java.lang.NoSuchFieldException?异常,查询不到方法会抛出?java.lang.NoSuchMethodException?异常等等。

初始化

在准备阶段,变量已经赋值过系统要求的默认值,在初始化阶段,则会根据程序制定的主观计划去初始化类变量和其他资源。这句话听起来有些绕口,根据上述例子,实际上就是:

1test = new ClassLoadTest();// x = 1;y =1
2y = 0;

这个过程,由于?x?咱们自己并没有去设定一个值,所以初始化阶段它不会发生任何改变,但是?y?咱们有设定一个值0,所以最后造成最终结果为?x = 1;y = 0?。

ps:在同一个类加载器下,一个类只会初始化一次。多个线程同时初始化一个类,只有一个线程能正常初始化,其他线程都会进行阻塞等待,直到活动线程执行初始化方法完毕。

总结

对于上面这个面试题,咱们用流程图简单的描述一下:

原文地址:https://blog.51cto.com/13919357/2366024

时间: 2024-08-23 21:42:40

由一道面试题理解类加载机制的相关文章

深入理解类加载机制

之前的博客说了,类加载分为五个阶段 加载->链接(验证+准备+解析)->初始化(使用前的准备)->使用->卸载 今天,遇到一个很有趣的现象: public class Dervied extends Base { private String name = "dervied"; public Dervied() { tellName(); printName(); } public void tellName() { System.out.println(&qu

关于Java类加载双亲委派机制的思考(附一道面试题)

预定义类加载器和双亲委派机制 JVM预定义的三种类型类加载器: 启动(Bootstrap)类加载器:是用本地代码实现的类装入器,它负责将 <Java_Runtime_Home>/lib下面的类库加载到内存中(比如rt.jar).由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作. 标准扩展(Extension)类加载器:是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)

从一道面试题来认识java类加载时机与过程

说明:本文的内容是看了<深入理解Java虚拟机:JVM高级特性与最佳实践>后为加印象和理解,便记录了重要的内容. 1  开门见山 以前曾经看到过一个java的面试题,当时觉得此题很简单,可是自己把代码运行起来,可是结果并不是自己想象的那样.题目如下: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class SingleTon {     private static SingleTon singleTon = new S

从一道面试题来认识java类加载时机与过程【转】

说明:本文的内容是看了<深入理解Java虚拟机:JVM高级特性与最佳实践>后为加印象和理解,便记录了重要的内容. 1  开门见山 以前曾经看到过一个java的面试题,当时觉得此题很简单,可是自己把代码运行起来,可是结果并不是自己想象的那样.题目如下: class SingleTon { private static SingleTon singleTon = new SingleTon(); public static int count1; public static int count2

两道面试题,带你透彻解析Java类加载机制

在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题: class Grandpa { static { System.out.println("爷爷在静态代码块"); } } class Father extends Grandpa { static { System.out.println("爸爸在静态代码块"); } public static int factor = 25; public Father() { System.ou

用两道面试题带你详细了解 Java 类加载机制

在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题: class Grandpa{static{System.out.println("爷爷在静态代码块");}} class Father extends Grandpa{static{System.out.println("爸爸在静态代码块");}public static int factor = 25;public Father(){System.out.println("

深入理解Java:类加载机制及反射

一.Java类加载机制 1.概述 Class文件由类装载器装载后,在JVM中将形成一份描述Class结构的元信息对象,通过该元信息对象可以获知Class的结构信息:如构造函数,属性和方法等,Java允许用户借由这个Class相关的元信息对象间接调用Class对象的功能. 虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 2.工作机制 类装载器就是寻找类的字节码文件,并构造出类在JVM内部表示

【深入理解Java虚拟机】类加载机制

本文内容来源于<深入理解Java虚拟机>一书,非常推荐大家去看一下这本书. 本系列其他文章: [深入理解Java虚拟机]Java内存区域模型.对象创建过程.常见OOM [深入理解Java虚拟机]垃圾回收机制 1.类加载机制概述 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 在java中,类型的加载.连接和初始化过程都是在程序运行期间完成的,这种策略虽然会带来一些性能开销,但是却为jav

【深入理解JVM】:类加载机制

概述 虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 与那些在编译时需要进行链接工作的语言不同,在Java语言里,类型的加载.连接和初始化过程都是在程序运行期间完成的,例如import java.util.*下面包含很多类,但是,在程序运行的时候,虚拟机只会加载哪些我们程序需要的类.这种策略虽然会令类加载时稍微增加一些性能开销,但是会为Java应用程序提供高度的灵活性. 类加载的时机 类从