`
messon619
  • 浏览: 45341 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

深入剖析Classloader(一)--类的主动使用与被动使用

 
阅读更多
我们知道java运行的是这样的,首先java编译器将我们的源代码编译成为字节码,然后由JVM将字节码load到内存中,接着我们的程序就可以创建对象了,我们知道JVM将字节码load到内存之后将将建立内存模型(JVM的内存模型我们将在稍后阐述),那JVM是怎么将类load到内存中的呢?对了,是通过Classloader,今天我们就来深入探讨一下Classloader。
首先我们来看一段诡异的代码(一段单实例测试代码)。
package com.yhj.jvm.classloader;
/**
* @Description:单例初始化探究
* @Author YHJ  create at 2011-6-4 下午08:31:19
* @FileName com.yhj.jvm.classloader.ClassLoaderTest.java
*/
class Singleton{
  
    private static Singleton singleton=new Singleton();
    private static int counter1;
    private static int counter2 = 0;
  
    public Singleton() {
       counter1++;
       counter2++;
    }
    public static int getCounter1() {
       return counter1;
    }
    public static int getCounter2() {
       return counter2;
    }
    /**
     * @Description:实例化
     * @return
     * @author YHJ create at 2011-6-4 下午08:34:43
     */
    public static Singleton getInstance(){
       return singleton;
    }
  
}
/**
* @Description: 测试启动类
* @Author YHJ  create at 2011-6-4 下午08:35:13
* @FileName com.yhj.jvm.classloader.ClassLoaderTest.java
*/
public class ClassLoaderTest {
    /**
     * @Description:启动类
     * @param args
     * @author YHJ create at 2011-6-4 下午08:30:12
     */
    @SuppressWarnings("static-access")
    public static void main(String[] args) {
       Singleton singleton=Singleton.getInstance();
       System.out.println("counter1:"+singleton.getCounter1());
       System.out.println("counter2:"+singleton.getCounter2());

    }

}
我们先猜测一下运行结果
然后我们再来调换一下单实例生成的顺序,将
    private static Singleton singleton=new Singleton();
    private static int counter1;
    private static int counter2 = 0;
修改为
    private static int counter1;
    private static int counter2 = 0;
    private static Singleton singleton=new Singleton();
再猜测一下结果,然后运行一下,看和你的猜测一致不?(是不是感觉很诡异)
好吧,我们先不看这段程序,先介绍相关的内容,等介绍完了你就明白这段诡异的代码为什么这么执行了!
我们知道我们运行刚才这段java程序是通过执行ClassLoaderTest的main函数引导起来的,而当我们执行完2个打印语句之后,JVM就停止了运行。这就是我们程序的生命周期。
在以下几种情况下JVM将结束自己的生命周期
1.         执行了System.exit()方法(具体可参见JDK的API文档)
2.         程序正常执行结束
3.         程序在执行过程中遇到了错误或异常而异常终止
4.         由于操作系统出现错误而导致JVM进程终止
类通过JVM的Classloader加载到内存经过以下几个步骤
加载 --> 连接 --> 初始化
?加载:查找并加载类的二进制数据
?连接
1.         验证:确保被加载的类的正确性
2.         准备:为类的静态变量分配内存,并将其初始化为默认值
3.         解析:把类中的符号引用转换为直接引用
?初始化:为类的静态变量赋予正确的初始值
我来分别解释一下这三个阶段都做了什么事情
1.         加载就是将二进制的字节码通过IO输入到JVM中,我们的字节码是存在于硬盘上面的,而所用的类都必须加载到内存中才能运行起来,加载就是通过IO把字节码从硬盘迁移到内存中。
2.         连接分为3个阶段,验证,准备和解析。
1)         验证这里可能大家会疑问了,我们的类不是通过JVM编译成的字节码的吗,为什么这里还要验证加载类的正确性,难道通过Java虚拟机的javac编译器生成的字节码还会有错误不成?当然,javac编译出来的类都是正确的,但是如果是通过其他途径生成的字节码呢?是不是正确的呢?就比如你自己建一个文本文件,然后重命名该文件为Test.class,然后让JVM来运行这个类,显然是错误的。当然因为JDK的源码是开放的,所以JVM字节码的生成规则也是公开的,所以也有一些第三方的软件可以生成符合JVM规范的字节码文件,如CGlib。
2)         准备:为类的静态变量分配内存,并将其初始化为默认值,这里我们一定要看清楚是为静态变量分配内存,而不是我们的实例变量,为什么我要强调静态变量,因为实例变量是什么时候产生的,是生成实例的时候产生的,而我们一般是在new一个对象的时候才对这个类进行实例化(前提是这个类已经被加载),而我们现在还没有加载完类,所以这个时候只能对静态变量分配内存空间(静态变量是属于这个类的而不属于某个对象),这个一定要分清楚。然后为该静态变量初始化为默认值(这个大家应该不陌生,int类型是0,boolean就是false,引用类型是null等)。
3)         解析:把类中的符号引用转换为直接引用,这个我们等下在讨论(后面我们会讲什么是符号引用,什么是直接引用)
3.         初始化:这个似乎与上面的初始化为默认值有点矛盾,我们再看一遍:为累的静态变量赋予正确的初始值,上面是赋予默认值,这里是赋予正确的初始值,什么是正确的初始值,就是用户给赋予的值。我们来看一个例子
class Test{
private static int a = 1;
}
我们知道,这个类加载好之后,a的值就是1,但实际是这样子的,类在加载的连接阶段,将a初始化为默认值0(int的默认值是0),然后在初始化阶段将a的值赋予为正确的初始值1. 我们看到最终a的值是等于1,但是实际的运行中是有一个将0赋予a的过程,这个过程放生在连接的准备阶段。类的初始化还有另外的一种形式,代码如下
class Test{
private static int a ;
static{
a=1;
}
}
这里强调一点,这个时候还是没有类的实例生成的,这点一定要注意!
《深入java虚拟机第二版》里面有一个图阐述了对应的关系,如下

Java
程序对类的使用方式可分为2种,主动使用和被动使用。所有的Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用时才初始化他们。”
主动使用(六种)
1)   –创建类的实例 (如new Integer())
2)   –访问某个类或接口的静态变量,或者对该静态变量赋值 (读写静态变量)
3)   –调用类的静态方法
4)   –反射
(如Class.forName(“com.yhj.jvm.classloader.ClassLoaderTest”))
5)   –初始化一个类的子类 (初始化子类的过程中会主动使用父类的构造方法)
6)   –Java虚拟机启动时被标明为启动类的类(含有main方法并且是启动方法的类)
除了以上六种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化 (除了上述6种情况以外,都不会执行初始化,只会执行加载和连接)
好了,讲到这里我们大概知道类加载的几个步骤,那我们现在来详细的介绍一下类加载这个过程中的一些细节!
类的加载:累的加载是指将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区里面(具体的JVM内存模型我们会在后面讲到,这里可以参考下面JVM的内存模型图),然后在堆区创建一个java.lang.Class的对象,用于封装类在方法区内的数据结构!我们知道我们对于一个类可以创建很多个对象,但是这些对象共享同样的数据结构,而这个数据结构就是在加在过程中创建的这个class对象。我们可以通过 类名.class或者对象名.getClass()获取这个对象!无论创建了多少个实例对象,这个class的对象始终只有一个,类里面所有的结构都可以通过class对象获取,因此class对象就像一面镜子一样,可以反射一个类的内存结构,因此class是整个反射的入口!通过class对象我们可以反射的获取某个对象的数据结构,访问对应数据结构中的数据!
 
JVM
内存模型
《深入java虚拟机第二版》上面一个实例描述了一个类在加载过程中的内存模型,如下

加载
.class文件有几种方式
1.   –从本地系统中直接加载 (直接加载本地硬盘上的.class文件加载)
2.   –通过网络下载.class文件 (通过java.net.URLClassLoader加载网络上的某个.class文件)
3.   –从zip,jar等归档文件中加载.class文件 (引入外部zip、jar包)
4.   –从专有数据库中提取.class文件 (不常用)
5.   –将Java源文件动态编译为.class文件 (动态代理)
分享到:
评论

相关推荐

    j-classloader-ltr

    ### Java ClassLoader 知识点解析 #### 一、引言 在《classloader教程 --- from ...总之,《classloader教程 --- from IBM》是一篇非常全面且实用的教程,对于希望深入了解Java类加载机制的开发者来说,绝对值得一读。

    xwiki-commons-classloader-protocol-jar-5.4.zip

    《XWiki Commons Classloader Protocol Jar 5.4与LaZyWorker开源项目解析》 XWiki Commons Classloader Protocol Jar 5.4是一个重要的组件,它属于XWiki开源项目的一部分。XWiki是一个强大的、可扩展的、面向企业的...

    java classloader讲义-淘宝网

    1. **继承`ClassLoader`类**:创建一个新的类,继承自`java.lang.ClassLoader`。 2. **重写`findClass()`方法**:实现具体的加载逻辑,读取类的二进制数据。 3. **调用`defineClass()`方法**:将读取到的二进制数据...

    【IT十八掌徐培成】Java基础第25天-04.classLoader-系统资源-不可见类访问.zip

    在“【IT十八掌徐培成】Java基础第25天-04.classLoader-系统资源-不可见类访问.zip”这个课程中,徐培成老师将深入讲解ClassLoader的工作原理以及如何处理系统资源,特别是关于不可见类访问的话题。 首先,...

    classloader-playground, 一个简单的java依赖隔离容器类.zip

    《深入理解Java类加载器:基于classloader-playground开源项目》 在Java世界里,类加载器(ClassLoader)是理解JVM工作原理的关键一环。它负责将字节码文件(.class)从磁盘、网络或其他数据源加载到内存,并转化为...

    javase源码-classloader-leak-prevention:ClassLoader泄漏预防/保护

    >se.jiderhamn.classloader-leak-prevention</ groupId > < artifactId >classloader-leak-prevention-servlet3</ artifactId > < version >2.7.0</ version > </ dependency > 如果您遇到 ...

    深入理解ClassLoader工作机制.docx

    虚拟机规范定义了五种主动引用,其他情况则视为被动引用,不会立即触发初始化。 例如,通过子类引用父类的静态字段只会初始化父类,而不会初始化子类。同样,通过数组定义引用类不会触发类的初始化,但可能会触发类...

    ClassLoader 详解.doc

    深入理解ClassLoader的工作原理对于优化应用性能、解决类加载问题以及实现自定义加载策略至关重要。 首先,JVM启动时,会构建一个类加载器的层次结构,主要包括三个基本类加载器: 1. Bootstrap ClassLoader:引导...

    classloader-study.zip

    在"**classloader-study.zip**"中,你可能会看到如何创建自定义`ClassLoader`的代码示例,以及如何使用`URLClassLoader`从网络或其他位置加载类。此外,还可能包含关于如何管理类加载的生命周期,避免类加载冲突,...

    轻量级的ndk实用程序,可帮助绕过Android N的classloader-namespace限制-Android开发

    轻量级ndk实用程序,可帮助绕过Android N的classloader-namespace限制ndk_dlopen轻量级ndk实用程序,可帮助绕过Android N的classloader-namespace限制技术OSR(堆栈替换)支持x86,x86_64,armeabi-v7a(拇指和手臂...

    restx-classloader-0.33.1.zip

    【标题】"restx-classloader-0.33.1.zip" 提供的是 Restx ClassLoader 的一个版本,这是 Restx 框架的一部分,主要负责处理类加载相关的任务。Restx 是一个轻量级、模块化的 Java web 应用框架,它允许开发者快速...

    自定义classloader的使用

    本文将深入探讨自定义Classloader的使用。 一、Classloader的工作原理 Java的类加载机制遵循双亲委派模型,即当一个类加载器需要加载类时,它首先委托父类加载器尝试加载,只有当父类加载器无法加载时,才会尝试...

    【图解版】深入分析ClassLoader类加载工作机制

    【图解版】深入分析ClassLoader类加载工作机制,从原理到JVM的装载过程,详情分析了ClassLoader加载类以及自定义类加载器的过程,不可用于商业用途,如有版权问题,请联系删除!

    ClassLoader类加载机制和原理详解

    本文将深入探讨ClassLoader的工作原理和类加载机制,帮助开发者理解这个至关重要的概念。 1. 类加载机制概述 Java的类加载机制遵循“双亲委派模型”(Delegation Model)。当一个类被加载时,它首先会尝试由当前...

    深入Java虚拟机-ClassLoader.pptx

    Java程序对类的使用分为主动使用和被动使用。只有在六种特定情况下,类才会被初始化:创建类实例、访问或修改静态变量、调用静态方法、反射操作、初始化子类以及JVM启动时指定的启动类。其他情况被视为被动使用,...

    《深入剖析 Tomcat》PDF版本下载.txt

    根据提供的文件信息,本文将对《深入剖析 Tomcat》这一资料进行详细的知识点解析。Tomcat作为一款开源的Servlet容器,被广泛应用于Java Web应用程序的部署与运行环境中。本资料旨在帮助读者深入了解Tomcat的工作原理...

    ClassLoader小例子

    在Java编程语言中,ClassLoader是一个至关重要的组成部分,它负责加载类到JVM(Java虚拟机)中,使得程序能够执行。本示例"ClassLoader小例子"将深入探讨这个概念,并通过一个具体的程序来演示其工作原理。下面我们...

Global site tag (gtag.js) - Google Analytics