读《深入理解Java虚拟机》有感——第二部分:虚拟机类加载机制

一、类加载过程

执行时机:编译程序——>执行程序(JVM启动、程序运行),类加载发生在程序运行期间


各个阶段:分为加载阶段、连接阶段(验证、准备、解析)、初始化、使用、卸载


执行顺序:大体是按以上阶段依次执行,但相互间有交叉

                     加载——>验证(文件格式)——>继续加载——>解析——>验证(元数据、字节码)——>准备——>初始化


参与角色:Class文件、Java虚拟机、类加载器

 /**HotSpot的Bootstrap ClassLoader(启动类加载器)是位于虚拟机内部(由C++实现),其它类加载器外置于JVM(由Java实现)*/

二、说明—各个阶段

加载阶段:

普通类/接口、 数组类(组件类型为基本类型,如int[][]):获取、转化、创建、触发

获取——类加载器加载Class文件(指定路径+文件名 ——>确定“全限定名称”——>拿到Class文件(与平台无关的二进制字节流))

转化——字节码以一定转化成格式,存放在方法区

创建——方法区中生成一个代表这个类的java.lang.Class对象

触发——加载的同时,会触发父类、关联引用类的加载

class A extends B————>Class<B>

private Class<Person> class————>Class<Person>

Class c =Person.getClass(); ————>Class<Person>

1.关于“类加载器”和“生成的这个Class对象”:       1)类加载器              Bootstrap Loader(启动类加载器)、Extended Loader(标准扩展类加载器ExtClassLoader)、AppClass Loader(系统类加载器/应用程序类加载器AppClassLoader)                   启动类加载器:                         目的:加载java_home\lib目录下的字节码文件(如:rt.jar,含有java.lang.Object等基本类)  具体有哪些文件及加载顺序?                          方式:加载System.getProperty("sun.boot.class.path")所指定的路径或jar,在使用Java运行程序时,也可以指定其搜索路径,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld                                 参考:http://www.cnblogs.com/ITtangtang/p/3978102.htm
               标准扩展类加载器:                         目的:加载java_home\lib\ext目录下的字节码文件
                         方式:加载System.getProperty("java.ext.dirs")所指定的路径或jar。在使用Java运行程序时,也可以指定其搜索路径,例如:java -Djava.ext.dirs=d:\projects\testproj\classes HelloWorld                         结果:<实现类>[email protected]————————<继承关系>ClassLoader>URLClassLoader>AppClassLoader
               应用程序类加载器:                         方式:加载System.getProperty("java.class.path")所指定的路径或jar。在使用Java运行程序时,也可以加上-cp来覆盖原有的Classpath设置,例如: java -cp ./lavasoft/classesHelloWorld                         结果:<实现类>[email protected]————————<继承关系>ClassLoader>URLClassLoader>AppClassLoader

                  自定义类加载器:                         方式:1)继承java.lang.ClassLoader并重写loadClass方法;2)继承java.lang.ClassLoader并重写findClass方法/**JDK1.2后推荐,原因见下方红色部分*/;                         相关:  
 1 <一>java.lang.Object
 2 1.getClass()
 3 public final native Class<?> getClass();    //拿到运行时的Class对象【通过本地方法】
 4 /**
 5  *例子:class-Test>main>
 6  *                    Class c =Person.getClass();
 7  */
 8      /**
 9        *实现:
10        *1)虚拟机启动
11        *2)虚拟机类加载——Test.class————加载阶段:方法区>外部接口>new java.lang.Class  //Class<?>
12        *3)虚拟机类加载——java.lang.Object————【Test加载阶段】触发
13        *3)虚拟机类加载——Person.class    ————【Test加载阶段】触发
14        *4)应用程序启动
15        *5)调用java.lang.ClassLoader>xxx1()、xxx2().....——返回运行时<Person>Class对象
16        */
17
18
19 <二>java。lang.ClassLoader
20       1.loadClass(String name, boolean resolve)  /**加载指定名称(包括包名)的二进制类型,同时指定是否解析*/
21         loadClass(String name)
22     protected Class<?> findClass(String name) throws ClassNotFoundException {   //空方法
23         throw new ClassNotFoundException(name);
24     }
25     protected Class<?> loadClass(String name, boolean resolve)    //拿到类加载器【通过本地方法】
26         throws ClassNotFoundException
27     {
28         synchronized (getClassLoadingLock(name)) {
29             // First, check if the class has already been loaded
30             Class c = findLoadedClass(name);
31             if (c == null) {
32                 long t0 = System.nanoTime();
33                 try {
34                     if (parent != null) {
35                         c = parent.loadClass(name, false);
36                     } else {
37                         c = findBootstrapClassOrNull(name);
38                     }
39                 } catch (ClassNotFoundException e) {
40                     // ClassNotFoundException thrown if class not found
41                     // from the non-null parent class loader
42                 }
43
44                 if (c == null) {
45                     // If still not found, then invoke findClass in order
46                     // to find the class.
47                     long t1 = System.nanoTime();
48                     c = findClass(name);
49
50                     // this is the defining class loader; record the stats
51                     sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
52                     sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
53                     sun.misc.PerfCounter.getFindClasses().increment();
54                 }
55             }
56             if (resolve) {
57                 resolveClass(c);
58             }
59             return c;
60         }
61     }
62
63
64
65
66 <三>java.lang.Class
67      1.getClassLoader()
68 /**例子:class-Test>main>
69   *                    Object o =new Object();
70   *                       System.out.println(o.getClass().getClassLoader());
71                                                                    结果>java.lang.NullPointerException
72   */
73      /**
74       *Returns the class loader for the class.
75       *This method will return null in such implementations if this class was loaded by the bootstrap class loader.
76       *如果没有指定classLoader就默认返回bootstrap classLoader(启动类加载器),因为这个bootstrap classLoader
77       *用户拿不到实例,所以返回null表示返回的是bootstrap classLoader
78       */
79 native ClassLoader getClassLoader0();      //拿到类加载器【通过本地方法】
80 public ClassLoader getClassLoader() {
81         ClassLoader cl = getClassLoader0();
82         if (cl == null)
83             return null;
84         SecurityManager sm = System.getSecurityManager();
85         if (sm != null) {
86             ClassLoader.checkClassLoaderPermission(cl, Reflection.getCallerClass());
87         }
88         return cl;
89     }         2.指定类加载器加载类           Class.forName(name, initialize, loader)
       2)与“生成的Class对象”关系              同一Class文件只有被同一类加载器加载,才能判断为相等————>相等的意义在于:1)不相等,会生成多个Class对象;相等,只会生成一个Class对象                                                                             2) 只有同一个Class对象,equals()、isAssignabaleFrom()、instanceof、isInstance()返回结果才相同;
       3)双亲委派模型                                          关系?                              上一级持有下一级的一个引用,属于 has A关系                          分工?(为什么叫双亲委派)                              一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给上一层的类加载器去完成。只有当上级加载器在搜索范围找不到所需类,无法完成这个加载请求时,下级加载器才会尝试自己去加载                           好处?                              Java类随着它的类加载器一起具备了一种优先级的层次关系


