JVM类加载器及Java类的生命周期

预定义类加载器(三种)

启动(Bootstrap)类加载器:
  是用本地代码实现的类装入器,它负责将<Java_Runtime_Home>/lib下面的类库加载到内存中(比如rt.jar)。
由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
扩展扩展(Extension)类加载器:
  是由 Sun 的 ExtClassLoader(sun.misc.Launcher$ExtClassLoader)实现的。它负责将< Java_Runtime_Home >/lib/ext或者由系统变量java.ext.dir指定位置中的类库加载到内存中。开发者可以直接使用标准扩展类加载器。
系统(System)类加载器:

  是由 Sun 的 AppClassLoader(sun.misc.Launcher$AppClassLoader)实现的。它负责将系统类路径(CLASSPATH)中指定的类库加载到内存中。开发者可以直接使用系统类加载器。

双亲委派机制:
  某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父类加载器,依次递归,如果父类加载器可以完成类加载任务,就成功返回;只有父类加载器无法完成此加载任务时,才自己去加载。这是一种代理方式。

双亲委派意义:
  系统类防止内存中出现多份同样的字节码
  保证Java程序安全稳定运行

线程上下文类加载器(特殊):
  破坏了“双亲委派模型”,可以在执行线程中抛弃双亲委派加载链模式,使程序可以逆向使用类加载器。
类 java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)用来获取和设置线程的上下文类加载器。
如果没有通过 setContextClassLoader(ClassLoader cl)方法进行设置的话,线程将继承其父线程的上下文类加载器。Java 应用运行的初始线程的上下文类加载器是系统类加载器。在线程中运行的代码可以通过此类加载器来加载类和资源。

1.当高层提供了统一接口让低层去实现,同时又要是在高层加载(或实例化)低层的类时,必须通过线程上下文类加载器来帮助高层的ClassLoader找到并加载该类。
2.当使用本类托管类加载,然而加载本类的ClassLoader未知时,为了隔离不同的调用者,可以取调用者各自的线程上下文类加载器代为托管。

几点问题:

  启动(Bootstrap)类加载器它用来加载 Java 的核心库,是用原生代码来实现的,并不继承自 java.lang.ClassLoader。

  Java 虚拟机是如何判定两个 Java 类是相同的?

  Java 虚拟机不仅要看类的全名是否相同,还要看加载此类的类加载器是否一样。只有两者都相同的情况,才认为两个类是相同的。即便是同样的字节代码,被不同的类加载器加载之后所得到的类,也是不同的。

eg:

public void testClassIdentity() {
   String classDataRootPath = "C:\\workspace\\Classloader\\classData";
   FileSystemClassLoader fscl1 = new FileSystemClassLoader(classDataRootPath);
   FileSystemClassLoader fscl2 = new FileSystemClassLoader(classDataRootPath);
   String className = "com.example.Sample";
   try {
       Class<?> class1 = fscl1.loadClass(className);
       Object obj1 = class1.newInstance();
       Class<?> class2 = fscl2.loadClass(className);
       Object obj2 = class2.newInstance();
       Method setSampleMethod = class1.getMethod("setSample", java.lang.Object.class);
       setSampleMethod.invoke(obj1, obj2);
   } catch (Exception e) {
       e.printStackTrace();
   }
}

  代码中使用了类 FileSystemClassLoader的两个不同实例来分别加载类 com.example.Sample,得到了两个不同的 java.lang.Class的实例,接着通过 newInstance()方法分别生成了两个类的对象 obj1和 obj2,最后通过 Java 的反射 API 在对象 obj1上调用方法 setSample,试图把对象 obj2赋值给 obj1内部的 instance对象

运行结果
java.lang.reflect.InvocationTargetException
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at classloader.ClassIdentity.testClassIdentity(ClassIdentity.java:26)
at classloader.ClassIdentity.main(ClassIdentity.java:9)
Caused by: java.lang.ClassCastException: com.example.Sample
cannot be cast to com.example.Sample
at com.example.Sample.setSample(Sample.java:7)
... 6 more

  给出的运行结果可以看到,运行时抛出了 java.lang.ClassCastException异常。虽然两个对象 obj1和 obj2的类的名字相同,但是这两个类是由不同的类加载器实例来加载的,因此不被 Java 虚拟机认为是相同的。

Java类的生命周期:

  类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中准备、验证、解析3个部分统称为连接(Linking)。

加载(Loading):

  就是将源文件的class文件找到类的信息将其加载到方法区中,
然后在堆区中实例化一个java.lang.Class对象,作为方法区中这个类的信息的入口。

连接(Linking):
  验证:确定该类是否符合java语言的规范,有没有属性和行为的重复,继承是否合理,总之,就是保证jvm能够执行
  准备:主要做的就是为由static修饰的成员变量分配内存,并设置默认的初始值
    (1.八种基本数据类型默认的初始值是0
       2.引用类型默认的初始值是null
       3.有static final修饰的会直接赋值,例如:static final int x=10;则默认就是10.)
  解析:这一阶段的任务就是把常量池中的符号引用转换为直接引用,说白了就是jvm会将所有的类或接口名、字段名、方法名转换为具体的内存地址。

初始化(Initialization)
  这个阶段就是将静态变量(类变量)赋值的过程,即只有static修饰的才能被初始化,执行的顺序就是:
父类静态域或着静态代码块,然后是子类静态域或者子类静态代码块

使用(Using)
  在类的使用过程中依然存在三步:对象实例化、垃圾收集、对象终结

