JVM篇: 类的加载过程详解

类的加载过程详解

概述

在 Java 中数据类型分为基本数据类型和引用数据类型。基本数据类型由虚拟机预先定义,引用数据类型则需要进行类的加载。

按照 Java 虚拟机规范,从 Class 文件到加载到内存中的类,到类卸载出内存位置,它的整个生命周期包括如下七个阶段:

 

其中,验证、准备、解析 3 个部分统称为链接(Linking)。

从程序中类的使用过程看:

 

Loading(加载)阶段

加载的理解

所谓加载,简而言之就是将 Java 类的字节码文件加载到机器内存中,并在内存中构建出 Java 类的原型——类模板对象。所谓类模板对象,其实就是 Java 类在 JVM 内存中的一个快照,JVM 将从字节码文件中解析出的常量池、类字段、类方法等信息存储到模板中,这样 JVM 在运行期便能通过类模板而获取 Java 类中的任意信息,能够对 Java 类的成员变量进行遍历,也能进行 Java 方法的调用。

反射的机制即基于这一基础。如果 JVM 没有将 Java 类的声明信息存储起来,则 JVM 在运行期也无法反射。

加载完成的操作

加载阶段,简言之,查找并加载类的二进制数据,生成 Class 的实例。

在加载类时,Java 虚拟机必须完成以下 3 件事情:

  • 通过类的全名,获取类的二进制数据流(类的全名:包名 + 类名)
  • 解析类的二进制数据流为方法区内的数据结构(Java 类模型)
  • 创建 java.lang.Class 类的实例,表示该类型。作为方法区这个类的各种数据的访问入口

二进制流的获取方式

对于类的二进制数据流,虚拟机可以通过多种途径产生或获得。(只要所读取的字节码符合 JVM 规范即可)

  • 虚拟机可能通过文件系统读入一个 Class 后缀的文件 (最常见)
  • 读入 jar、zip 等归档数据包,提取类文件
  • 事先存放在数据库中的类的二进制数据
  • 使用类似于 HTTP 之类的协议通过网络进行加载
  • 在运行时生成一段 Class 的二进制信息等

在获取到类的二进制信息后,Java 虚拟机就会处理这些数据,并最终转为一个 java.lang.Class 的实例。

如果输入数据不是 ClassFile 的结构(如 Class 文件的魔数不是 ca fe ba be),则会抛出 ClassFormatError。

类模型与 Class 实例的位置

类模型的位置

加载的类在 JVM 中创建相应的类结构,类结构会存储在方法区(JDK 1.8 之前:永久代;JDK 1.8 之后:元空间))。

Class 实例的位置

类将 .class 文件加载至元空间后,会在堆中创建一个 java.lang.Class 对象,用来封装类位于方法区内的数据结构,该 Class 对象是在加载类的过程中创建的,每个类都对应有一个 Class 类型的对象。

 

外部可以通过访问代表 Order 类的 Class 对象来获取 Order 的类数据结构。

再说明

Class 类的构造方法是私有的,只有 JVM 能够创建。

java.lang.Class 实例是访问类型元数据的接口,也是实现反射的关键数据、入口。通过 Class 类提供的接口,可以获得目标类所关联的 .class 文件中具体的数据结构:方法、字段等信息。

代码示例:

ini复制代码public class LoadingTest {
    public static void main(String[] args) {
        try {
            Class clazz = Class.forName("java.lang.String");
            // 获取当前运行时类声明的所有方法
            Method[] ms = clazz.getDeclaredMethods();
            for (Method m : ms) {
                // 获取方法的修饰符
                String mod = Modifier.toString(m.getModifiers());
                System.out.print(mod + " ");
                // 获取方法的返回值类型
                String returnType = m.getReturnType().getSimpleName();
                System.out.print(returnType + " ");
                // 获取方法名
                System.out.print(m.getName() + "(");
                // 获取方法的参数列表
                Class<?>[] ps = m.getParameterTypes();
                if (ps.length == 0) System.out.print(')');
                for (int i = 0; i < ps.length; i++) {
                    char end = (i == ps.length - 1) ? ')' : ',';
                    // 获取参数的类型
                    System.out.print(ps[i].getSimpleName() + end);
                }
                System.out.println();
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

输出结果:

scss复制代码public boolean equals(Object)
public String toString()
public int hashCode()
public int compareTo(String)
public volatile int compareTo(Object)
public int indexOf(String,int)
public int indexOf(String)
public int indexOf(int,int)
public int indexOf(int)
static int indexOf(char[],int,int,char[],int,int,int)
static int indexOf(char[],int,int,String,int)
public static String valueOf(int)
public static String valueOf(long)
public static String valueOf(float)
public static String valueOf(boolean)
public static String valueOf(char[])
public static String valueOf(char[],int,int)
public static String valueOf(Object)
public static String valueOf(char)
public static String valueOf(double)
public char charAt(int)
private static void checkBounds(byte[],int,int)
public int codePointAt(int)
public int codePointBefore(int)
public int codePointCount(int,int)
public int compareToIgnoreCase(String)
public String concat(String)
public boolean contains(CharSequence)
public boolean contentEquals(CharSequence)
public boolean contentEquals(StringBuffer)
public static String copyValueOf(char[])
public static String copyValueOf(char[],int,int)
public boolean endsWith(String)
public boolean equalsIgnoreCase(String)
public static transient String format(Locale,String,Object[])
public static transient String format(String,Object[])
public void getBytes(int,int,byte[],int)
public byte[] getBytes(Charset)
public byte[] getBytes(String)
public byte[] getBytes()
public void getChars(int,int,char[],int)
 void getChars(char[],int)
private int indexOfSupplementary(int,int)
public native String intern()
public boolean isEmpty()
public static transient String join(CharSequence,CharSequence[])
public static String join(CharSequence,Iterable)
public int lastIndexOf(int)
public int lastIndexOf(String)
static int lastIndexOf(char[],int,int,String,int)
public int lastIndexOf(String,int)
public int lastIndexOf(int,int)
static int lastIndexOf(char[],int,int,char[],int,int,int)
private int lastIndexOfSupplementary(int,int)
public int length()
public boolean matches(String)
private boolean nonSyncContentEquals(AbstractStringBuilder)
public int offsetByCodePoints(int,int)
public boolean regionMatches(int,String,int,int)
public boolean regionMatches(boolean,int,String,int,int)
public String replace(char,char)
public String replace(CharSequence,CharSequence)
public String replaceAll(String,String)
public String replaceFirst(String,String)
public String[] split(String)
public String[] split(String,int)
public boolean startsWith(String,int)
public boolean startsWith(String)
public CharSequence subSequence(int,int)
public String substring(int)
public String substring(int,int)
public char[] toCharArray()
public String toLowerCase(
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值