JVM Loading 加载阶段

一 加载完成的操作

1 加载的理解

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

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

2 加载完成的操作

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

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

  • 通过类的全名,获取类的二进制数据流。

  • 解析类的二进制数据流为方法区内的数据结构(Java 类模型)

  • 创建 java.lang.Class 类的实例,表示该类型。作为方法区这个类的各种数据的访问入口。

二 二进制流的获取方式

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

  • 虚拟机可能通过文件系统读入一个 class 后缀的文件(最常见)

  • 读入 jar、zip 等归档数据包,提取类文件

  • 事先存放在数据库中的类的二进制数据

  • 使用类似于 HTTP 之类的协议通过网络进行加载

  • 在运行时生成一段 class 的二进制信息等

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

如果输入数据不是 ClassFile 的结构,则会抛出 ClassFormatError。

三 类模型与 Class 实例的位置

1 类模型的位置

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

2 Class 实例的位置

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

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

四 实战

1 代码

package chapter03.java;

import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**

* 过程一:加载阶段
*
* 通过 Class 类,获得了 java.lang.String 类的所有方法信息,并打印方法访问标识符、描述符
*/
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();
        }
    }
}

2 运行结果

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(Locale)
public String toLowerCase()
public String toUpperCase()
public String toUpperCase(Locale)
public String trim()


Process finished with exit code 0

五 数组类的加载

创建数组类的情况稍微有些特殊,因为数组类本身并不是由类加载器负责创建,而是由 JVM 在运行时根据需要而直接创建的,但数组的元素类型仍然需要依靠类加载器去创建。创建数组类(下述简称 A)的过程:

  • 如果数组的元素类型是引用类型,那么就遵循定义的加载过程递归加载和创建数组 A 的元素类型;

  • JVM 使用指定的元素类型和数组维度来创建新的数组类。

如果数组的元素类型是引用类型,数组类的可访问性就由元素类型的可访问性决定。否则数组类的可访问性将被缺省定义为 public。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值