JVM及垃圾回收算法

一、JVM

1、jvm的内存组成

五大内存区域,分1.7和1.8

1.堆内存:引用类型的数据,内部组成:1.新生代(伊甸区和幸存者区)2.老年代。该区域经常发生垃圾回收的操作

堆是JVM中最大的一块内存区域,用于存储对象实例和数组。堆被 所有线程 共享,用于 动态分配 内存。堆被分为新生代(Young Generation)和老年代(Old Generation)两部分,新生代又被分为Eden空间和两个Survivor空间。

2.虚拟机栈:方法在运行时,需要存储一些内容,存储到栈

虚拟机栈用于 存储方法的 局部变量、操作数栈、方法调用 和 返回等信息 。每个线程在运行时都会有一个对应的 虚拟机栈,每个方法在执行时 都会 创建一个 栈帧(Stack Frame)。

3.本地方法栈:本地方法(被native修饰的方法),方法运行时,保存的一些信息

4.程序计数器:针对线程的,记录每个线程当前的执行的行数 每个线程都有一个独立的程序计数器。

5.元空间(1.7叫做方法区):存放已被加载的类信息、常量、静态变量等信息.

JDK 1.8 同 JDK 1.7 比,最大的差别就是:元数据区 取代了 永久代。元空间的 本质 和永久代类

似,都是对 JVM 规范中方法区的实现。不过元空间与永久代之间最大的 区别 在于:

元数据空间 并 不在 虚拟机 内存 中 ,而是 使用 本地内存

2、JVM的堆内存

堆内存:分为两块:1.新生代 2.老年代

新生代:内部又分为 伊甸园区 和 幸存者区 伊甸园区:幸存者区:幸存者区 = 8:1:1

创建新对象,默认进入到伊甸区,GC在运行时,会将存活的对象放在幸存者区,如果对象存活超过15次,存储到老年代

老年代:存活时间长的对象或者大对象

新生代、老年代 所占 比例 2:1

3、jvm内存模型

Java 内存模型 (下文简称 JMM)就是在底层处理器内存模型的基础上,定义自己的多线程语义。它明确指定了一组排序规则,来保证线程间的可见性。

这一组规则被称为 Happens-Before , JMM 规定,要想保证 B 操作能够看到 A 操作的结果(无论它们是否在同一个线程),那么 A 和 B 之间必须满足 Happens-Before 关系

  • 单线程规则:一个线程中的每个动作都 happens-before 该线程中后续的每个动作

  • 监视器锁定规则 :监听器的解锁 动作 happens-before 后续对这个监听器的锁定动作

  • volatile 变量规则:对 volatile 字段的写入动作 happens-before 后续对这个字段的每个读取动作

  • 线程 start 规则 :线程 start() 方法的执行 happens-before 一个启动线程内的任意动作

  • 线程 join 规则 :一个线程内的所有动作 happens-before 任意其他线程在该线程 join() 成功返回之前

  • 传递性:如果 A happens-before B, 且 B happens-before C, 那么 A happens-before C

Java 提供了几种语言结构,包括 volatile , finalsynchronized , 它们旨在帮助程序员向编译器描述程序的并发要求,其中:

  • volatile - 保证可见性有序性

  • synchronized - 保证可见性有序性 ; 通过管程(Monitor) 保证一组动作的原子性

  • final - 通过禁止在构造函数初始化给 final 字段赋值 这两个动作的重排序,保证可见性 (如果 this 引用逃逸就不好说可见性了)

编译器在遇到这些关键字时,会插入相应的内存屏障,保证语义的正确性。

有一点需要注意 的是,synchronized 不保证 同步块内的代码禁止重排序,因为它通过锁保证同一时刻只有一个线程 访问同步块(或临界区),也就是说同步块的代码只需满足 as-if-serial 语义 - 只要单线程的执行结果不改变,可以进行重排序。

所以说**,Java 内存模型描述的是多线程对共享内存修改后彼此之间的可见性**,另外,还确保正确同步的 Java 代码可以在不同体系结构的处理器上正确运行。

二、什么是STW

