JVM(二)——垃圾回收

三、垃圾回收

1、如何判断对象可以回收

1)引用计数法

定义:
在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一;任何时刻计数器为零的对象就是不可能再被使用的。
但是存在循环引用的问题:
在这里插入图片描述

2)可达性分析算法

一系列称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系向下搜索,搜索过
程所走过的路径称为“引用链”(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,
或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。

在Java技术体系里面,固定可作为GC Roots的对象包括以下几种:

  • 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的
    参数、局部变量、临时变量等
  • 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
  • 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。
  • 在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
  • Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如
    NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。

3)四种引用

在这里插入图片描述

  1. 强引用:强引用是最传统的“引用”的定义,是指在程序代码之中普遍存在的引用赋值,即类似“Object
    obj=new Object()”这种引用关系。无论任何情况下,只要强引用关系还存在,垃圾收集器就永远不会回收掉被引用的对象。

  2. 软引用 (SoftReference):在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象
    可以配合引用队列来释放软引用自身

  3. 弱引用 (WeakReference):在垃圾回收时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。
    可以配合引用队列来释放弱引用自身

  4. 虚引用 (PhantomReference):无法通过虚引用获取一个对象实例。必须配合引用队列使用,主要配合 ByteBuffer 使用,被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存

  5. 终结器引用 (FinalReference):无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize方法,第二次 GC 时才能回收被引用对象

软引用的例子:
设置堆内存大小为 20m,直接list.add(byte[]),会报 java.lang.OutOfMemoryError: Java heap space 异常,因为是强引用,无法垃圾回收。
使用 SoftReference 可以添加到 list 集合中,不会报错,但是执行到内存不足时,会进行垃圾回收。

/*** 演示软引用* -Xmx20m -XX:+PrintGCDetails -verbose:gc*/
public class Demo2_3 {private static final int _4MB = 4 * 1024 * 1024;public static void main(String[] args) throws IOException {/*List<byte[]> list = new ArrayList<>();for (int i = 0; i < 5; i++) {list.add(new byte[_4MB]);}System.in.read();*/soft();}public static void soft() {// list --> SoftReference --> byte[]List<SoftReference<byte[]>> list = new ArrayList<>();for (int i = 0; i < 5; i++) {SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB]);System.out.println(ref.get());list.add(ref);System.out.println(list.size());}System.out.println("循环结束:" + list.size());for (SoftReference<byte[]> ref : list) {System.out.println(ref.get());	// null}}
}

在这里插入图片描述
输出结果中软引用被为null,可以通过引用队列释放软引用自身

/*** 演示软引用, 配合引用队列*/
public class Demo2_4 {private static final int _4MB = 4 * 1024 * 1024;public static void main(String[] args) {List<SoftReference<byte[]>> list = new ArrayList<>();// 引用队列ReferenceQueue<byte[]> queue = new ReferenceQueue<>();for (int i = 0; i < 5; i++) {// 关联了引用队列, 当软引用所关联的 byte[]被回收时,软引用自己会加入到 queue 中去SoftReference<byte[]> ref = new SoftReference<>(new byte[_4MB], queue);System.out.println(ref.get());list.add(ref);System.out.println(list.size());}// 从队列中获取无用的 软引用对象,并移除Reference<? extends byte[]> poll = queue.poll();while( poll != null) {list.remove(poll);poll = queue.poll();}System.out.println("===========================");for (SoftReference<byte[]> reference : list) {System.out.println(reference.get());}}
}

在这里插入图片描述

弱引用的例子:

/*** 演示弱引用* -Xmx20m -XX:+PrintGCDetails -verbose:gc*/
public class Demo2_5 {private static final int _4MB = 4 * 1024 * 1024;public static void main(String[] args) {//  list --> WeakReference --> byte[]List<WeakReference<byte[]>> list = new ArrayList<>();for (int i = 0; i < 10; i++) {WeakReference<byte[]> ref = new WeakReference<>(new byte[_4MB]);list.add(ref);for (WeakReference<byte[]> w : list) {System.out.print(w.get()+" ");}System.out.println();}System.out.println("循环结束:" + list.size());}
}

2、垃圾回收算法

1)标记清除(Mark-Sweep)

