学习Java类加载机制

加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)、卸载(unloading)

类的生命周期

下面主要详细的讲类加载的过程,也就是加载、验证、准备、解析和初始化这5个阶段所执行的具体动作。

加载

“加载”是"类加载"过程的一个阶段,在加载阶段虚拟机需要完成三件事情:

  1. 通过一个类的全限定名来定义这个类二进制字节流
  2. 将这个字节流所代表的静态存储结构转化为方法区的运行数据结构
  3. 在内存中生成一个代表这个类的java.lang.class对象,作为方法区这个类的各个数据访问入口

验证

验证是连接阶段的第一步,这一阶段的目的是为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。

准备

准备阶段是正式为类变量分配内存并设置类变量(被static修饰的变量,而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。)初始值的阶段,这些变量所使用的内存 都将在方法区中进行分配。
需要说明的一点事,这里所说的初始值“通常情况”下是数据类型的零值,
例如:

 public static int value=123;

在准备阶段过后的初始化value是0 而不是123,因为这个时候尚未执行Java方法,而把value赋值为123的putstatic指令是程序被编译后,存放在类构造器方法中的,所以把value赋值123的动作将在初始化阶段才会执行。

解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,类或接口的解析、字段解析、类方法解析、接口方法解析

初始化

在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源,或者可以从另外一个角度来表达:初始化阶段是执行类构造器<clinit>()方法的过程。
<clinit>()方法是由编译器自动收集类中的所有变量的赋值动作和静态语句块(static{}块)中的语句合并产生的,编译器收集的顺序是由语句在源文件中出现的顺序所决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问;

static {
    i = 0;//编译时可以赋值
    System.out.println(i);/编译时提示Illegl forward reference
}
static int i=1;

<clinit>()方法与类的构造函数或者说实例构造器<init>()方法不同,它不需要显示的调用父类构造器,虚拟机会保证在子类的<clinit>()方法执行之前,父类的<clinit>()方法已经执行完毕。因此在虚拟机中第一个被执行的<clinit>()方法的类肯定是java.lang.object。

由于父类的<clinit>()方法先执行,也就意味着父类中定义的静态语句块优先于子类的变量赋值操作

类加载器

在类加载阶段中提到——“通过一个类的全限定名来定义这个类二进制字节流”,这个动作是放在Java虚拟机外部实现的,以便程序自己决定如何去获取所需要的类,实现这一动作的代码模块就是"类加载器"。
类加载器的作用
类加载器虽然说只是用于类的加载,但还起到另一层作用——通过类加载器和这个类本身一同确定其在Java虚拟机的中唯一性,每个类加载器也有一个独立的类空间。换一句话说就,比较两个类是否相等,不只是这两个类是否是是来自同一个class文件,还要由同一个类加载器来完成加载过程,否则这两个类不相等。

下面通过一段代码来演示这一过程:

  public static void main(String[]args)throws Exception {
    ClassLoader myLoader = new ClassLoader() {
        @Override
        public Class<?> loadClass(String name) throws ClassNotFoundException {
            try {
                String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class";
                InputStream is = getClass().getResourceAsStream(fileName);
                if (is == null) {
                    return super.loadClass(name);
                }
                byte[] b = new byte[is.available()];
                is.read(b);
                return defineClass(name, b, 0, b.length);
            } catch (IOException e) {
                throw new ClassNotFoundException(name);
            }
        }
    };
    Object obj = myLoader.loadClass("com.captain.wds.classLoad.ClassLoadTest").newInstance();
    System.out.println(obj.getClass());
    ClassLoader classLoader = ClassLoadTest.class.getClassLoader();
    System.out.println("classLoader  :" + classLoader.toString());

    System.out.println("myLoader  :" + myLoader.toString());
    System.out.println(obj instanceof com.captain.wds.classLoad.ClassLoadTest);
}

打印结果:

class com.captain.wds.classLoad.ClassLoadTest
classLoader  :sun.misc.Launcher$AppClassLoader@74a14482
myLoader  :com.captain.wds.classLoad.ClassLoadTest$1@330bedb4
false

可以发现,虽然是同一个类,但通过instanceof关键字的结果是false,一个是通过系统应用程序的类加载器加载而成,另一个是我们自定义的类加载器,虽然都是来之同一个class文件,得到的对象却不同等。

双亲委派模型

从Java虚拟机的角度上讲,只存在两种不同的类加载器:一种启动类加载器,是虚拟机的一部分,由C/C++来实现的:另一类就是其他所有类加载器,这类加载器独立于虚拟机,由Java代码实现,并且全部继承至Java.lang.ClassLoader。


类加载器双亲委派模型

类加载器之间的这种层次关系,称为类加载器的双亲委派模型(ParentsDelegation Model)。双亲委派模型要求除了顶层的启动类加载器外,其余的类加载器都应当有自己的父类加载器。这里类加载器之间的父子关系一般不会以继承(Inheritance)的关系来实现,而是都使用组合(Composition)关系来复用父加载器的代码。
双亲委派模型的工作过程是:如果一个类加载器收到了类加载的请求,它首先不会自己去加载,而是把这个请求委派给父类加载器完成,依次类推,每个类加载请求都会传递到顶层的启动类加载中,只有当父类加载器无法完成这个类加载请求时,子类加载器才会尝试自己去加载。

在java.lang.ClassLoader这个类中,实现双亲委派的主要代码也相当简单,先检查是否已经被加载过,若没有加载则调用父加载器的loadClass()方法,若父加载器为空则默认使用启动类加载器作为父加载器。如果父类加载失败,抛出ClassNotFoundException异常后,再调用自己findClass()方法进行加载。

protected Class<?> loadClass(String name, boolean resolve)
    throws ClassNotFoundException
{
        // First, check if the class has already been loaded
        //首先先检查这个类是否已经被加载
        Class<?> c = findLoadedClass(name);
        if (c == null) {
            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  
                //如果父类加载器抛出ClassNotFoundException 
                //说明父类加载器无法完成这个加载请求
            }

            if (c == null) {
                // If still not found, then invoke findClass in order
                // to find the class.
                //在父类加载器无法完成的情况下,调用自身的findClass来进行加载请求
                c = findClass(name);
            }
        }
        return c;
}

好了关于类加载机制的内容就学习到这了😄😄😄


风后面是风,天空上面是天空,而你的生活可以与众不同

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容