2.加载“类/接口”的策略:         非数组类/接口、数组类(组件类型为基本类型,如int[][]):               加载生成代表这个类的java.lang.Class对象后,将在类加载器的“类名称空间”上标识为“已加载”(因为前面已经讨论了,同一Class文件对应同一个类加载器才确定生成的是同一个Class对象)

数组类(组件类型为引用类型,如Person[][])、非数组类/接口:

递归加载组件类型,每次去掉一个维度


连接阶段:

验证——文件格式、元数据、字节码

准备——在方法区为类变量分配内存并初始化

例子                     编译时期                                                             (类加载时)验证阶段—准备时期

static int i =20;             产生constantValue属性,但不会存入常量20                       常规方式进行准备:初始化、分配内存

结果—————>        无                                                                                    0

final static int i =20;             产生constantValue属性,并存20到constantValue               从对应constantValue取出来初始化

结果—————>       20                                                                                   20

解析——将运行时常量池的符号引用替换为直接引用(指向内存目标的句柄),这一过程又叫“静态绑定/静态连接”

初始化阶段:(动态绑定/动态连接)

              1. 执行时机——主动引用(new、反射、父类、执行主类包含main方法、调用静态方法

                  *new

——执行父类的<clinit>()、执行本类的<clinit>()、执行父类的<client>、执行本类的<client>

 1 //例子(笔试常考题目)
 2 public class Father {
 3
 4 private static int i =20;
 5 static{
 6     System.out.println("Father;(类构造器-before)"+i);
 7     i =50;
 8     System.out.println("Father;(类构造器-after)"+i);
 9 }
10 public Father() {
11     System.out.println("Father:(实例构造器)"+i);13 }
14
15 }
16
17
18
19 public class Son extends Father{
20
21     private static int i =20;
22     static{
23         System.out.println("Son;(类构造器-before)"+i);
24         i =50;
25         System.out.println("Son;(类构造器-after)"+i);
26     }
27     public Son() {
28         System.out.println("Son:(实例构造器)"+i);        30     }
31 }
32
33
34
35 public class Main {
36
37     public static void main(String[] args) {
38         new Son();
39
40     }
41
42 }
43
44 //输出结果:
45         /**
46            *Father;(类构造器-before)20
47            *Father;(类构造器-after)50
48            *Son;(类构造器-before)20
49            *Son;(类构造器-after)50
50            *Father:(实例构造器)50
51            *Son:(实例构造器)50
52            */

2.主动引用、被动引用 

                            被动引用——会发生类加载,但不会初始化

                                         ——例子:略

时间: 2024-10-13 12:21:51

读《深入理解Java虚拟机》有感——第二部分:虚拟机类加载机制的相关文章

读深入理解Java中的String(包括JVM)一文总结和提升

读深入理解Java中的String(包括JVM)一文总结和提升 摘要:String作为Java语言中的字符串模拟类,无论是实际的编程工作还是笔试面试过程,都需要我们都String类非常熟悉,对于String类的大部分字符串操作方法,都必须达到熟练运用的程度才行.但是,笔试和面试过程中,面试官往往喜欢问一些String特性相关的题目,来考察面试者对于String基础知识的掌握是否牢固.(本人尚未研读深入理解JVM这本书,分析JVM都是查看网上资料来分析的,若在接下来的内容有分析不到位的地方请见谅和

Java基础 -- 深入理解Java类型信息(Class对象)与反射机制

一 RTTI概念 认识Claa对象之前,先来了解一个概念,RTTI(Run-Time Type Identification)运行时类型识别,对于这个词一直是 C++ 中的概念,至于Java中出现RTTI的说法则是源于<Thinking in Java>一书,其作用是在运行时识别一个对象的类型和类的信息,这里分两种: 传统的”RTTI”:它假定我们在编译期已知道了所有类型(在没有反射机制创建和使用类对象时,一般都是编译期已确定其类型,如new对象时该类必须已定义好): 反射机制,它允许我们在运

(转)《深入理解java虚拟机》学习笔记6——类加载机制

Java虚拟机类加载过程是把Class类文件加载到内存,并对Class文件中的数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的java类型的过程. 在加载阶段,java虚拟机需要完成以下3件事: a.通过一个类的全限定名来获取定义此类的二进制字节流. b.将定义类的二进制字节流所代表的静态存储结构转换为方法区的运行时数据结构. c.在java堆中生成一个代表该类的java.lang.Class对象,作为方法区数据的访问入口. Java虚拟机的类加载是通过类加载器实现的, Java中

[笔记][思维导图]读深入理解JAVA内存模型整理的思维导图

本人记忆差,整理这个思维导图,相当于较认真的看了一遍,整个思维导图,基本上就是对原文的拷贝. 有了层级关系.和本人自己的一些理解.由于思维导图弄出来的图片大于100M了.所以就放出源文件, 更方便的阅读查阅.在csdn太穷了.下载2积分.有需要的希望意思意思.我也要去下载资料呢.(下载地址在最后) 有几点我觉得是看这个本书或则思维导图.你要明白的是: 1. 什么是内存可见性 2. 在java程序中,在底层执行的代码指令并不是完全按照顺序执行的-有重排序的存在 3. volatile 是一个和硬件

深入理解Java类型信息(Class对象)与反射机制(干货)

[版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/70768369 出自[zejian的博客] 本篇主要是深入对Java中的Class对象进行分析,这对后续深入理解反射技术非常重要,这篇有点偏向内功类文章,主要内容如下: 深入理解Class对象 RRTI的概念以及Class对象作用 Class对象的加载及其获取方式 Class对象的加载 ClassforName方法 Class字面常

深入理解Java类型信息(Class对象)与反射机制

深入理解Class对象 RRTI的概念以及Class对象作用 认识Class对象之前,先来了解一个概念,RTTI(Run-Time Type Identification)运行时类型识别,对于这个词一直是 C++ 中的概念,至于Java中出现RRTI的说法则是源于<Thinking in Java>一书,其作用是在运行时识别一个对象的类型和类的信息,这里分两种:传统的"RRTI",它假定我们在编译期已知道了所有类型(在没有反射机制创建和使用类对象时,一般都是编译期已确定其类

全面理解Java内存模型(JMM)及volatile关键字(转)

原文地址:全面理解Java内存模型(JMM)及volatile关键字 关联文章: 深入理解Java类型信息(Class对象)与反射机制 深入理解Java枚举类型(enum) 深入理解Java注解类型(@Annotation) 深入理解Java类加载器(ClassLoader) 深入理解Java并发之synchronized实现原理 Java并发编程-无锁CAS与Unsafe类及其并发包Atomic 深入理解Java内存模型(JMM)及volatile关键字 剖析基于并发AQS的重入锁(Reetr

深入理解Java注解类型(@Annotation)

"-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> 深入理解Java注解类型(@Annotation) - zejian的博客 - 博客频道 - CSDN.NET zejian的博客 目录视图 摘要视图 订阅 [活动]2017 CSDN博客专栏评选 &nbsp [5月书讯]流畅的Python,终于等到你!    &am

深入理解Java枚举类型(enum)

[版权申明]未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) http://blog.csdn.net/javazejian/article/details/71333103 出自[zejian的博客] 关联文章: 理解Java位运算 深入理解Java类型信息(Class对象)与反射机制 本篇主要是深入对Java中枚举类型进行分析,主要内容如下: 理解枚举类型 枚举的定义 枚举实现原理 枚举的常见方法 Enum抽象类常见方法 编译器生成的Values方法与ValueOf方法 枚举与Clas