卸载(Unloading)
  类的生命周期走到了最后一步,程序中不再有该类的引用,该类也就会被JVM执行垃圾回收,从此生命结束。

参考文章:

https://www.ibm.com/developerworks/cn/java/j-lo-classloader/

http://blog.csdn.net/yangcheng33/article/details/52631940

原文地址:https://www.cnblogs.com/daily-note/p/8548904.html

时间: 2024-10-08 21:09:00

JVM类加载器及Java类的生命周期的相关文章

Java类的生命周期详解

引言 最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目前国内java方面的教材大多只是告诉你“怎样做”,但至于“为什么这样做”却不多说,所以造成大家在基础和原理方面的知识比较匮乏,所以笔者今天就斗胆来讲一下这个问题,权当抛砖引玉,希望对在这个问题上有疑惑的朋友有所帮助,文中有说的不对的地方,也希望各路高手前来指正. 首先来了解一下jvm(java虚拟机)中的几个比较重要的内存区域,这几

【转载】详解java类的生命周期

原文地址:http://blog.csdn.net/zhengzhb/article/details/7517213 引言 最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目前国内java方面的教材大多只是告诉你“怎样做”,但至于“为什么这样做”却不多说,所以造成大家在基础和原理方面的知识比较匮乏,所以笔者今天就斗胆来讲一下这个问题,权当抛砖引玉,希望对在这个问题上有疑惑的朋友有所帮助,

详解java类的生命周期

最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目前国内java方面的教材大多只是告诉你“怎样做”,但至于“为什么这样做”却不多说,所以造成大家在基础和原理方面的知识比较匮乏,所以笔者今天就斗胆来讲一下这个问题,权当抛砖引玉,希望对在这个问题上有疑惑的朋友有所帮助,文中有说的不对的地方,也希望各路高手前来指正.        首先来了解一下jvm(java虚拟机)中的几个比较重要的内存区

【转】Java 类的生命周期详解

一. 引 言 最近有位细心的朋友在阅读笔者的文章时,对java类的生命周期问题有一些疑惑,笔者打开百度搜了一下相关的问题,看到网上的资料很少有把这个问题讲明白的,主要是因为目前国内java方面的教材大多只是告诉你“怎样做”,但至于“为什么这样做”却不多说,所以造成大家在基础和原理方面的知识比较匮乏,所以笔者今天就斗胆来讲一下这个问题,权当抛砖引玉,希望对在这个问题上有疑惑的朋友有所帮助,文中有说的不对的地方,也希望各路高手前来指正. 首先来了解一下jvm(java虚拟机)中的几个比较重要的内存区

java类的生命周期,从装载,链接,初始化到卸载,关键是何时卸载??

卸载       关于类的卸载,笔者在单例模式讨论篇:单例模式与垃圾回收一文中有过描述,在类使用完之后,如果有下面的情况,类就会被卸载: 该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例.加载该类的ClassLoader已经被回收.该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法.        如果以上三个条件全部满足,jvm就会在方法区垃圾回收的时候对类进行卸载,类的卸载过程其实就是在方法区中清空类信息,java类的整个生

java类的生命周期_机制

类的生命周期: 在一个类编译完成之后,下一步就需要开始使用类,如果要使用一个类,肯定离不开JVM.在程序执行中JVM通过装载,链接,初始化这3个步骤完成. 类的装载是通过类加载器完成的,加载器将.class文件的二进制文件装入JVM的方法区,并且在堆区创建描述这个类的java.lang.Class对象.用来封装数据. 但是同一个类只会被类装载器装载以前 链接就是把二进制数据组装为可以运行的状态.   链接分为校验,准备,解析这3个阶段 校验一般用来确认此二进制文件是否适合当前的JVM(版本),

java类的生命周期

类的生命周期:加载.连接(验证.准备.解析).初始化.使用.卸载主动引用(有且只有)初始化: 1.new.getstatic.putstatic.invokestatic如果类没初始化,则初始化new关键字实例化对象.读取或设置一个类的静态字段(被final修饰.*已在编译期把结果放入常量池的静态字段除外).调用一个类的静态方法  2.使用java.lang.reflect包的方法对类进行发射调用的时候,如果类没有进行过初始化,则初始化 3.当初始化一个类的时候,父类没初始化,则初始化 4.当虚

JAVA类的生命周期,以及类的初始化时机

类的生命周期从类被加载.连接和初始化开始,到类被卸载结束. 只有当类处于生命周期时,java程序才能使用它,比如 调用类的静态属性和方法.或者创建类的实列 简要介绍 1:加载  类的加载时指把类的.class文件中的二进制读入到内存中,把它存放在运行时数据区的方法区内,然后在堆区创建一个java.long.Class对象用来封装类在方法区内的数据结构.并且向java程序提供了访问类在方法区内的数据结构接口. 类的加载器并不需要某个类"首次主动使用"时在加载它,java虚拟机规范允许类加

Java 类的生命周期

参考博文:http://www.ibm.com/developerworks/cn/java/j-lo-clobj-init/ 上图展示的是类生命周期流向:在本文里,我只打算谈谈类的"初始化"以及"对象实例化"两个阶段. 类初始化 类"初始化"阶段,它是一个类或接口被首次使用的前阶段中的最后一项工作,本阶段负责为类变量赋予正确的初始值. Java 编译器把所有的类变量初始化语句和类型的静态初始化器通通收集到 <clinit> 方法内,