STW: Stop-The-World: 是在垃圾回收算法执⾏过程当中,将JVM内存冻结、应用程序停顿的⼀种状态。

  • 在STW 状态下,JAVA的所有线程都是停⽌执⾏的 -> GC线程除外

  • 一旦Stop-the-world发生,除了GC所需的线程外,其他线程都将停止工作,中断了的线程直到GC任务结束才继续它们的任务

  • STW是不可避免的,垃圾回收算法执⾏一定会出现STW,我们要做的只是减少停顿的时间

  • GC各种算法优化的重点,就是 减少STW(暂停) ,同时这也是JVM调优的重点

三、垃圾回收的相关算法:

1、GC常用算法

标记清除算法

第一步:利用可达性去遍历内存,把存活对象、需要回收的对象标记出来;第二步:在遍历一遍,把标记过的对象回收掉。缺点:效率不高,无法清除垃圾碎片

标记整理算法

首先,把存活对象和垃圾对象进行标记,然后将所有的存活对象向一端进行移动,然后直接清除端以外的内存。特点:适用于存活对象多,垃圾少的情况;需要整理的过程,无空间碎片产生;

标记复制算法

按照容量复制两个大小相等的内存空间,当有一个用完以后,就把还存活着对象复制到另一个区域中,在清除掉用完的区域,缺点:内存使用率低,只有原来的一半空间

分代收集算法

根据内存对象的存活周期不同,将内存划分成几块,一般为新生代、老年代。

新生代一般采用复制算法,老年代一般采用标记整理算法。

2、GC如何判定对象是否回收

引用计数法

使用可达性分析算法来判定:GC Roots

3、如何判断一个对象是否存活?( 判断一个对象是不是垃圾 )

判断一个对象是否存活,分为两种算法1:引用计数法;2:可达性分析算法;

引用计数法 : 给每一个 对象设置 一个引用计数器,当有一个地方 引用该对象 的时候,引用计数器就+1,引用失效时,引用计数器就-1;当引用计数器为0的时候,就说明这个对象没有被引用,也就是垃圾对象,等待回收; ​ 缺点:无法解决 循环引用 的问题,当A引用B,B也引用A的时候,此时AB对象的引用都不为0,此时也就无法垃圾回收,所以一般主流虚拟机都不采用这个方法;

可达性分析法 从一个被称为 GC Roots的对象向下搜索 ,如果一个对象到GC Roots 没有任何 引用链 相连接时,说明此对象不可用,在java中可以作为GC Roots的对象有以下几种:

  • 虚拟机栈 中引用的对象

  • 方法区 类静态属性 引用的变量

  • 方法区 常量池 引用的对象

  • 本地方法 栈 JNI 引用的对象

但一个对象满足上述条件的时候,不会马上被回收,还需要进行 两次标记 ; 第一次标记:判断当前对象是否有 finalize()方法 并且 该方法 没有被执行过 ,若 不存在标记为垃圾对象 ,等待回收;若 的话,则进行 第二次标记 ; 第二次标记将当前对象放入 F-Queue队列 ,并生成一个finalize线程去执行该方法,虚拟机不保证该方法一定会被执行,这是因为如果线程执行缓慢或进入了死锁,会导致回收系统的崩溃;如果 执行了finalize方法之后仍然没有与GC Roots有直接或者间接的引用,则该对象会被回收;

4、被引用的对象就一定能存活吗?

不一定 ,看 Reference 类型,弱引用在 GC 时会被回收,软引用在内存不足的时候,即 OOM 前会被回收,但如果没有在 Reference Chain 中的对象就一定会被回收

相关推荐
流星5211225 小时前
GC 如何判断对象该回收?从可达性分析到回收时机的关键逻辑
java·jvm·笔记·学习·算法
JanelSirry5 小时前
我的应用 Full GC 频繁,怎么优化?
jvm
JH30735 小时前
jvm,tomcat,spring的bean容器,三者的关系
jvm·spring·tomcat
DKPT9 小时前
JVM直接内存和堆内存比例如何设置?
java·jvm·笔记·学习·spring
siriuuus9 小时前
JVM 垃圾收集器相关知识总结
java·jvm
小满、12 小时前
什么是栈?深入理解 JVM 中的栈结构
java·jvm·1024程序员节
百花~1 天前
JVM(Java虚拟机)~
java·开发语言·jvm
每天进步一点点dlb1 天前
JVM中的垃圾回收算法和垃圾回收器
jvm·算法
漫漫不慢.1 天前
蓝桥杯-16955 岁月流转
java·jvm·蓝桥杯
boy快快长大2 天前
【JVM】线上JVM堆内存报警,占用超90%
jvm