我所知道JVM虚拟机之类的生命周期一(LoadKing加载阶段)

前言

前篇文章内容我们讲解了有关于虚拟机的布局结构、垃圾回收器、以及字节码分析等等

那么从本篇开始我们就要仔细讲讲类加载器,对于程序来说不一定是一个类,有可能还是接口,枚举类或者注解等等。这些都属于Java中的结构,我们都需要加载进来,加载过程称为类加载器

接下来我们就针对于类加载过程,以及更加详细的过程是怎么样的?

类的生命周期概述

在Java中数据类型分为:基本数据类型和引用数据类型

  • 基本数据类型:虚揶机预先定义
  • 引用数据类型:则需要进行类的加载

类的加载包括指:类、接口、注解、枚举类等等都可以称为类的加载

按照Java虚拟机规范,从class文件到加载到内存中的类,到类卸载出内存为止

它的整个生命周期包括如下7个阶段:
在这里插入图片描述
同时我们也可以称为五个阶段,其中验证、准备、解析我们称呼为链接(Linking)阶段

举例加载HelloWorld类,看看从程序中加载来看,我们可以看看使用过程是怎么样的?

在这里插入图片描述

Loading(加载阶段)

我们一起先来了解一下加载阶段做些什么事情呢?

将Java类的字节码文件加载到机器内存中,并在内存中构建出]ava类的原型(类模板对象)

所谓类模板对象就是Java类在JVM内存中的一个快照,JVMN将从字节码文件中解析出的常量池、类字段、类方法等信息存储到类模板中,这样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的结构,则会抛出ClassFormatError

类模型与Class实例的位置

对于类模型的位置指的是加载的类在JVM中创建相应的类结构

类结构会存储在方法区(JDK1.8之前:永久代,JDK1.8及之后:元空间)

对于Class实例的位置指类将.class文件加载至元空间后,会在堆中创建一个Java.lang.class对象用来封装类位于方法区内的数据结构
在这里插入图片描述
该class对象是在加载类的过程中创建的,每个类都对应有一个class类型的对象

外部可以通过访问代表order类的Class对象来获取Order的类数据结构,并且Class类的构造方法是私有的,只有JVM能够创建

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

接下来我们使用示例代码来体会一下反射获取的目标类的信息

public class LoadingTest {
	public static void main(String[] args) {
		try{
			
			Class clazz = Class.forName( "java.1ang.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.1ength; i++) {
					char end = (i == ps.length - 1) ?')':',';
					System.out.print(ps[i].getsimpleName() + end);
				}
				System.out.println();	
			}
		}cath(ClassNotFountException e){
			e.printStackTrace();
		}	
	}			
}
//运行结果如下:
public boolean equals(Object)
public String toString()
public int hashCode()
public int comparTo(String)
public volatitle int comparTo(Object)
public int indexOf(String,int)
public int indexOf(String)
public int indexOf(int,int)
public int indexOf(int)
public int indexOf(char[],int,int,char[],int,int,int)
......

此时我们就可以通过反射获取该类声明的方法,以及字段也可以

数组类的加载

创建数组类的情况稍微有些特殊,因为数组类本身并不是由类加载器负责创建

而是JVM在运行时根据需要而直接创建的,但数组的元素类型仍然需要依靠类加载器去创建

创建数组类(下述简称A)的过程:

  • 如果数组的元素类型是引用类型,那么就遵循定义的加载过程递归加载和创建数组A的元素类型
  • JVM使用指定的元素类型和数组维度来创建新的数组类

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

如果元素为String,那么遵循String类的访问性质
如果元素为基本数据类型的int,那么就直接缺省定义为public即可

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值