java 类的加载过程

ClassLoader的主要职责就是负责各种class文件到jvm中,ClassLoader是一个抽象的class,给定一个class文件的二进制名,ClassLoader会尝试加载并且在jvm中生成构建这个类的各个数据结构,然后使其分布在对应的内存区域中。

  1. 1类的加载过程简介

类的记载过程一般分为三个比较大的阶段,分别是加载阶段,连接阶段和初始化阶段,如下图所示

加载阶段:主要负责查找并且加载类的二进制数据文件,其实就是class文件。

连接阶段:连接阶段所做的工作比较多,细分的话还可以分为如下三个阶段。

  • 验证:主要是确保类文件的正确性,比如class文件的版本,class文件的魔术因子是否正确。
  • 准备:为类的静态变量分配内存,并且为其初始化默认值。
  • 解析:把类中的符号引用转换为直接引用。

初始化阶段:为类的静态变量赋予正确的初始值(代码编写阶段给定的值)

jvm对类的初始化是一个延迟的机制,即:使用的是lazy的方式,当一个类在首次使用的时候才会被初始化,在同一个运行时包下,一个class只会被初始化一次

(运行时包和类的包时有区别的,下次再说),那么什么是类的主动使用和被动使用呢?

  1. 2 类的主动使用和被动使用

jvm虚拟机规范规定了,每个类或者接口被java程序首次主动使用时才会对其进行初始化,当然随着JIT技术越来越成熟,JVM运行期间的编译也越来越只能,

不排除JVM在运行期间提前预判并且初始化某个类。

JVM同时规范了以下6种主动使用类的场景,具体如下

  • 通过new关键字会导致类的初始化:这种是大家经常采用的初始化一个类的方式,它肯定会导致类的加载并且最终初始化。
  • 访问类的静态变量,包括读取和更新会导致类的初始化,这种情况的示例代码如下:

    public class SimpleOne {
    	static{
    		System.out.println("我会被初始化");
    	}
    	public static int x = 10;
    
    }  

这段代码中x是一个简单的静态变量,其他类即使不对SimpleOne进行new的创建,直接访问x也会导致类的初始化。

  • 访问类的静态方法,会导致类的初始化,这种情况的示例代码如下:
public class SimpleTwo {
	static{
		System.out.println("我会被初始化");
	}
	// 静态方法
	public static void test(){
	}
}

  同样,在其他类中直接调用test静态方法也会导致类的初始化。

  • 对某个类进行反射操作,会导致类的初始化,这种情况的示例代码如下:
public class InvokeClass {

	public static void main(String[] args) {
		try{
			Class.forName("com.lanlei.classLoader.SimpleOne");
		}catch(ClassNotFoundException e){

		}
	}

}

  运行上面的代码,同样会看到静态代码块中的输出语句执行。

  • 初始化子类会导致父类的初始化,这种情况的示例代码如下:
public class Parent {
	static{
		System.out.println("父类初始化了");
	}

	public static int y = 100;
}

  

public class Child extends Parent{
	static{
		System.out.println("子类会被初始化");
	}
	public static int x = 10;
}

  

public class ActiveLoadTest {

	public static void main(String[] args) {
		System.out.println(Child.x);
	}

}

在ActiveLoadTest中,我们调用了Child的静态变量,根据前面的知识可以得出Chid类被初始化了,Child类又是Parent类的子类,子类的初始化会进一步导致

父类的初始化,当然这里需要注意的一点是,通过子类使用父类的静态变量只会导致父类的初始化,子类则不会被初始化,示例代码如下:

public class ActiveLoadTest {

	public static void main(String[] args) {
		System.out.println(Child.y);
	}

}

改写后的ActiveLoadTest,直接用Child访问子类的静态变量y,并不会导致Child的初始化,仅仅会导致Parent的初始化。

  • 启动类:也就是执行main函数所在的类会导致该类的初始化,比如使用java命令运行上下文中的ActiveLoadTest类。

除了上述6种情况,其余的都被称为被动引用,不会导致类的加载和初始化。  

  1. 3 类的加载过程详解

在正式讲解类的各个阶段的内容之前,请大家思考下面这段程序的输出结果,如果你不能准确计算出结果或者感觉有点模棱两可,那么请认真看完本小节。

public class Singleton {

	//  ①
	private static int x =0;
	private static int y;

	private static Singleton instance = new Singleton(); // ②

	private Singleton(){
		x++;
		y++;
	}

	public static Singleton getInstance(){
		return instance;
	}
	public static void main(String[] args) {
		Singleton singleton = Singleton.getInstance();
		System.out.println(singleton.x);
		System.out.println(singleton.y);
	}

}

  运行上面的程序代码输出将是多少?如果将注释②的代码移到注释①的位置,输出结果又是什么呢?两种输出会产生不一样的结果,为何会发生这样的

现象,下面就看下本小节寻找答案。

  1. 3.1 类的加载阶段

简单来说,类的加载就是将class文件中的二进制数据读取到内存中,然后将该字节流所代表的静态存储结构转换为方法区中运行时的数据结构,并且在堆内存总

生成一个该类的java.lang.Class对象,作为访问方法区数据结构的入口,如下图所示。

类加载的最终产物就是堆内存中的class对象,对同一个ClassLoader来讲,不管某个类被加载了多少次,对应到堆内存中的class对象始终是同一个。虚拟机

规范中指出了类的加载是通过一个全限定名(包名+类名)来获取二进制数据流,但是并没有限定必须通过某种方式获得,比如我们常见的二进制文件的形式,