  • 速度较快
  • 会造成内存碎片

在这里插入图片描述

2)标记整理(Mark-Compact)

  • 速度慢
  • 没有内存碎片

在这里插入图片描述

3)复制(Copy)

  • 不会有内存碎片
  • 需要占用两倍内存空间

在这里插入图片描述

3、分代垃圾回收

在这里插入图片描述

老年代:常时间使用的对象,垃圾回收很久发生一次。单线程的垃圾收集器Serial Old(串行),多线程并发Parallel Old(吞吐量优先)使用的垃圾回收方法是 标记-整理,多线程并发的垃圾收集器CMS(Concurrent Mark Sweep)(响应时间优先)使用的垃圾回收方法是 标记-清除

新生代:经常需要更换的对象,垃圾回收频繁,"朝生夕灭"的特点。垃圾收集器(Serial)使用的方法是 复制

  • 对象首先分配在伊甸园区域
  • 新生代空间不足时,触发 minor gc,伊甸园和 from 存活的对象使用 copy 复制到 to 中,存活的
    对象年龄加 1并且交换 from to
  • minor gc 会引发 stop the world,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行
  • 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit)
  • 当老年代空间不足,会先尝试触发 minor gc,如果之后空间仍不足,那么触发 full gc,STW的时间更长

1) 相关JVM参数

在这里插入图片描述

4、垃圾回收器

  1. 串行
    • 单线程
    • 堆内存较小,适合个人电脑
  2. 吞吐量优先
    • 多线程
    • 堆内存较大,多核 cpu
    • 让单位时间内,STW 的时间最短 0.2 0.2 = 0.4,垃圾回收时间占比最低,这样就称吞吐量高
  3. 响应时间优先
    • 多线程
    • 堆内存较大,多核 cpu
    • 尽可能让单次 STW 的时间最短 0.1 0.1 0.1 0.1 0.1 = 0.5

1)串行

-XX:+UseSerialGC = Serial + SerialOld
在这里插入图片描述

Serial收集器:它只会使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强
调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。它依然是HotSpot虚拟机运行在客户端模式下的默认新生代收集器,有着优于其他收集器的地方,那就是简单而高效(与其他收集器的单线程相比),对于内存资源受限的环境,它是所有收集器里额外内存消耗(Memory Footprint)最小的。

2)吞吐量优先

-XX:+UseParallelGC ~ -XX:+UseParallelOldGC
-XX:+UseAdaptiveSizePolicy
-XX:GCTimeRatio=ratio
-XX:MaxGCPauseMillis=ms
-XX:ParallelGCThreads=n
在这里插入图片描述

吞吐量 = 运行用户代码时间 / (运行用户代码时间 + 运行垃圾收集时间)

Parallel Scavenge收集器:老年代的垃圾回收器,支持多线程并发收集,基于标记-整理算法实
现。

3)响应时间优先

-XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld
-XX:ParallelGCThreads=n ~ -XX:ConcGCThreads=threads
-XX:CMSInitiatingOccupancyFraction=percent
-XX:+CMSScavengeBeforeRemark
在这里插入图片描述

CMS(Concurrent Mark Sweep) 收集器: 是一种低延迟、响应时间优先的收集器。目前很大一部分的Java应用集中在互联网网站或者基于浏览器的B/S系统的服务端上,这类应用通常都会较为
关注服务的响应速度,希望系统停顿时间尽可能短,以给用户带来良好的交互体验。CMS收集器就非
常符合这类应用的需求。

5、G1 (Garbage First)

使用场景:

  • 同时注重吞吐量(Throughput)和低延迟(Low latency),默认的暂停目标是 200ms
  • 超大堆内存,会将堆划分为多个大小相等的 Region
  • 整体上是标记 + 整理算法,两个区域之间是复制算法
    相关 JVM 参数
    -XX:+UseG1GC
    -XX:G1HeapRegionSize=size
    -XX:MaxGCPauseMillis=time

1)G1 垃圾回收阶段

