Jvm结构 类加载
类加载子系统的作用
类加载子系统负责从文件系统或者网络中加载class文件,class文件开头有特定的文件标识,(字节码文件都以CA FE BA BE标识开头)
classLoader只负责class文件的加载,至于它是否可以允许,泽由ExcecutionEngine决定,加载的类信息存放于一块称之为方法区的内存空间,除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量
类加载ClassLoader的角色
1.classfile(class文件)存在于硬盘上,可以理解为设计师画在纸上的模板,最终这个模板执行的时候是要加载JVM中来,根据这个模板实例化出n个一模一样的实例.
2.classfile 加载到jvm中,被称为DNA元数据模板,放在方法中去
3.在class----->jvm-------->最终被称为元数据模板,此过程就要有一个运输工具(类加载器 class loader)扮演一个快递员的角色
JVM类加载过程
加载--------验证---------准备------------解析-----------------初始化
也可以说 加载-链接-初始化
加载:
1.通过类名,获取此类的二进制字节流
2.将这个字节流所代表的静态存储结构转化为方法区(元空间)的运行时结构
3,在内存中生成一个代表这个类的java.lang.class对,作为方法区这个类的各种数据的访问入口
链接
链接部分分为三步,
验证:检验被加载的类是否有正确的内部结构,并与其他类协调一致
准备:准备阶段则负责为类的静态属性分配内存,并设置默认初始值
不包含用final修饰的static实例变量,在编译时进行初始化,不会为实例变量初始化,
解析:将类的二进制数据中的符号引用替换成直接引用,
符号引用是用户一组符号描述所引用的目标,直接引用是指向目标的指针
初始化
类什么时候会初始化?
1创建类的实例,也就是创建一个对象
2访问某个类或者接口的静态变量,或者对该静态变量赋值
3调用类的静态方法
4反射()Class.forName("")
5,初始化一个类的子类(会首先初始化子类的父类)
类的初始化顺序
对static修饰的变量或者语句块进行赋值,
如果同时包含多个静态变量和静态代码块,则按照自上而下的顺序依次执行
如果初始化一类的时候,其有父类而且父类没有被初始化,则优先初始化它的父类
顺序是:父类static 子类static 父类构造方法 子类构造方法
类加载器的分类
JVM支持两种类型的类加载器,分别为引导类加载器,和自定义类加载器
无论类加载器的类型怎么划分,在程序中我们最常见的始终有三个
引导类加载器(也就是启动类加载器)
这个类加载器使用c/c++语言实现,嵌套在jvm内部,他用来加载java核心类库
并不集成于java.lang.ClassLoader没有父加载器(孤儿)
负责加载扩展类加载器和应用类加载器,并为他们制定父类加载器
出于安全考虑,引用类加载器只加载包名为java,javax,sun开头的类
扩展类加载器
java语言编写的,由sun,misc,Launcher$ExtClassLoader实现
派生于ClassLoader类,上层加载器类引用类加载器(有爹不是孤儿)
从java.ext.dirs系统属性所指定的目录中加载类库,或者从JDK系统安装目录的,jre/lib/ext子目录下加载类库,如果用户创建的jar放在此目录下,也会自动由扩展类加载器加载
应用程序类加载器,(系统类加载器)
java语言编写的,由 sun.misc.Launcher$AppClassLoader 实现.
派生于classLoader类 上层类加载器为扩展类加载器
来加载我们自己定义的类 该类加载器是程序中默认的类加载器
通过类名.class.getClassLoader(),ClassLoader.getSystemClassLoader()来获得.
ClassLoader 类,它是一个抽象类,其后所有的类加载器都继承自 ClassLoader
(不包括启动类加载器)
双亲委派机制
java虚拟机对class文件采取的是按需加载的方式,也就是说当需要该类是才会将它的class文件加载到内存中生成class对象,而且加载某个类的class文件时,java虚拟机采用的是双亲委派机制,
工作原理;
如果一个类加载器收到了类加载请求,它会把请求交给父类加载器执行,如果父类加载器还有父亲,则进一步向上委托,最后父类如果加载失败,就交由子加载器自己处理
如果全部失败:就会抛出ClassNotFoundException异常
双亲委派的优点:
1,安全 可避免用户自己编写的类动态替换java的核心类,如java.lang.String
2 避免全限定命名的类重复加载
沙箱安全机制:
作用;防止代码被污染
面试题:
在jvm中如何判断两个对象属于同一类?
类的全类名完全一致.类的加载器必须相同
类的主动使用
主动使用:
通过new关键字被导致类的初始化,这是大家经常使用的初始化一个类的方式,
他肯定会导致类的加载并且初始化
访问类的静态变量,包括读取和更新
访问类的静态方法
对某个类进行反射操作,会导致类的初始化
初始化子类会导致父类的的初始化
执行该类的 main 函数
被动使用:
其实除了上面的几种主动使用其余就是被动使用了
1.引用该类的静态常量,注意是常量,不会导致初始化,但是也有意外,这里的常量
是指已经指定字面量的常量,对于那些需要一些计算才能得出结果的常量就会导
致初始化,比如:
public final static int NUMBER = 5 ; //不会导致类初始化,被动使用public final static int RANDOM = new Random().nextInt() ; //会导致类的初
始化,主动使用
2.构造某个类的数组时不会导致该类的初始化,比如:
Student[] students = new Student[10] ;
主动使用和被动使用的区别在于类是否会被初始化.