当前位置:网站首页>JVM学习——2——内存加载过程(类加载器)

JVM学习——2——内存加载过程(类加载器)

2022-08-10 15:13:00 涛涛英语学不进去

类加载器

一、效果

class文件被load进内存,同时生成一个Class类的对象,可以用这个Class对象指向这块内容。(class类的对象不是new出来的,是hotspot中C++代码load出来的)
内存分区,存常量,逻辑上是 method Area 方法区
1.8之前叫 Perm Generation 永久代
1.8之后叫 Meta Space
这两个都是指方法区

二、 过程

1. loading  class文件加载到内存
2. linking
	1. Verification   校验该文件是否符合class文件标准
	2. Preparation    把class的静态变量 赋 默认值
	3. Resolution     常量池里的符号引用转换成实际能直接访问到的地址
3. Initializing   静态变量赋值为初始值

三、不同层次的类加载器

Class类可以用来解析
class文件的二进制码
不同的类加载器负责加载不同的class
String.class.getClassLoader ( )
!!! 加载器直接不是继承关系,只是层次关系

1. Bootstrap加载器

加载核心包;类加载器的范围路径:sun.boot.class.path
ClassLoader的ClassLoader是Bootstrap加载器,最顶级的加载器 打印为 null
Bootstrap加载器 负责 加载 JDK中最核心的 jar
如 runtime.jar
charset,jar 等等
是C++实现的
Bootstrap 引导的意思

2. Extension加载器

打印为 ExtClassLoader
加载扩展包;
类加载器的范围路径:java.ext.dirs
加载扩展jar包
jre/lib/ext/*.jar
ext目录下的jar 都是扩展包
或 由 -Djava,ext,dirs指定

3. App加载器

打印为 AppLoader
加载我们自己写的 Java 类;
类加载器的范围路径:java.class.path
加载 classpath 指定的内容
我们自己写的 java 文件都是位于classpath下
由 App 加载器加载
APP extend Ext
APP加载器的父类是Ext加载器
APP加载器的逻辑上的上一级加载器也是Ext加载器
但APP加载器的加载器是null
而Ext没有parent

4. Custom ClassLoader 加载器

使用ClassLoader的loadClass(“类路径”)就能把类加载进内存,返回Class类的对象
Class clazz=Student.class.getClassLoader().loadClass(“com.soft.Student”);
继承ClassLoader,重写findClass方法
使用字节流把,class文件load加载到内存
再使用 defineClass(name,字节流,0,bytes.length) 截取字节流中[0,bytes.lengrh]位置的字节转化为Class类的对象
// class对象通过反射生成对象,调用方法
Hello h =(Hello)class.newInstance()
h.m()
======================
通过自定义ClassLoader可以对class加密
把生成的class文件 01值 对某个种子值seed 进行异或 ^ 加密
再seed异或一下就是解密
int seed=0B10110110
随便一个种子值都行

四、 双亲委派

在这里插入图片描述

JVM是按需动态加载采用双亲委派机制
.class 被load进内存的过程
自定义了类加载器,则先用自定义的类加载器,它内部维护一个缓存,记录这个class是否被加载进来了,如果没加载进来,就回去问App加载器是否加载了,加载就返回结果,
否则就再往上问,问Extension加载器加载了吗,如果有就返回结果,
如果没有就继续往上问Bootstrap加载了吗,如果有就返回,没有,就让 Extension加载器加载,如果找到就加载,如果没有找到,就让App加载器去加载,如果找到就加载,
如果没有,就让custom classloader加载
还是没找到,就报classNotFound异常
Bootstrap
Extension
App
Custom ClassLoader
每个加载器都有自己的缓存
先自底向上检查 该类是否已经加载 parent 方向
再自顶向下 进行实际查找和加载child方向

1.为什么要双亲委派?

主要是为了安全。次要问题是加载过了,浪费资源;
如果没有这个机制,可以随便把自己写的类Load进内存,会有安全问题,比如自己写一个类叫 java.lang.String ,把它load进内存,会破坏原有的API

2. 父加载器

父加载器不是 “类加载器的加载器”,也不是“类加载器的父类加载器” ,只是上一层次的更高级的加载器;
双亲委派是一个孩子向父亲的方向,然后父亲再向孩子方向委派的过程

3. 什么情况下会打破双亲委派?

重写loadClass方法 ,而不是重写findclass
什么时候需要打破双亲委派
JDK1.2之前自定义ClassLoader都必须重写loadClass()
设置自己线程的classLoader()
热启动、热部署,为了加载同一类库的不同版本(使用两个同名的类)

五、编译器

1. 字节码解释器 bytecode intepreter

2. 即时编译器 Just In-Time complier ( JIT )

编译成本地代码,类似.exe

3. 混合模式

混合使用 解释器 + 热点代码编译
起始阶段采用解释执行
==========
热点代码阶段
多次被调用的方法(方法计数器:监测方法执行频率)
多次被调用的循环 (循环计数器:监测循环执行频率)
进行编译
==========
-Xmined 模式为混合模式。开始解释执行,启动速度较快,对热点代码进行检测和编译
-Xint 使用纯解释模式,启动很快执行稍慢
-Xcomp 使用纯编译模式,执行很快,启动很慢
检测热点代码
-XX:CompileThreshold=10000

六、JVM的懒加载

1. 是不是所有的class文件都在启动的时候全部加载呢?

不是
java的三种类
系统类 比如 rt.jar
拓展类 比如 ext/*.jar
自己写的类
加载的时候是先加载核心类,再拓展类,最后自己的类
按需加载,Student s=new Student()被执行了,会被加载
Student s2=null就不会加载

2. 什么时候必须初始化呢?

初始化子类的时候,父类首先被初始化
虚拟机启动时,被执行的主类必须初始化
new 、getstatic、putstatic、invokestatic指令时该类必须初始化
对类进行反射调用时必须初始化
动态语言支持java.lang.invoke.MethodHandle解析的结果为REF_getstaticREF_putstaticREF_invokestatic的方法句柄时,该类必须初始化

原网站

版权声明
本文为[涛涛英语学不进去]所创,转载请带上原文链接,感谢
https://blog.csdn.net/niTaoTaoa/article/details/126254772