在这里插入图片描述

2)Young Collection

会 STW
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3)Young Collection + CM(并发标记)

  • 在 Young GC 时会进行 GC Root 的初始标记
  • 老年代占用堆空间比例达到阈值时,进行并发标记(不会 STW),由下面的 JVM 参数决定

-XX:InitiatingHeapOccupancyPercent=percent (默认45%)
在这里插入图片描述

4)Mixed Collection

会对 E(伊甸园区)、S(幸存区)、O(老年代)进行全面垃圾回收

  • 最终标记(Remark)会 STW
  • 拷贝存活(Evacuation)会 STW

-XX:MaxGCPauseMillis=ms
在这里插入图片描述

5)Full GC

  • SerialGC
    • 新生代内存不足发生的垃圾收集 -minor gc
    • 老年代内存不足发生的垃圾 - full gc
  • ParallelGC
    • 新生代内存不足发生的垃圾收集 - minor gc
    • 老年代内存不足发生的垃圾收集 - full gc
  • CMS
    • 新生代内存不足发生的垃圾收集 - minor gc
    • 老年代内存不足
  • G1
    • 新生代内存不足发生的垃圾收集 - minor gc
    • 老年代内存不足

6)跨代引用

  • 新生代回收的跨代引用(老年代引用新生代)问题
    在这里插入图片描述
  • 卡表与 Remembered Set
  • 在引用变更时通过 post-write barrier + dirty card queue
  • concurrent refinement threads 更新 Remembered Set

在这里插入图片描述

7)Remark

  • pre-write barrier + satb_mark_queue

重新标记阶段
在垃圾回收时,收集器处理对象的过程中

  • 黑色:已被处理,需要保留的
  • 灰色:正在处理中的
  • 白色:还未处理的
    在这里插入图片描述

6、垃圾调优

C:\Program Files\Java\jdk1.8.0_271\bin\java -XX:+PrintFlagsFinal -version | findstr "GC"
可以根据参数去查询具体的信息

1)调优领域

  • 内存
  • 锁竞争
  • cpu 占用
  • io
  • gc

2)确定目标

低延迟/高吞吐量? 选择合适的GC

  • CMS,G1, ZGC
  • ParallelGC
  • Zing

3) 最快的 GC是不发生 GC

  • 查看 FullGC 前后的内存占用,考虑下面几个问题
    • 数据是不是太多?
      • resultSet = statement.executeQuery(“select * from 大表 limit n”)
    • 数据表示是否太臃肿?
      • 对象图
      • 对象大小 16 Integer 24 int 4
    • 是否存在内存泄漏?
      • static Map map =
      • 第三方缓存实现

4)新生代调优

  • 新生代的特点
    • 所有的 new 操作的内存分配非常廉价
      • TLAB thread-lcoal allocation buffer
    • 死亡对象的回收代价是零
    • 大部分对象用过即死
    • Minor GC 的时间远远低于 Full GC

一般对新生代调优,可以调优的空间大

  • 越大越好吗?
    -Xmn (设置堆空间中新生代的内存大小)
    Sets the initial and maximum size (in bytes) of the heap for the young generation (nursery). GC is
    performed in this region more often than in other regions. If the size for the young generation is
    too small, then a lot of minor garbage collections are performed. If the size is too large, then only
    full garbage collections are performed, which can take a long time to complete. Oracle recommends that you keep the size for the young generation greater than 25% and less than 50% of the overall heap size.

回答:新生代的空间越大,吞吐量越大,(Full GC的次数少了),但是空间大到一定程度时,吞吐量会下降,因为空间太大,一旦触发 Full GC 时,垃圾回收时间会大大增加。

  • 新生代能容纳所有【并发量 * (请求-响应)】的数据
  • 幸存区大到能保留【当前活跃对象 + 需要晋升对象】
  • 晋升阈值配置得当,让长时间存活的对象尽快晋升
    -XX:MaxTenuringThreshold=threshold
    -XX:+PrintTenuringDistrubution
    

5)老年代调优

