标签:size print 阶段 载器 替换 自己 系统变量 trap 常量
前引:类加载是java程序运行的第一步,研究类加载有助于理解jvm执行过程.
? 类加载机制可以让程序能动态的控制类加载的过程,比如热部署等(在应用运行的时候升级软件,无需重新启动的方式 )
jvm类加载分为五个部分:加载,验证,准备,解析,初始化
加载:加载主要是将 .class文件的字节流读取到jvm中.在这个过程jvm需要完成三件事:
通过类的全限定名获取该类的二进制字节流;
将字节流所代表的静态存储结构转化为方法区中的运行时数据结构
在内存中创建了一个该类的java.lang.Class文件,作为这个类在方法区中数据的访问入口;
验证:验证是连接的第一步,目的是保证加载进来的字节流符合jvm规范;完成以下行为
文件格式验证;
元数据验证,判断是否符号java语法
字节码验证,确保逻辑正确;
符号引用验证,确保下一步能正常执行.
准备:连接阶段的第二步,为静态变量在方法区分配内存.并设置默认值.
解析:连接阶段第三步,是虚拟机将常量池中的符号引用替换为直接引用的过程.
初始化:类加载的最后一步.先static,再构造器。则按照static在方法内的顺序 ,先初始化父类再初始化子类.顺序为 父类静态——》子类静态——》父类非静态代码块——》父类构造方法——》子类非静态代码块——》子类构造方法。
jvm初始化步骤:
假如这个类还没有加载和连接,则程序会先加载并连接该类.加入该类的直接父类还没有被初始化,则先初始化其直接父类
如果该类有初始化语句,则依次执行初始化语句
结束生命周期
•在如下几种情况下,Java虚拟机将结束生命周期
– 执行了System.exit()方法
– 程序正常执行结束
– 程序在执行过程中遇到了异常或错误而异常终止
– 由于操作系统出现错误而导致Java虚拟机进程终止
什么时候对类初始化:
1.遇到new,getstatic,putstatic,invokestatic这4条字节码指令时,假如类还没进行初始化,则马上对其进行初始化工作。其实就是3种情况:用new实例化一个类时、读取或者设置类的静态字段时(不包括被final修饰的静态字段,因为他们已经被塞进常量池了)、以及执行静态方法的时候。
2.使用java.lang.reflect.*的方法对类进行反射调用的时候,如果类还没有进行过初始化,马上对其进行。
3.初始化一个类的时候,如果他的父亲还没有被初始化,则先去初始化其父亲。
4.当jvm启动时,用户需要指定一个要执行的主类(包含static void main(String[] args)的那个类),则jvm会先去初始化这个类。5.Class.forName(StringclassName);来加载类的时候,也会执行初始化动作。注意:ClassLoader的loadClass(String className);方法只会加载并编译某类,并不会对其执行初始化。
实现的功能:加载阶段获取类的二进制字节流的时候
jvm提供了三种类加载器:
启动类加载器(Bootstrap Loader):最顶层的类加载器, JAVA_HOME\lib 目录中的 rt.jar,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载)。启动类加载器是无法被Java程序直接引用的。
扩展类加载器(Extention Loader):负责加载 JAVA_HOME\lib\ext 目录中类库. 或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器
应用程序加载器(Application Loader):也叫做系统类加载器,可以通过getSystemClassLoader()获取负责加载用户路径(classpath)上的类库。如果没有自定义类加载器,一般这个就是默认的类加载器。
类加载方式:
1 /*
2 *1.命令行启动程序时,由jvm初始化加载
3 *2.通过Class.forName()动态加载
4 *3.通过ClassLoader.LoadClass()动态加载
5 */
双亲委派模式规定除了启动类加载器,其他加载器都要有父类加载器,他们的关系是组合关系
工作过程:
当一个类需要加载的时候,他自己不会加载这个类,而是向上委派请求,委托父类加载器来加载类,如果启动类加载器都不能加载这个类时,子加载器才会自己加载.
jvm类加载机制
全盘负责:当一个类加载器负责加载某个类的时候,这个类所依赖和引用的Class也会由该类加载器负责加载.除非显示使用另外的类加载器.
父类委托:先让父类加载该类,只有在父类不能加载该类时才会尝试 从自己的类路径加载该类
缓存机制:他会保证所有的已经加载过的Class文件会被缓存,当程序需要使用某个Class文件时就会从缓存区读取,只有缓存区没有的时候才会去读取该类对应的二进制数据,并将其转化成Class对象,并存入缓存区.所以改了class文件后就要重启jvm
双亲委派模型的代码实现集中在java.lang.ClassLoader的loadClass()方法当中。
1)首先检查类是否被加载,没有则调用父类加载器的loadClass()方法;
? 2)若父类加载器为空,则默认使用启动类加载器作为父加载器;
3)若父类加载失败,抛出ClassNotFoundException 异常后,再调用自己的findClass() 方法。
意义:1.系统类防止内存中出现多份同样的字节码
2.保证java程序安全稳定运行.
面试题:
1 public class Test { 2 public static void main(String[] args) { 3 SingleTon singleTon = SingleTon.getInstance(); 4 System.out.println("count1=" + singleTon.count1); 5 //类初始化的顺序为,先加载 static,再构造器。 6 System.out.println("count2=" + singleTon.count2); 7 } 8 } 9 10 class SingleTon{ 11 private static SingleTon singleton=new SingleTon(); 12 public static int count1; 13 public static int count2; 14 15 private SingleTon(){ 16 count1++; 17 count2++; 18 } 19 20 public static SingleTon getInstance(){ 21 return singleton; 22 } 23 }
多个线程同时调用getInstance方法,如果不存在SingleTon实例对象,则会触发类的初始化。已经存在类初始化,则直接会去调用。
标签:size print 阶段 载器 替换 自己 系统变量 trap 常量
原文地址:https://www.cnblogs.com/52haiyan/p/9506527.html