龙空技术网

Java虚拟机(JVM)- 类加载系统流程

zuoandwang 418

前言:

此时姐妹们对“javac编译成功但是java无法找到或无法加载主类”大体比较关切,咱们都想要剖析一些“javac编译成功但是java无法找到或无法加载主类”的相关资讯。那么小编也在网络上收集了一些对于“javac编译成功但是java无法找到或无法加载主类””的相关知识,希望我们能喜欢,朋友们快快来学习一下吧!

常见的虚拟机HotSpot: java8默认,解释器和JIT并存,自动探索热点代码用JIT编辑成机器语言,提升性能JRocketit: 适用于服务器环境,只有JIT,性能较好IBM J9: 定位与HotSpot接近,广泛用于IBM的各种Java产品JVM的工作流程

编译好的字节码文件在JVM里面的加载和执行流程如下:

编写好的java源代码,经过javac编译,成为字节码文件(.class)当一个java类被使用到的时候,类会被加载进JVM (按需),类的加载由 类加载子系统 完成,一个java类经过类加载子系统后,类的meta信息会被存储进JVM的 方法区 (Method Area)。类的meta信息包含:方法代码,变量名,方法名,访问权限,返回值等类加载完成后会生成一个Class对象,Class对象是存放在 内存中类依赖的类会被递归依次加载执行引擎:运行java代码类加载子系统:

类加载子系统加载一个java类有三步,分别是:装载,链接和初始化。

装载

通过ClassLoader来读取字节码文件,读取成功后返回一个Class<?>实例;java自带的ClassLoader有以下三种:

BootstrapClassLoader:由C/C++实现,在JVM内部无法获取到实例,在创建JVM虚拟机的时候自动使用BootstrapClassLoader加载java核心类库 ($jre/lib下的rt.jar, resources.jar等, String, Integer, Map等java, javax, sun开头的类),加载ExtClassLoader, SystemClassLoader (这两个ClassLoader位于rt.jar中)ExtClassLoader:加载java.ext.dirs或者$jre/lib/ext下的类库,自定义的jar放到$jre/lib/ext下的话也会被ExtClassLoader加载AppClassLoader:加载classpath或者java.class.path下的类库,通常用来加载自定义的类库。

这三种都继承自抽象类ClassLoader,JVM对ClassLoader的分类就两种:BootstrapClassLoader和Custom ClassLoader, ExtClassLoader和AppClassLoader都属于CustomClassLoader。

为什么需要Custom ClassLoader?

隔离加载类修改类的加载方式扩展加载源防止源码泄露

ClassLoader的双亲委派机制:

避免类的重复加载:通过委托去parent问问,如果加载过了就不用重新加载。沙箱安全机制:避免系统核心类被破坏,比如自己写一个java.lang.String类(同包同名),JVM在加载的时候一定会只加载rt.jar里面的String类保证核心API包的访问权限:比如自己写一个java.lang.TestString类(位于java.lang包中),暗示这个类是个JVM系统类,根据委托机制,会向上传递到BootstrapClassLoader, BootstrapClassLoader中不存在向下传递,ExtClassLoader也不存在,向下传递到AppClassLoader,强制加载会报SecurityException。即便可以通过一些手段加载,由于TestString跟其他的java.lang下的类在同一个包里,也不能访问java.lang下其他包里的可见数据,因为TestString和java.lang下的类不是由同一个ClassLoader加载的,要取得java.lang包中的权限,TestString类必须和java.lang下的其他类是来自同一个运行时包(即:同一个ClassLoader, 属于同一个包的多个类的集合)。工作原理:

能不能自己写个类叫做java.lang.System或者java.lang.TestString?

通常不行但是可以通过指定JVM的参数-Xbootstrappath来完全取代java核心类,需要重写所有java核心类,可以通过写CustomClassLoader的方式实现,自定义一个ClassLoader,重写其中的loadClass方法(双亲委派机制在ClassLoader.loadClass方法中实现)可以通过SPI机制:只针对接口有效链接

链接过程分为3个阶段:

验证:确保.class文件中的字节流包含的信息符合JVM的要求(例如hotspot jvm要求文件头以CAFA BABE开头),确保被加载类的正确性,不会危害虚拟机自身的安全;主要有:文件格式验证,元数据验证,字节码验证,符号引用验证。准备:为类变量(static变量,不包含final static变量)分配内存并且设置零值(即改变量类型的默认值),注意:不是代码里面赋予的初始值,而是零值;final static修饰的变量在编译的时候就分配了,准备阶段会显式初始化;这里不会为类的实例变量初始化或者分配内存;类变量分配在方法区中,而实例变量是随着对象实例一起分配到堆内存中。解析:将常量池内的符号引用转换为直接引用的过程。事实上解析操作往往会伴随着JVM在执行完初始化之后再执行;解析主要针对类或者接口、字段、类方法、接口方法、方法类型等,对应常量池中的CONSTANT_Class_info, CONSTANT_Field_info,CONSTANT_Methodref_info等。初始化初始化阶段就是执行静态类构造器方法<clinit>()的过程。此方法是javac编译器自动收集类中的所有类变量的赋值动作和静态构造函数(static { ... }) 语句合并而来。注意:<clinit>()不是类构造器,一般类构造器在JVM下表现为<init>()<clinit>()中的指令按照语句在源文件中出现的顺序执行。如果该类具有父类,JVM会保证父类的<clinit>()一定是先于子类的<clinit>()被执行。JVM会保证多线程下<clinit>()里面的代码块只会被执行一次。

标签: #javac编译成功但是java无法找到或无法加载主类