以 CMS 为例

  • CMS 的老年代内存越大越好
  • 先尝试不做调优,如果没有 Full GC 那么已经…,否则先尝试调优新生代
  • 观察发生 Full GC 时老年代内存占用,将老年代内存预设调大 1/4 ~ 1/3
    • -XX:CMSInitiatingOccupancyFraction=percent 值越低,老年代触发垃圾回收的时机越早

6)案例

案例1:Full GC 和 Minor GC 频繁
案例2:请求高峰期发生 Full GC,单次暂停时间特别长(CMS)
案例3:老年代充裕情况下,发生 Full GC(jdk1.7前)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/286450.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java面试篇:Redis使用场景问题(缓存穿透,缓存击穿,缓存雪崩,双写一致性,Redis持久化,数据过期策略,数据淘汰策略)

目录 1.缓存穿透解决方案一:缓存空数据解决方案二&#xff1a;布隆过滤器 2.缓存击穿解决方案一:互斥锁解决方案二:设置当前key逻辑过期 3.缓存雪崩1.给不同的Key的TTL添加随机值2.利用Redis集群提高服务的可用性3.给缓存业务添加降级限流策略4.给业务添加多级缓存 4.双写一致性…

MySQL substr函数使用详解

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

景联文科技上新高质量大模型训练数据!

在过去的一年中&#xff0c;人工智能领域呈现出了风起云涌的态势&#xff0c;其中模型架构、训练数据、多模态技术、超长上下文处理以及智能体发展等方面均取得了突飞猛进的发展。 在3月24日举办的2024全球开发者先锋大会的大模型前沿论坛上&#xff0c;上海人工智能实验室的领…

c语言--内存函数的使用(memcpy、memcmp、memset、memmove)

目录 一、memcpy()1.1声明1.2参数1.3返回值1.4memcpy的使用1.5memcpy模拟使用1.6注意 二、memmove()2.1声明2.2参数2.3返回值2.4使用2.5memmove&#xff08;&#xff09;模拟实现 三、memset3.1声明3.2参数3.3返回值3.4使用 四、memcmp()4.1声明4.2参数4.3返回值4.4使用 五、注…

MySQL-extra常见的额外信息

本文为大家介绍MySQL查看执行计划时&#xff0c;extra常见的额外信息 Using index 表示使用了覆盖索引&#xff0c;即通过索引树可以直接获取数据&#xff0c;不需要回表。 表结构: CREATE TABLE t1 (id int(11) NOT NULL AUTO_INCREMENT,name varchar(255) DEFAULT NULL,ag…

IP SSL证书注册流程

使用IP地址申请SSL证书&#xff0c;需要用公网IP地址申请&#xff0c;申请之前确保直接的IP地址可以开放80或者443端口两者选择1个就好&#xff0c;端口不需要一直开放&#xff0c;只要认证的几分钟内开放就可以了&#xff0c;然后IP地址根目录可以上传txt文件。 IP SSL证书认…

vue3+vite - 报错 import.meta.glob() can only accept string literals.(详细解决方案)

报错说明 在vue3+vite项目中,解决报错: [plugin:vite:import-analysis] import.meta.glob() can only accept string literals. 如果我们报错差不多,就可以完美搞定这个错误。 解决教程 这个错误,是因为

【STM32嵌入式系统设计与开发】——9Timer(定时器中断实验)

这里写目录标题 一、任务描述二、任务实施1、ActiveBeep工程文件夹创建2、函数编辑&#xff08;1&#xff09;主函数编辑&#xff08;2&#xff09;USART1初始化函数(usart1_init())&#xff08;3&#xff09;USART数据发送函数&#xff08; USART1_Send_Data&#xff08;&…

蓝桥杯学习笔记(贪心)

在很久很久以前&#xff0c;有几个部落居住在平原上&#xff0c;依次编号为1到n。第之个部落的人数为 t 有一年发生了灾荒&#xff0c;年轻的政治家小蓝想要说服所有部落一同应对灾荒&#xff0c;他能通过谈判来说服部落进行联台。 每次谈判&#xff0c;小蓝只能邀请两个部落参…

HarborCDN技术分析

