标签:however 场景 定义类 get 链接 master 依赖 correct reason
虚拟机的类加载机制是指,虚拟机把描述类的数据从Class?件加载到内存,并对数据进?验证、准备、解析、初始化等,最终形成可以被虚拟机直接使?的Java类型。
与那些在编译时需要进?链接的语?不同,在Java语???,类型的加载、链接和初始化过程都是在程序运?期间完成的,这种策略虽然会令类加载时稍微增加?些性能开销,但是会为Java应?程序提供?度的灵活性,Java天?可以动态扩展的语?特性就是依赖运?期动态加载和动态链接这个特点实现的。
类从被加载到虚拟机内存中开始,到卸载出内存为?,它的整个?命周期包括:加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使?(Using)和卸载(Unloading)7个阶段。
Loading is the process of finding the class file that represents the class or interface type with a particular name and reading it into a byte array. Next the bytes are parsed to confirm they represent a Class object and have the correct major and minor versions. Any class or interface named as a direct superclass is also loaded. Once this is completed a class or interface object is created from the binary representation.
加载是类加载(Class Loading)过程的?个阶段,虚拟机需要完成以下3件事情:
第一条中的获取二进制字节流可以从多个地方获取。例如ZIP包(JAR),网络(Applet),运行时计算生成(
验证是为了确保Class?件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机??的安全。
虽然Java语?本?是相对安全的语?,但是Class?件并不?定要求?Java编译?来,它可以使?任何途径产?,甚?包括??六进制编辑器直接编写来产?Class?件。虚拟机如果不检查输 ?的字节流,对其完全信任的话,很可能会因为载?了有害的字节流?导致系统崩溃。
Preparing involves allocation of memory for static storage and any data structures used by the JVM such as method tables. Static fields are created and initialized to their default values, however, no initializers or code is executed at this stage as that happens as part of initialization.
准备阶段为类变量分配内存并设置类变量(仅包括被被static修饰的变量)初始值,这些变量所使?的内存都将在?法区中进?分配。实例变量将会在对象实例化时随着对象一起分配在Java堆中。初始值通常情况下是数据类型的默认值.
public static int value = 123; //初始值为0
如果类字段的字段属性表中存在ConstantValue属性,那在准备阶段变量value就会被初始化为ConstantValue属性所指定的值:
public static final int value = 123; // 初始值为123
解析阶段将常量池内的符号引?替换为直接引?。
符号引?(Symbolic References): 符号引?以?组符号来描述所引?的?标,符号可以是任何形式的字?量,只要使?时能?歧义地定位到?标即可。符号引?与虚拟机实现的内存布局?关,引?的?标并不?定已经加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引?必须都是?致的,因为符号引?的字?量形式明确定义在Java虚拟机规范的Class?件格式中。
直接引?(Direct References): 直接引?可以是直接指向?标的指针、相对偏移量或是?个能间接定位到?标的句柄。直接引?是和虚拟机实现的内存布局相关的,同?个符号引?在不同虚拟机实例上翻译出来的直接引??般不会相同。如果有了直接引?,那引?的?标必定已经在内 存中存在。
Initialization of a class or interface consists of executing the class or interface initialization method <
What is the difference between <
class X { static Log log = LogFactory.getLog(); // <clinit> private int x = 1; // <init> X(){ // <init> } static { // <clinit> } }
对于初始化阶段,虚拟机规范严格规定了有且只有5种情况必须?即对类进?“初始化”:
Class.forName
)"有且只有"这5种场景中的?为称为对?个类进?主动引?。除此之外,所有引?类的?式都不会触发初始化,称为被动引?。
下面代码运?之后,只会输出"SuperClass init!",?不会输出"SubClass init!"。可通过
主动使用和被动使用
//被动使用类字段演示一:通过子类引用父类的静态字段,不会导致子类初始化 public class NotInitialization1 { public static void main(String[] args) { System.out.println(SubClass.value); } } class SuperClass { static { System.out.println("SuperClass init!"); } public static int value = 123; } class SubClass extends SuperClass { static { System.out.println("SubClass init!"); } }
// 被动使用类字段演示二:通过数组定义来引用类,不会触发此类的初始化 public class NotInitialization2 { public static void main(String[] args) { SuperClass[] sca = new SuperClass大专栏 7 虚拟机类加载机制o">[10]; } }
对于任意?个类,都需要由加载它的类加载器和这个类本??同确?其在Java虚拟机中的唯?性,每?个类加载器, 都拥有?个独?的类名称空间。
?较两个类是否“相等”,只有在这两个类是由同?个类加载器加载的前提下才有意义,否则,即使这两个类来源于同?个Class?件,被同?个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相等。
不同的类加载器对
//类加载器与instanceof关键字演示 public class ClassLoaderTest { static class MyClassLoader extends ClassLoader { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { String fileName = name.substring(name.lastIndexOf(".") + 1) + ".class"; InputStream is = getClass() .getResourceAsStream(fileName); // 找到class文件 if (is == null) return super.loadClass(name); byte[] b = new byte[is.available()]; is.read(b); // 读取class return defineClass(name, b, 0, b.length); // 转化为class实例 } catch (IOException e) { throw new ClassNotFoundException(name); } } } public static void main(String[] args) throws Exception { MyClassLoader myClassLoader = new MyClassLoader(); Object obj = myClassLoader .loadClass("com.unstandingJVM.ClassLoaderTest").newInstance(); // 输出:class com.unstandingJVM.ClassLoaderTest System.out.println(obj.getClass()); //输出: false System.out.println(obj instanceof com.unstandingJVM.ClassLoaderTest); } }
从Java虚拟机的角度来讲,只存在两种不同的类加载器:?种是启动类加载器(Bootstrap ClassLoader),这个类加载器使?C++语?实现 ,是虚拟机??的?部分;另?种就是所有其他的类加载器,这些类加载器都由Java语?实现,独?于虚拟机外部,并且全都继承?抽象类
rt.jar
and other core libraries located in $JAVA_HOME/jre/lib
directory.$JAVA_HOME/lib/ext
directory.-cp
or -classpath
command line options.public class ViewClassLoader { public static void main(String[] args) { // 启动类加载器,HashMap位于$JAVA_HOME System.out.println(java.util.HashMap.class.getClassLoader()); // 扩展类加载器,位于$JAVA_HOME/lib/ext System.out.println(javafx.collections.FXCollections .class.getClassLoader()); // 应用程序类加载器,位于当前目录 System.out.println(ViewClassLoader.class.getClassLoader()); // 自定义类加载器 ClassLoaderTest.MyClassLoader myClassLoader = new ClassLoaderTest.MyClassLoader(); Object obj = myClassLoader .loadClass("com.unstandingJVM.ClassLoaderTest").newInstance(); System.out.println("class loader for testClassLoader class:" + obj.getClass().getClassLoader()); } } // 输出结果 // null #启动类加载器 // sun.misc.Launcher$ExtClassLoader@123a439b # 扩展类加载器 // sun.misc.Launcher$AppClassLoader@18b4aac2 # 应用程序类加载器 // com.unstandingJVM.ClassLoaderTest$MyClassLoader@76ccd017 # 自定义类加载器
类加载器的?作过程是:如果?个类加载器收到了类加载的请求,它?先不会??去尝试加载这个类,?是把这个请求委派给?类加载器去完成,每?个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当?加载器反馈???法完成这个加载请求(它的搜索范围中没有找到所需的类)时,?加载器才会尝试??去加载。类加载器之间的这种关系被称为类加载器的双亲委派模型(Parents Delegation Model)。
到了Java SE9,classloader越来越复杂,详细见The Class Loader Hierarchy
标签:however 场景 定义类 get 链接 master 依赖 correct reason
原文地址:https://www.cnblogs.com/dajunjun/p/11698466.html