但是除此之外还会有如下的几种形式。

  • 运行时动态生成,比如通过开源的ASM包可以生成一些class,或者通过动态代理java.lang.Proxy也可以生成代理类的二进制字节流。
  • 通过网络获取,比如很早之前的Applet小程序,以及RMI动态发布等。
  • 通过读取zip文件获得类的二进制字节流,比如jar、war(其实,jar和war使用的是和zip同样的压缩算法)。
  • 将类的二进制数据存储在数据库的BLOB字段类型中。
  • 运行时生成class文件,并且动态加载,比如使用Thrift,AVRO等都是可以在运行时将某个Schema文件生成对应的若干个class文件,然后进行加载。
  1. 3.2类的连接阶段

类的连接阶段可以细分为三个小的过程,分别为验证,准备,解析。

  • 验证

先写到这,改天补充,有点事

原文地址:https://www.cnblogs.com/pb13/p/11371997.html

时间: 2024-10-25 06:36:48

java 类的加载过程的相关文章

JAVA类的加载过程

周末闲来无事,做个小例子去看一下JAVA代码初始化的过程 JAVA代码初始化分为两个步骤:类初始化,对象初始化. 类初始化 1 类初始化是指类加载器将类加载到内存时,对类成员的初始化过程(其中包括static修饰的变量). 2 对于加载完的类,它的类变量都会赋一个默认值,即使你定义时就赋值了. 3 例如int类型就是0,引用类型就是null. 对象初始化 1 其实和类初始化差不多,但是他通过构造函数对对象进行了赋值 其实简单来说 类初始化做了2件事情:1 把所有属性全部赋值为0或者NULL,2

Java类的加载、链接和初始化

一.Java的类加载机制回顾与总结: 我们知道一个Java类要想运行,必须由jvm将其装载到内存中才能运行,装载的目的就是把Java字节代码转换成JVM中的java.lang.Class类的对象.这样Java就可以对该对象进行一系列操作,装载过程有两个比较重要的特征:层次组织结构和代理模式.层次组织结构指的是每个类加载器都有一个父类加载器,通过getParent()方法可以获取到.类加载器通过这种父亲-后代的方式组织在一起,形成树状层次结构.代理模式则指的是一个类加载器既可以自己完成Java类的

java 反射,类的加载过程以及Classloader类加载器

首先自定义一个类Person package reflection; public class Person { private String name; public int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int a

jvm系列(一):java类的加载机制

java类的加载机制 原文:http://www.cnblogs.com/ityouknow/p/5603287.html 1.什么是类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对象,用来封装类在方法区内的数据结构.类的加载的最终产品是位于堆区中的Class对象,Class对象封装了类在方法区内的数据结构,并且向Java程序员提供了访问方法区内的数据结构的接口. 类加载器并不需要等到某个

【Java基础】Java类的加载和对象创建流程的详细分析

相信我们在面试Java的时候总会有一些公司要做笔试题目的,而Java类的加载和对象创建流程的知识点也是常见的题目之一.接下来通过实例详细的分析一下. 实例问题 实例代码 Parent类 1 package mytest.javaBase; 2 3 public class Parent { 4 int a = 10; 5 static int b = 11; 6 // 静态代码块 7 static { 8 System.out.println("Parent静态代码块:b=" + b)

Java类的加载 链接 初始化

原文地址 Java类的加载.链接和初始化.Java字节代码的表现形式是字节数组(byte[]),而Java类在JVM中的表现形式是java.lang.Class类的对象.一个Java类从字节代码到能够在JVM中被使用,需要经过加载.链接和初始化这三个步骤.这三个步骤中,对开发人员直接可见的是Java类的加载,通过使用Java类加载器(class loader)可以在运行时刻动态的加载一个Java类:而链接和初始化则是在使用Java类之前会发生的动作.本文会详细介绍Java类的加载.链接和初始化的

跟王老师学反射(二):Java类的加载、连接和初始化

跟王老师学反射(二):Java类的加载.连接和初始化 主讲教师:王少华   QQ群号:483773664 学习内容: 了解类的加载.连接和初始化 一.类的生命周期 当我们编写一个java的源文件后,经过编译会生成一个后缀名为class的文件,这种文件叫做字节码文件,只有这种字节码文件才能够在java虚拟机中运行,java类的生命周期就是指一个class文件从加载到卸载的全过程.一个java类的完整的生命周期会经历加载.连接.初始化.使用.和卸载五个阶段,当然也有在加载或者连接之后没有被初始化就直

JVM中java类的加载时机(转载:http://blog.csdn.net/chenleixing/article/details/47099725)

Java虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的加载机制.类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括了:加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(Initialization).使用(using).和卸载(Unloading)七个阶段.其中验证.准备和解析三个部分统称为连接(Linki

java 类的加载及反射机制

一,类的加载,连接,初始化 一个类被加载到JVM需要三个步骤:加载,链接,初始化 1,先说下加载过程 2,连接 注意连接过程分为三个阶段,验证,准备,解析 3,初始化 这里注意:类的加载过程,先加载静态代码块,其次是代码块,然后是构造函数 静态成员之间级别一样,因此谁在前,谁最先被加载 二,反射机制 1,先理解下反射 2,为什么要使用反射 可能以上叙述依然很抽象,下面我们用具题代码说明 在开始代码之前我们要先,明白一个对象 java.lang.Class 我们可以这样想,java程序在运行前,会