JVM 如何装载和初始化一个Java class(类)
2017-08-15 12:48
190 查看
装载一个类
学习过C/C++语言的读者知道,C/C++源代码必须首先别编译成本地的机器代码,然后还需要一个链接代码过程。该链接过程的主要任务就是:合并不同的源码文件产出的中间代码,并最终获得一个可直接执行的应用程序。然后,Java语言不是这么做的,Java应用中类似于链接过程的步骤是在字节码被装载到JVM中之后再执行的。正如我们所知,JVM存在不止一个类加载器,不同的加载器使用不同的方式来加载类。但是,它们都只在必须加载类时,才开始装载类。当然,如果已装载完毕的类引用了某些未装载的类,类装载器会进一步去装载这些类,整个的装载过程是递归的。
装载时机与方式
在Java中,类的装载策略是由类加载器控制的。我们使用下面这个简单的示例代码来演示类的装载方式与时机:// TestLoader.java package compiler; public class TestLoader { public static void main(String[] args) { System.out.println("test"); } }
// A.java package compiler; public class A { public void method(){ System.out.println("inside of A"); } }
再看一下它们的目录结构:
|-src |--compiler |------A.java |------TestLoader.java
为了获得每个类加载的信息,我们可以使用如下的命令来运行程序。其中的 “-verbose:class” 参数告诉JVM打印出每次加载类的信息。
java -verbose:class -classpath /home/ron/workspace/UltimateTest/bin/ compiler.TestLoader
整个程序的输出信息很繁琐,我们摘取其中重要的部分展示如下:
[Loaded sun.misc.JavaSecurityProtectionDomainAccess from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar] [Loaded java.security.ProtectionDomain$2 from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar] [Loaded java.security.ProtectionDomain$Key from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar] [Loaded java.security.Principal from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar] [Loaded compiler.TestLoader from file:/home/xiwang/workspace/UltimateTest/bin/] test [Loaded java.lang.Shutdown from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar] [Loaded java.lang.Shutdown$Lock from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
现在,假设我们把 TestLoader.java 修改成如下所示:
package compiler; public class TestLoader { public static void main(String[] args) { System.out.println("test"); A a = new A(); a.method(); } }
再次执行同样的运行命令,我们获得输出结果变成如下所示:
[Loaded sun.misc.JavaSecurityProtectionDomainAccess from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar] [Loaded java.security.ProtectionDomain$2 from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar] [Loaded java.security.ProtectionDomain$Key from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar] [Loaded java.security.Principal from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar] [Loaded compiler.TestLoader from file:/home/xiwang/workspace/UltimateTest/bin/] test [Loaded compiler.A from file:/home/xiwang/workspace/UltimateTest/bin/] inside of A [Loaded java.lang.Shutdown from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar] [Loaded java.lang.Shutdown$Lock from /usr/local/java/jdk1.6.0_34/jre/lib/rt.jar]
读者可仔细查看两者的区别,可以发现只有当 类A 被使用时,该类对应的 A.class 才会被装载。总结起来,一个类的加载条件是:
1. 当使用 new 操作符执行某类时。比如 SomeClass some = new SomeClass()。
2. 当已加载类中有一个静态引用指向某类时。 比如 System.out 。
初始化时机与方式
在Java中,一个类被加载完毕后,不会立即执行类的初始化工作。当类中的符号被第一次使用时,该类才开始被初始化。在初始化顺序方面:JVM会首先完成父类的初始化,再执行子类的初始化;对于类中静态的常量,会按照定义的先后顺序完成初始化;最后会保证在初始化之前,每个属性都会有一个默认值。至于类中各种类型成员的初始化顺序细节,建议查看本站的这篇文章。
相关文章推荐
- Java之深入JVM(3) - 由一个栈溢出的问题看Java类和对象的初始化 (转)
- jvm虚拟机的故事之(我是一个Java class)
- 学习Java的第一步是安装好JDK,写一个Hello World, 其实JDK的学习没有那么简单,关于JDK有两个问题是很容易一直困扰Java程序员的地方:一个是CLASSPATH的问题,其实从原理上来说,是要搞清楚JRE的ClassLoader是如何加
- 一个java文件如何放两个public class 类
- 转载------如何dump出一个Java进程里的类对应的Class文件
- JVM如何判断一个Java对象是否可以回收
- 深入JVM(2): 由一个栈溢出的问题看Java类和对象的初始化
- java如何创建并初始化一个动态的二维数组
- Java之深入JVM(3) - 由一个栈溢出的问题看Java类和对象的初始化
- 如何实现一个Java Class解析器
- [java]一个多线程引起的死锁(class初始化,锁)
- Rhyme/Java getClass()与instanceof的区别以及如何编写一个完美的equals()
- 如何实现一个Java Class解析器
- Java中的类文件结构之一:如何分析一个.class文件的二进制码内容
- JVM如何判断一个Java对象是否可以回收
- Java之深入JVM(4) - 由一个栈溢出的问题看Java类和对象的初始化续
- 如何实现一个Java Class 解析器
- Java中class是如何加载到JVM中的(Class.forName("name")和ClassLoader.loadClass("name")的区别)
- JVM深入学习-Java代码执行篇二-[装载Class ]
- 关于在java如何为一个class文件写一个说明书