Java类的加载过程

java类加载机制

本文介绍从java源文件到在JVM运行时经历的过程

一、编译

我们写好的java代码保存为.java源文件,要投入内存运行,首先需要由java的编译器javac进行编译,成为.clss文件即字节码文件,该文件本质上是一个二进制文件,用读取字节流的软件打开发现是16进制的。它其实就是把我们写的代码转换为另一种表达方法,一种符合在jvm上运行规范的标准的文件。

二、类的加载过程(加载到JVM上)

其实就是把编译后的字节码文件加载到内存中去,以便JVM对改文件进行解释运行。整个类的加载过程可以分为三个步骤:

1.类加载

由类加载器完成,负责根据类的全限定类名把.class文件中的字节流加载到运行时内存的方法区上,并为该类创建一个java.lang.Class对象,一般存放在堆中,以后对方法区中该类的数据访问就以这个Class对象为接口。

2.链接

可以细分为验证准备解析三个阶段。验证:确保字节码文件中字节流中包含的信息符合当前JVM的要求,且不会危害JVM自身安全。准备:该阶段负责为类的静态变量分配内存,并赋默认值。解析:负责把把类中的符号引用转换为直接引用。

3.初始化

为类的静态变量赋值(链接阶段的准备是赋默认值),然后执行类的初始化语句(即static代码块)。可细分为三个步骤:

1.如果类还没被加载和链接,先进行加载和链接。

2.如果类存在父类,并且父类为初始化,先初始化父类。

3.如果类中存在初始化语句,顺序执行这些语句。

在这里插入图片描述

三、JVM解释运行

字节码文件经类加载器加载到内存中后,就由JVM将其解释成机器码,让机器直接运行。


java运行时数据区介绍

在这里插入图片描述

java运行时数据区主要包含五个部分:方法区、堆,这两个区域是线程共享的;jvm栈、程序计数器和本地方法栈,这三个区域是每个线程私有的。

1.方法区:共享内存区,允许多个线程共同访问,是线程安全的。运行时常量池包含在内。存储内容有:类的全限定类名、超类的权限定类名、类的访问修饰符、常量池等类的基本信息,在虚拟机启动时创建。

2.堆:共享内存区,允许多个线程共同访问,是线程安全的。也是GC的主要区域,在虚拟机启动时创建。存储类的实例化对象和数组。

3.虚拟机栈:线程私有,栈中包含着一个个栈帧(基本单位),一个栈帧的入栈和出栈即对应该线程内一个方法的调用和销毁。栈帧中包含该方法的局部变量表、操作数栈、指向运行时常量池的引用、方法返回地址等信息。

4.程序计数器:线程私有,负责记录线程下一条指令的选取,虚拟机解释字节码就需要用到该器件,从而决定执行线程的哪一条指令。

5.本地方法栈:与虚拟机栈功能基本一致,只是虚拟机栈是用于执行java方法,而本地方法栈用于执行本地方法。


类加载器介绍

JVM提供了三种类加载器:

1.系统类加载器(Bootstrap ClassLoader):C++实现,在java里无法获取,负责加载<JAVA_HOME>/lib下的类。

2.扩展类加载器(Extension ClassLoader):Java实现,可以再java里获取,负责加载<JAVA_HOME>/lib/ext下的类。

3.系统类加载器/应用程序类加载器(Application ClassLoader):是我们接触最多的类加载器,我们写的代码默认就是由它来加载的。

在这里插入图片描述

双亲委派机制:当前加载器加载某个类的时候,一般先不自己加载,如果有父类加载器,那就先交给父类加载器加载该类,逐层递归,如果父类加载器无法加载该类,才逐层返回,交由子类加载器加载该类。loadClass()源码如下:

protected Class<?> loadClass(String name, boolean resolve)
        throws ClassNotFoundException
    {
        synchronized (getClassLoadingLock(name)) {
            // First, check if the class has already been loaded
            Class<?> c = findLoadedClass(name);
            if (c == null) {
                long t0 = System.nanoTime();
                try {
                    if (parent != null) {
                        c = parent.loadClass(name, false);
                    } else {
                        c = findBootstrapClassOrNull(name);
                    }
                } catch (ClassNotFoundException e) {
                    // ClassNotFoundException thrown if class not found
                    // from the non-null parent class loader
                }

                if (c == null) {
                    // If still not found, then invoke findClass in order
                    // to find the class.
                    long t1 = System.nanoTime();
                    c = findClass(name);

                    // this is the defining class loader; record the stats
                    sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
                    sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
                    sun.misc.PerfCounter.getFindClasses().increment();
                }
            }
            if (resolve) {
                resolveClass(c);
            }
            return c;
        }
    }

如果要破坏双亲委派机制,交给我们自己定义的类加载器来加载某个类,可以重写loadClass()方法。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值