JVM相关知识
- 🌳1.JVM概述
- 🪴1.1 JVM作用:
- 🪴1.2JVM构成
- 🪴1.3JVM整体结构图
- 🌳2.类加载子系统
- 🪴2.1作用
- 🪴2.2类加载过程
- 🌱2.2.1加载:
- 🌱2.2.2连接
- 🌱2.2.3初始化
- 🪴2.3类加载器
- 🪴2.4双亲委派机制
- 🌳3.运行时数据区
- 🪴3.1组成:
- 🪴3.2程序计数器
- 🪴3.3虚拟机栈
- 🌱栈帧构成
- 🪴3.4本地方法栈
- 🪴3.5堆(存储空间)
- 🌱堆分区
- 🌱对象创建与内存分配过程
- 🪴3.6方法区
- 🌳4.本地方法接口
- 🌳5.执行引擎
- 🌳垃圾回收
- 🪴垃圾回收算法
- 🌱标记阶段
- 🌱对象的finalize()方法
- 🌱回收算法
- 🪴垃圾回收器:
🌳1.JVM概述
🪴1.1 JVM作用:
1.jvm负责将字节码文件加载虚拟机并将字节码文件解释/编译为机器码,管理数据存储与垃圾回收,
2.jvm还可以将其他语言的字节码文件进行编译
🪴1.2JVM构成
1.类加载器
负责从硬盘加载字节码文件到JVM
2.运行时数据区
数据进行分区储存
3.执行引擎
将字节码再次编译/解释为机器码
4.本地库接口
负责调用本地操作系统方法
🪴1.3JVM整体结构图
🌳2.类加载子系统
🪴2.1作用
负责从硬盘上加载字节码文件
到JVM(运行时数据区)
🪴2.2类加载过程
🌱2.2.1加载:
以二进制字节流形式
加载字节码,在内存生成一个Class类对象,将静态存储改为运行时存储
🌱2.2.2连接
1.验证
验证字节码格式是否正确,验证语法是否正确
2.准备
为类的静态属性分配内存,并设置初始值
3.解析
将静态文件的指令由符号引用
替换为直接引用
注:符号引用是 Class 文件的逻辑符号,直接引用指向的方法区中某一个地址
🌱2.2.3初始化
对类的静态变量赋初值
注:使用类的静态变量或静态方法,在一个类中运行main方法,创造对象,使用反射加载类,类会被初始化
🪴2.3类加载器
宏观分类:
1.引导类加载器(启动类加载器),C/C++ jvm底层实现
2.其他类加载器,用java写的实现类,继承java.lang.ClassLoader
微观分类:
1.引导类类加载器(启动类加载器)
java中系统提供的类,都有启动类加载器加载例如String
2.扩展类加载器
java语言编写,由sun.misc.Launcher$ExtClassLoader
实现. 派生于 ClassLoader 类.
3.应用程序类加载器
Java 语言编写的,由sun.misc.LauncherAppClassLoader
实现. 派生于 ClassLoader 类. 加载我们自己定义的类,用于加载用户类路径(classpath)上所有的类
4.自定义类加载器
例如我们自己写一个类继承ClassLoder
注:例如tomcat这种容器,都会有自己加载类的加载器
🪴2.4双亲委派机制
原理:
. 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行. 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器. 如果父类加载器可以完成类的加载任务,就成功返回,倘若父类加载器无法完成加载任务,子加载器才会尝试自己去加载,这就是双亲委派机制.
注:如果均加载失败,就会抛出ClassNotFoundException
异常
优点:
避免子级开发的类替换系统中的类
如何打破:
自定义类加载器,重写ClassLoader类中findClass()
🌳3.运行时数据区
🪴3.1组成:
方法区.程序计数器.本地方法栈,虚拟机栈,堆
🪴3.2程序计数器
作用:
用来记录程序执行指令位置
特点:
内存空间小,运行速度快,线程私有(每个线程都有一个计数器)生命周期与线程一致,不会出现内存溢出与垃圾回收
🪴3.3虚拟机栈
概念:
栈是运行单位(保存一个个栈帧),管理方法(java自己写的方法)
特点:
运行调用方法入栈,运行完成出栈,线程私有,没有垃圾回收,但存在内存溢出,一个方法入栈后,可以看做一个栈帧不同线程中所包含的栈帧(方法)是不允许存在相互引用的,即不可能在一个栈中引用另一个线程的栈帧(方法).
🌱栈帧构成
🍃1.局部变量表:存储定义变量,参数
🍃2.操作数栈:运算操作
🍃3.方法返回地址
注:当一个方法执行完毕之后,要返回之前调用它的地方,因此在栈帧中必须保
存一个方法返回地址
🪴3.4本地方法栈
管理本地方法地方
本地方法:native关键字修饰的方法,没有方法体
例子:
Object native hashCode() 内存地址 native getClass();native Object clone()native void notify();native void notifyAll();native void wait(long timeout)FileInputStream native int read0()Thread native void start0();
特点:线程私有,会出现内存溢出,不会有垃圾回收
🪴3.5堆(存储空间)
概述:
存放程序对象,是运行时数据区最大空间,大小可调节,线程共享,内存溢出,进行垃圾回收,是垃圾回收器重点回收区域
🌱堆分区
🍃新生代:
伊甸园区:存放刚刚创建的对象
幸存者1:存放伊甸园区和另一个幸存者区经过垃圾回收存活的对象
幸存者2:存放伊甸园区和另一个幸存者区经过垃圾回收存活的对象
注:两个幸存者区可以交替使用,始终有一个区域空闲
🌴老年代:
存放生命周期长/非常大的对象
注 :经历15
次垃圾回收依然存活的对象,就会被存放到老年代.
分区好处:
根据对象存活时间划分,生命较长的对象放在老年代减少垃圾回收的频率和扫描次数
🌱对象创建与内存分配过程
🌴1.新创建的对象存放在伊甸园区
🌴2.当垃圾回收时,将伊甸园存活的对象移入到幸存者0区
🌴3.继续运行,再次创建的对象还是保存到伊甸园区
🌴4.下一次垃圾回收到来时,将伊甸园区存活的对象和幸存者0区存活的对象移入到幸存者1交替反复执行
🌴5.当一个对象经历过15次垃圾回收后仍然存活,那么就将此对象移入到老年代
注:在对象头中4个bit位用来记录回收次数.可以设置回收次数,但是最大值是15,老年代和新生代比例: 2:1,伊甸园和两个幸存者区比例: 8:1:1
🪴3.6方法区
🌴作用:主要储存加载虚拟机类信息(类字节码、
class/method/field 等元数据、static final 常量、static 变量、即时编译器编译
后的代码等数据),方法区大小可调节,线程共享,会出现内存溢出
方法区的垃圾回收:
🌴类满足三个条件被卸载
1.该类的所有对象以及子类对象都不存在,
2.加载该类的类加载器不存在了,
3.该类的Class对象没有被其他地方引用.
注:一般情况也可以认为类是不会被卸载的.
🌳4.本地方法接口
🌴1.什么样的方法是本地方法
被Native关键字修饰的方法,本地方法不是java语言实现的是由操作系统
实现.
🌴2.java中为什么会调用本地方法
因为上层的高级语言没有对底层硬件直接操作的权限
,而是需要调用操作系统的系统提供的接口进行访问.
🌳5.执行引擎
作用:
负责将装载jvm的字节码编译/解释为机器码
🌴JIT编译器
对某段代码整体编译,执行编译后的结果
☘️优点:编译需要一定时间
☘️缺点:执行效率低不能马上发挥作用
🌴解释器:对代码逐行进行解释
☘️优点:省去编译时间
☘️缺点:效率低
注:java执行引擎采用的是半解释半编译方式将字节码转换为机器码
🌴过程: 程序刚开始运行时,立即采用逐行解释执行,程序运行过程中,会将热点代码
使用编译器编译并缓存起来,这样两者结合使用,提高运行效率.
🌳垃圾回收
☘️1.什么是垃圾
一个没有被任何引用指向的对象就是一个垃圾对象,若不及时清除,会占着内存,严重的话导致内存溢出
☘️2.垃圾回收区域:
频繁回收新生代
较少回收老年代(频率低)
较少回收方法区
☘️3.内存溢出和内存泄漏
🍂内存溢出:垃圾回收后,内存依然不够用,导致系统崩溃
🍂内存泄漏:一个对象在程序不会被使用,但垃圾收集器又不能回收,一直占着内存空间
☘️4. STW (stop the world)
当垃圾回收时(标记,回收),会导致其他用户线程暂停. 必须保证分析时其他程序不再运行,保证分析准确性
🪴垃圾回收算法
🌱标记阶段
作用:标记那些对象是垃圾对象
算法
🍂1.引用计数算:
在对象中保存一个计数属性,只要有引用指向该对象,计数器加以,如果计数器值结果为0,则表示此对象是垃圾对象
缺点:
计数器占用空间
加减一需要时间开销
无法解决循环引用问题
🍂2.可达性分析算法:
从一些活跃对象(GCRoots)开始搜索,与根对象相关联对象都是被使用,与根对象或根对象相关的引用链不相关对象称为垃圾对象
注:
虚拟机栈中(正在运行的方法)被引用的对象 ,类中静态属性,被用来当做同步锁,java系统中的类可以称为根对象
🌱对象的finalize()方法
Object类中 protected void finalize() throws Throwable { }
在垃圾回收之前,可以在此方法中执行一些需要逻辑
如果对象被判为垃圾时,回收之前会调用finalize()
注:finalize()方法只会被调用一次,由垃圾回收器调用
☘️对象可分为:
可触及的:不是垃圾对象
可复活的:标记为垃圾对象,但finalize()还没有被调用
不可触及的:标记为垃圾对象,但finalize()被调用了
🌱回收算法
☘️1.标记-复制算法:
有多块内存,每次有一块是空闲的,将存活的对象移动到未使用空间中,清除其他块垃圾对象
好处:内存碎片少,适合存活对象少,垃圾对象多场景(新生代)
☘️2.标记-清除算法:
将垃圾对象地址记录一个空闲列表,如果创建新对象会将空闲列表垃圾对象覆盖
优点:
不移动对象,适合老年代,回收效率高,会产生内存碎片
☘️3.标记-压缩算法
将存活对象重新排列到内存一端,讲其他区域空间进行清理
优点:移动对象,适合老年代,回收压缩,不会产生内存碎片,效率低
🍀分代收集:
根据不同的区域特点进行各自的回收
年轻代,对象生命周期短,存活对象少,回收频繁,采用复制算法
老年代,对象生命周期长,存活对象多,回收频率低,
可以采用清除和压缩两种算法混合使用.
🍀垃圾回收名词:
🍂 Minor GC: 是针对新生代的垃圾回收 频繁回收新生代
🍂Major GC: 是针对老年区进行的来及回收 较少回收老年代
🍂FULL GC: 整堆收集 实际开发中尽量避免整堆收集
注: 老年代空间不足时, 方法区空间不足时触发整堆收集
🪴垃圾回收器:
分类:
☘️从线程数量上分:
单线程: 适用于一些小的设备,只有一个线程进行垃圾回收
多线程: 有多个线程进行垃圾回收
☘️按照工作模式分:
独占式: 垃圾回收线程执行时,其他用户线程暂停执行
并发式: 垃圾回收线程可以和用户线程同时执行
☘️按工作的内存区间分:
年轻代垃圾回收器
老年代垃圾回收器
🍀CMS(Concurrent Mark Sweep,并发标记清除)
首个实现垃圾收集线程和用户线程可以同时执行
注:不是所有都是并发执行,也有独占执行
初始标记: 独占 会暂停用户线程
并发标记: 并发 与用户线程同时执行
重新标记:独占 会暂停用户线程
并发清除: 并发 与用户线程同时执行
🍀 G1(Garbage First)回收器:
特点: 适合大型服务器端,内存大,cpu更加先进的.将每个区域(伊甸园,幸存者,老年代)又划分成若干个小的区域,
哪个区域垃圾数量多,优先回收哪个区域,可以做到整堆管理收集也可以做到并发执行