一、介绍 简要介绍 ​​Harbor​​ 是由VMware公司开源的企业级的Docker Registry管理项目&#xff0c;它包括权限管理(RBAC)、LDAP、日志审核、管理界面、自我注册、镜像复制和中文支持等功能。Harbor 的所有组件都在 Dcoker 中部署&#xff0c;所以 Harbor 可使用 Docker C…

php反序列化刷题1

[SWPUCTF 2021 新生赛]ez_unserialize 查看源代码想到robots协议 看这个代码比较简单 直接让adminadmin passwdctf就行了 poc <?php class wllm {public $admin;public $passwd; }$p new wllm(); $p->admin "admin"; $p->passwd "ctf"; ec…

Redis中的事件

事件 概述 Redis服务器是一个事件驱动程序:服务器需要处理以下两类事件: 1.文件事件(file event):Redis服务器通过套接字与客户端(或者其他Redis服务器)进行连接&#xff0c;而文件事件就是服务器对套接字操作的抽象。服务器与客户端(或者其他服务器)的通信会产生相应的文件…

java串口接收和发送消息集成Springboot

写在前面&#xff1a;1、jdk我用的1.8.0_31 ,不能用太高的java版本。 2、&#xff08;1&#xff09;将rxtxParallel.dll和rxtxSerial.dll文件放到${JAVA_HOME}&#xff08;jdk目录,不是jre目录&#xff09;\jre\bin目录下 如&#xff1a; C:\Program Files\Java\jdk1.8.0_31\…

1升级powershell后才能安装WSL2--最后安装linux--Ubuntu 22.04.3 LTS

视频 https://www.bilibili.com/video/BV1uH4y1W7UX特殊开启–Hyper-V虚拟机 把一下代码保存到【a.bat】的执行文件中&#xff0c;进行Hyper-V虚拟机的安装开启【Windows 批处理文件 (.bat)】 pushd "%~dp0" dir /b %SystemRoot%\servicing\Packages\*Hyper-V*.mu…

fifo ip核 ————读写时钟同步

1.原理 timescale 1ns/1ns module tb_fifo();reg sys_clk ; reg sys_rst_n ; reg [7:0] pi_data ; reg rd_req ; reg wr_req ; reg [2:0] cnt;wire empty ; wire full ; wire [7:0] po_data ; wire [7:0] usedw ;initial begins…

203基于matlab的曲柄滑块机构的运动学仿真分析GUI

基于matlab的曲柄滑块机构的运动学仿真分析GUI&#xff0c;包括《系统仿真与matlab》综合试题文档。分析滑块速度、角速度&#xff0c;曲轴投影长。曲柄滑块机构的动画。程序已调通&#xff0c;可直接运行。 203 曲柄滑块机构 运动学仿真分析 - 小红书 (xiaohongshu.com)

SQLiteC/C++接口详细介绍sqlite3_stmt类(十一)

返回&#xff1a;SQLite—系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;十&#xff09; 下一篇&#xff1a; SQLiteC/C接口详细介绍sqlite3_stmt类&#xff08;十二&#xff09; 43、sqlite3_reset sqlite3_reset 函数用于重置已经编…

Linux:http协议初步认识

文章目录 OSI七层模型http协议域名路径信息请求和响应 编写一个httpserver OSI七层模型 在结束了前面对于序列化反序列化等内容的学习后&#xff0c;重新回到对于OSI模型的部分 如上所示的是对于OSI接口的示意图&#xff0c;在这当中可以看到会话层的概念&#xff0c;会话层的…

word文件如何转PDF格式?word转PDF的方法

在当今数字化时代&#xff0c;文档格式的转换已成为日常生活和工作中不可或缺的一部分。其中&#xff0c;将Word文档转换为PDF格式更是受到了广大用户的青睐。本文将详细介绍Word转PDF的方法&#xff0c;帮助读者轻松实现文档格式的转换&#xff0c;并探讨转换过程中的注意事项…

LeetCode 61. 旋转链表

给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[4,5,1,2,3] 示例 2&#xff1a; 输入&#xff1a;head [0,1,2], k 4 输出&#xff1a;[…