JVM 心得分享(加载 链接 初始化)
有三个加载类:BootstrapClassLoader,加载jre/lib/下的类;
ExtensionClassLoader:加载jre/lib/ext下的类;
ApplicationClassLoader:加载classpath下的类(应用程序自己开发的类,如工程目录/bin/下的.class文件)
还有一个扩展的加载类,满足应用程序的特殊需求。类的加载时,父亲loader优先执行load动作,父亲load不了时,子类运作。
2、链接阶段:又分为三个小阶段校验,准备,解析。
校验:实施字节码文件的格式,语法等的校验。
准备:对静态变量申请存储空间,并设置默认的初始值。如:privatestaticinta=2;那么在准备阶段a被设置为0;
解析:把方法区中的符号指针替换为直接引用。
第一步:验证静态变量和静态块的加载+链接(校验,准备,解析)+初始化过程中值的变化。
packagecom.chong.studyparalell.clazz.loader; publicclassClassLoaderDemo{ publicstaticvoidmain(String[]args){ Testtest2=newTest(); System.out.println("Test2实例化结束"+test2.toString()); } }
packagecom.chong.studyparalell.clazz.loader; publicclassTest{ privatestaticTesttest1=newTest(); privatestaticinta=2; privatestaticintb=2; static{ System.out.println("【Test类静态块】a="+a); } publicTest(){ System.out.println("【Test类构造方法】a="+a); System.out.println("【Test类构造方法】b="+b); System.out.println("【Test类实例】"+this.toString()); } publicstaticTestnewInstance(){ returntest1; } }
log输出如下:
1【Test类构造方法】a=0
2【Test类构造方法】b=0
3【Test类实例】com.chong.studyparalell.clazz.loader.Test@16c1857
4【Test类静态块】a=2
5【Test类构造方法】a=2
6【Test类构造方法】b=2
7【Test类实例】com.chong.studyparalell.clazz.loader.Test@1b1fd9c
8Test2实例化结束com.chong.studyparalell.clazz.loader.Test@1b1fd9c
首先Test类在链接阶段(准备阶段),a,b分别被设置默认值0。
当newTest()执行后,
1)首先初始化Test类的三个静态变量test1,a,b。
初始化test1时,第一次调用构造方法,此时a,b为0。对应日志1,2行。
实例化test1,日志第3行。
test1初始化完成后,继续初始化a,b,设为2。
接着初始化静态块,对应日志第4行。
2)执行Test类的构造方法
因为a,b已经被初始化为2,所以执行类的构造方法时,会输出a,b为2。日志第5,6行。
实例化后输出地址信息,日志第7行。
3)最终main方法里打出实例工作完成,日志第8行。
第二步,加入父类后,进行确认。
packagecom.chong.studyparalell.clazz.loader; publicclassTestBase{ privatestaticintbase_a=2; privatestaticintbase_b=2; static{ System.out.println("【父类静态块】base_a="+base_a); } publicTestBase(){ System.out.println("【父类构造方法】base_a="+base_a); System.out.println("【父类构造方法】base_b="+base_b); System.out.println("【父类实例】"+this.toString()); } }
packagecom.chong.studyparalell.clazz.loader; publicclassTestextendsTestBase{ 内容同第一步; }
log输出如下:
【父类静态块】base_a=2
【父类构造方法】base_a=2
【父类构造方法】base_b=2
【父类实例】com.chong.studyparalell.clazz.loader.Test@19ab8d
【Test类构造方法】a=0
【Test类构造方法】b=0
【Test类实例】com.chong.studyparalell.clazz.loader.Test@19ab8d
【Test类静态块】a=2
【父类构造方法】base_a=2
【父类构造方法】base_b=2
【父类实例】com.chong.studyparalell.clazz.loader.Test@14dcfad
【Test类构造方法】a=2
【Test类构造方法】b=2
【Test类实例】com.chong.studyparalell.clazz.loader.Test@14dcfad
Test2实例化结束com.chong.studyparalell.clazz.loader.Test@14dcfad
可以发现父类的静态变量,静态块,构造方法首先被初始化。然后子类的静态变量,静态块和构造方法被初始化。
第三步:写一个自定义的类加载器
packagecom.chong.studyparalell.clazz.loader; publicclassMyClassLoaderPilot{ publicstaticvoidmain(String[]args){ try{ MyClassLoaderclassLoader=newMyClassLoader(); Stringfilename="com.chong.studyparalell.demon.DemonThreadDemo"; Objectclazz=classLoader.loadCustomizeClass(filename); System.out.println(clazz); }catch(Exceptione){ e.printStackTrace(); } } }
packagecom.chong.studyparalell.clazz.loader; importjava.io.ByteArrayOutputStream; importjava.io.FileInputStream; importjava.io.IOException; publicclassMyClassLoaderextendsClassLoader{ privateStringdemoPath="D:\\work\\temp\\"; publicClass>loadCustomizeClass(Stringfilename)throwsClassNotFoundException{ FileInputStreamfis=null; ByteArrayOutputStreambaos=newByteArrayOutputStream(); try{ //1.获取class文件的字节码(二进制数据) String[]fileNames=filename.split("\\."); fis=newFileInputStream(demoPath+fileNames[fileNames.length-1]+".class"); byte[]bytes=newbyte[1024]; intlen=0; while((len=fis.read(bytes))!=-1){ baos.write(bytes,0,len); } }catch(Exceptione){ thrownewClassNotFoundException(); }finally{ try{ fis.close(); }catch(IOExceptione){ thrownewClassNotFoundException(); } } byte[]paramArrayOfByte=baos.toByteArray(); //2。把二进制文件定义为class对象返回 returndefineClass(filename,paramArrayOfByte,0,paramArrayOfByte.length); } }
日志输出如下:
classcom.chong.studyparalell.demon.DemonThreadDemo
实际的跟着代码走一遍,看看控制台的输出,用自己的思路虚拟着跟一跟,对于类的加载过程能够认识的更加清晰一些。
以上这篇JVM心得分享(加载链接初始化)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持毛票票。