Java - GC 垃圾回收

article/2025/9/19 14:21:55

JVM中一个垃圾回收线程,它的优先级较低,正常情况下不会执行。JVM空闲或者当前内存不足时,才会触发垃圾回收线程执行,扫描内没有被引用的对象,将这些对象添加到要回收的集合中进行回收。

GC介绍

Garbage Collection 垃圾收集,监测对象是否可用进而实现自动回收对象的目的。

GC调用方法:System.gc() 或Runtime.getRuntime().gc() 。

GC作用:防止内存泄漏。

GC运行方式:作为优先级低的线程运行,不可预知的情况下清除/回收内存中已经死亡或长时间未使用的对象。程序员无法实时调用GC回收对象。

说明
-Xms | -Xmx | -Xmn堆的初始大小 | 堆的最大值 | 堆中年轻代值
-XX:-DisableExplicitGC禁用System.gc()
-XX:NewSize | -XX:MaxNewSize新生代大小 | 新生代最大大小
-XX:NewRation老生代与新生代的比例
-XX:InitialTenuringThreshold | -XX:MaxTenuringThreshold老年代阀值 | 老年代最大值

GC触发条件

方法一:引用计数器

为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用的问题;

方法二:可达性分析

从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。

引用计数器 1.为对象创建一个引用计数器,当该对象被引用时,计数器就加1;当该对象的引用被释放后,计数器就减1。 2.当计数器为0时就表明该对象没有被引用,可以被GC回收。但是无法解决循环依赖的问题。

可达性分析法

  1. 可达性分析的操作,必须确保一致性的快照中进行,保证所有引用在此期间不存在变化的情况。

  2. 可达性分析的操作会触发所有用户线程暂停(挂起),官方称为STW。

GC何时调用

  1. 对象没有引用(引用计数器=0,没有可达GC Root对象)

  2. 作用域捕获异常

  3. 程序意外终止 (例如:kill -9)

  4. 执行System.exit()

  5. 程序正常执行结束

GC如何调用

  • System.gc()

  • Runtime.getRuntime().gc()

GC 降低开销

  • 少用静态变量,静态变量属于全局变量,不会被GC回收,一直占用内存。

  • 尽量使用基本类型,而不是包装类(int / Integer),基本类型占用的资源少。

  • StringBuffer替代String来增加字符串。

  • 对象尽量不显示设为null,对象=null,会被作为垃圾处理。将不用的对象设置为null,有利于GC回收。

  • 尽量不显示调用System.gc()。System.gc()不会立即触发Full GC,但是大多时候会触发GC,从而增加了GC频率、增加了STW的次数、影响系统性能。

GC算法

标记-清除算法

第一步,标记需要回收的对象;

第二步,清除回收。

优点:操作简单方便、无需移动对象。

缺点:造成空间碎片,提高垃圾回收的频率。

 复制算法

按容量划分为2个大小相等的内存区域,当其中一块用完时将存活的对象复制到另一块中,将已使用的内存空间一次清理掉。

优点:不会产生内存碎片。

缺点:降低了内存空间的使用率,生存周期长达的对象容易被重复复制,浪费CPU资源。

标记-整理算法

标记无用对象,将所有可用的对象移动到一端,直接除掉边界外的对象。

优点:不会存在内存碎片

缺点:需要局部移动,会降低效率。

分代算法

根据对象存活周期的不同,将内存分为年轻代、老年代、永久代。

新生代基本采用复制算法,老年代采用标记-整理算。

GC Collector

描述
Serial最早的单线程串行垃圾回收器新生代
Serial OldSerial 垃圾回收器的老年版本,也是单线程的老年代
ParNewSerial 的多线程版本新生代
Parallel吞吐量优先的收集器(多线程),牺牲等待时间换取吞吐量新生代
Parallel OldParallel 老生代版本。采用标记-整理算法老年代
CMS以获得最短停顿时间为目标的收集器,适用 B/S 系统。老年代
G1兼顾吞吐量和停顿时间的 GC 实现,是 JDK 9 以后的默认 GC 选项整体
  • 串行 (必须掌握)

  • 并行(必须掌握)

  • 并发 CMS(必须掌握)

  • G1(必须掌握)

JDK1.7默认:Parallel Scavenge(新生代) + Parallel Old(老年代)

JDK1.8默认:Parallel Scavenge(新生代) + Parallel Old(老年代)

JDK1.9默认:G1

串行垃圾回收器

比喻:顾客在吃饭,服务员让其起来离开位置,需要打扫,顾客吃饭的行为被中断。

  • 单线程环境,只运行一个线程回收垃圾。

  • 回收时会暂停所有的用户线程 STW

并行垃圾回收器

比喻:客在吃饭,出现几个清洁工打扫卫生,顾客先出去,一会回来用餐。

  • 多个垃圾收集线程执行垃圾收集工作

  • 用户线程暂停 STW

CMS 并发垃圾回收器

比喻:顾客在吃饭,出现几个清洁工打扫卫生,顾客先去1号卓用餐,一会回到原位置。

  • 垃圾收集与用户线程同时运行

 串行垃圾回收器

  • JVM 开启串行垃圾回收器:-XX:+UseSerialGC

  • JVM开启串行垃圾回收器之后,新生代、老年代会使用相关的组合

    • 新生代:Serial,使用复制算法

    • 老年代:Serial Old,使用标记-整理算法

并行垃圾回收器

新生代并行收集器,是Serial收集器的多线程版本,在多核cpu环境下使用。

  • JVM开启并行垃圾回收器:-XX:+UseParNewGC

  • JVM开启并行垃圾回收器之后,新生代、老年代会使用相关的组合

    • 新生代:ParNew,使用复制算法

    • 老年代:Serial Old,使用标记-整理算法

并发垃圾回收器

又称为吞吐量收集器(Throughout Collector),追求高吞吐量、高效运行CPU。

吞吐量=用户线程时间/(用户线程时间+垃圾回收时间)

使用场景:订单处理、工资支付等

  • -XX:+UseParallelGC:开启此模式,使用ParallelScavenge + Serial Old组合进行垃圾回收。

  • -XX:+UseParallelOldGC: 开启此模式,使用Parallel Scavenge + Parallel Old的收集器组合进行垃圾回收。

CMS

老年代并行收集器,追求最短卡顿时间。

CMS执行步骤

  1. 初始标记 STW ①

  2. 并发标记 ②

  3. 预清理

  4. 重新标记 STW ③

  5. 并发清除 ④

  6. 重置

1. 初始标记 STW
标记从GC Roots直接可达的老年代对象、新生代引用的老年代对象。
整个过程是单线程,其他线程会被暂停执行。

2. 并发标记
开始tracing,标记可达对象。
垃圾回收线程与应用程序并行执行,GC Roots对象会发生改变(例如:对象从新生代进入老年代,老年代中的引用发生改变,之前的引用被标记为dirty)。

3. 预清理

4. 重新标记 STW
暂停所有应用线程,重新标记并发标记遗漏的对象。

5. 并发清除
激活应用程序线程,将未被标记为存活的对象记为不可达。

6. 重置
CMS内部重置回收器状态,准备进入下一个回收周期。

CMS收集器在Minor GC时会暂停所有的应用线程,并以多线程的方式进行垃圾回收。在Full GC时不再暂停应用线程,而是使用若干个后台线程定期的对老年代空间进行扫描,及时回收其中不再使用的对象。

G1特点

  • 不产生内存碎片。G1会压缩空闲内存使之足够紧凑,做法是用regions代替细粒度的空闲列表进行分配,减少内存碎片的产生。

  • 可精确控制停顿时间。G1的STW更可控,G1在停顿时间上添加了预测机制,用户可以指定期望停顿时间,避免同一时刻回收垃圾过多,造成雪崩。

G1开启

  • 第一步:开启G1垃圾收集器 -XX:+UseG1GC

  • 第二步:设置堆的最大内存 -XX Xms 32g

  • 第三步:设置最大停顿时间 -XX:MaxGCPauseMillis=100

G1 Region

G1取消了年轻代、老年代的物理划分,将堆划分为若干个region,这些region包含了逻辑上的年轻代、老年代区域。

 

Humongous 巨大: 对象占用空间超过分区容量50%,G1认为这个对象是一个巨型对象。Humongous区专门存放巨型对象,一个H区装不下一个巨型对象,G1会寻找连续的H区来存储。

寻找连续的H区时,会触发Full GC。

G1全局并发标记

第一步:初始标记 (initial mark, STW):标记从根节点直接可达的对象,该阶段执行一次年轻代GC,产生STW。

第二步:根区域扫描(root region scan)

  • 在初始标记活动区扫描对老年代的引用,标记被引用的对象。

  • 该阶段与程序同时运行,只有完成该阶段,才能开始下一次STW年轻代垃圾回收。

第三步:并发标记(Cocurrent Marking):G1 GC在整个堆中查找可访问(存活的)的对象,该阶段与程序同时运行,可以被STW年轻代垃圾回收中断。

第四步:重新标记(Remark, STW):产生STW,针对上一次的标记进行修正。

第五步:清除垃圾(Cleanup,STW):清点和重置标记状态,产生STW。该阶段实际不会做GC,等待evacuation阶段来回收。

G1优化

G1 GC吞吐量目标:90%应用程序时间 + 10% 垃圾回收时间

G1模式

1. Young GC

Eden区使用达到阀值,并且无法申请足够内存时会触发Young GC。

每次Young GC会回收所有Eden和Survior区,将存活对象复制到老年代和Survior区。

2. Mixed GC

收集整个Young Gen和部分Old Gen。G1独有的模式

MixedGC是G1GC特有的,跟FullGC不同的是MixedGC只回收部分老年代的Region。

MixedGC一般会发生在一次YoungGC后面,为了提高效率,MixedGC会复用YoungGC的全局的根扫描结果,因为这个STW过程是必须的,整体上来说缩短了暂停时间。

GC 练习

public class Math {public static final int initData = 666;public static User user = new User(); // 实际在堆中分配内存空间,元空间中user指向堆中user的实际地址。public int math() {int a = 1;int b = 2;int c = (a+b) * 10;return c;}public static void main(String[] args) {Math demo1 = new Math(); // 实际在堆中分配地址,局部变量表中math是堆中math的存储地址的引用。demo1.math();}
}
class User{}// math()对应的汇编代码如下public int math();Code:0: iconst_11: istore_12: iconst_23: istore_24: iload_15: iload_26: iadd7: bipush        109: imul10: istore_311: iload_312: ireturn

Math.java 操作数栈过程

 Math.java内存分配过程

Math.class 加载过程

0.加载:将bin-JVM-Math.class加载至JVM内存区域加载方式:懒加载war包有1万个文件,实际使用不足10%。实际,90%的文件不会被加载(未使用)。Math.class加载至JVM内存之前有一些列步骤:1. 验证Math.class第1行:cafe babe 0000 0034 0029 0700 0201 0008如果修改内容:cafe babe 0000 0034 0029 0700 0201 1118新的字节码文件不符合格式,验证格式错误。则不加载Math.class2.准备将静态变量赋予默认值(JVM规定:boolean->false, Integer->0, Object->null 等)3.解析3.1 静态链接:符号引号替换为直接引用。(main被称为符号,直接引用就是内存地址。由方法名获取变为直接通过物理地址获取。)
3.2 动态链接:在程序运行期间完成的将符号引用替换为直接引用。4.初始化实际赋值,initData 从0变为666。

http://chatgpt.dhexx.cn/article/qSM5X1AS.shtml

相关文章

GC是如何判断一个对象为垃圾的?被GC判断为垃圾的对象一定会被回收吗?

一.GC如何判断一个对象为”垃圾”的 java堆内存中存放着几乎所有的对象实例,垃圾收集器在对堆进行回收前,第一件事情就是要确定这些对象之中哪些还“存活”着,哪些已经“死去”。那么GC具体通过什么手段来判断一个对象已经”死去”的? 1.引用计数算法(…

基于28181协议的视频与平台的对接

问题描述 将IPC摄像头接入平台进行观看,或使用28181/rtsp/rtmp/flv等协议将视频流分发到其他平台。 解决方案 WVP-PRO(基于GB/T 28181-2016标准实现的流媒体平台),依托优秀的开源流媒体服务ZLMediaKit,提供完善丰富…

GB28181协议实现系列之----SDK Demo发布(7)

GB28181在终端的应用越来越广,但是一般很多做终端的厂家要加入到互联互通的GB28181网络协议栈中都必须招聘一些专业做GB28181的开发人员,并且需要对接各厂家的兼容性开发及对接调试工作,或是修改一些定制功能。安防市场一年有千亿以上的产值&…

GB28181协议之设备录像查询

目录 一、概述 二、GB28181录像文件查询 2.1 录像文件基本要求 2.2 命令流程 2.3 抓包文件抓图示例 三、国标平台介绍 一、概述 近年来,国内视频监控应用发展迅猛,系统接入规模不断扩大,涌现了大量平台提供商,平台提供商的接…

ZLMediaKit+wvp-GB28181-pro,搭建28181协议视频平台

ZLMediaKitwvp-GB28181-pro,搭建28181协议视频平台 一,简介 ZLMediaKit:实现RTSP/RTMP/HLS/HTTP协议的轻量级流媒体框架,支持大并发连接请求 一个基于C11的高性能运营级流媒体服务框架,项目地址:ZLMediaKit 特点: 基…

java gb28181网关_视频网关GB28181协议转换解决方案

原标题:视频网关GB28181协议转换解决方案 应用需求及问题: 四川某一所学校,校园内装置有上百个摄像头,学校监控系统是RTSP协议,现在需求要把校园的监控系统视频画面接入到公安系统里, 可公安系统只支持GB-T…

GB28181协议--校时

1、简介 根据《GB/T 28181 —2016》7.10、9.10的要求,GB28181设备网络校时功能描述如下: 联网系统内的IP 网络服务器设备宜支持 NTP(见IETF RFC2030) 协议的网络统一校时服务。 网络校时设备分为时钟源和客户端, 支持客户/服务器的工作模式; 时钟源应支…

GB28181协议常见几种信令流程(一)

在监控系统中,常见的摄像机设备互联协议有国际标准的ONVIF、国标的GB28181和各厂商的私有连接协议,本章从GB28181-2016中摘抄整理常见信令流程,如28181的会话/媒体通道、SIP基本注册/注销流程、客户端主动发起视频点播流程、设备控制流程等。…

国标GB28181协议客户端开发(一)整体流程和技术选型

国标GB28181协议客户端开发(一)整体流程和技术选型 本系列文章将介绍国标GB28181协议设备端的开发过程。本文旨在探讨整体设计和技术选型方面的考虑,为开发人员提供指导和参考。文章将从设备端开发的整体架构、信令交互流程以及关键技术选型等…

java gb28181网关_国标GB28181协议对接网关

国标GB28181网关概述 国标GB28181《安全防范视频监控联网系统信息传输、交换、控制技术要求》最新版为2016年版,较2011年版更加完善,其是基于SIP协议的视频联网框架标准。因其核心应用于公安视频联网,在平安城市、雪亮工程超大规模视频监控联…

GB28181协议之录像回放

目录 一、概述 二、录像文件播放 2.1 基本要求 2.2 命令流程 2.3 抓包示例 2.3.1 Invite请求[SIP服务器---->设备] 2.3.2 Invite应答[设备---->SIP服务器] 2.3.3 ACK[SIP服务器---->设备] 2.3.4 Bye结束回放 2.3.5 Bye应答 2.3.6 播放速度控制 2.3.6 控制…

GB28181协议之实时视频

目录 一、概述 二、实时视频 2.1基本要求 2.2命令流程 2.3抓包示例 2.3.1 Invite请求[SIP服务器----->设备] 2.3.2 Invite应答[设备---->SIP服务器] 2.3.3 ACK[SIP服务器----->设备] 2.3.4 Bye 2.3.5 Bye应答 三、国标平台介绍 一、概述 近年来,国…

GB28181协议--实时视音频点播(预览)

1、基本要求 根据《GB/T 28181 —2016》第9章关于实时视音频点播的描述,其内容如下: 实时视音频点播的SIP 消息应通过本域或其他域的SIP 服务器进行路由、 转发, 目标设备的实时视音频流宜通过本域内的媒体服务器进行转发。实时视音频点播采用SIP 协议…

GB28181协议--心跳

1、心跳介绍: 根据《GBT 28181-2016 公共安全视频监控联网系统信息传输、交换、控制技术要求》9.6.1状态信息报送章节描述。 当源设备(包括网关、SIP 设备、SIP 客户端或联网系统) 发现工作异常时, 应立即向本 SIP 监控域的SIP服务器发送状态信息; 无异常时, 应定…

国标28181: 视频国标28181协议

国标的由来 GB28181国标解决平台与平台对接问题 比如A平台大连交警系统需要看B平台上海交警系统的视频。需要对接过来,实现调度视频。这时候需要知道他们取流的协议,各家厂商都自定义了一套协议,就很麻烦,因此国家就制定了GB28…

【C/C++】isalpha、islower、isupper、isalnum、isblank、isspace函数cctype / ctype.h头文件

isalpha、islower、isupper、isalnum、isblank、isspace这些函数都在<cctype>&#xff08;即C语言中的<ctype.h>&#xff09;的头文件里面&#xff0c;下图是它们所表示的范围&#xff1a; 总的来说就是&#xff1a; isalpha &#xff08;字母&#xff0c;包括大写…

细微之处见真章之StringUtils的isBlank函数细节解读

一、背景 技术群里有一个老铁分享了一段 commons-lang 的 StringUtils 工具类的代码&#xff1a; public static boolean isBlank(final CharSequence cs) {int strLen;if (cs null || (strLen cs.length()) 0) {return true;}for (int i 0; i < strLen; i) {if (Char…

字符串判空,isBlank 和 isEmpty 到底选那个?

字符串的判空&#xff0c;日常开发是经常要做的一种校验&#xff0c;common-lang包帮我们做了一些字符串判空的封装 <dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId> </dependency> 并对外提…

sbrk() 函数是干什么的?

brk和sbrk主要的工作是实现虚拟内存到内存的映射. 在GNU C中,内存分配是这样的: 每个进程可访问的虚拟内存空间为3G&#xff0c;但在程序编译时&#xff0c;不可能也没必要为程序分配这么大的空间&#xff0c;只分配并不大的数据段空间&#xff0c;程序中动态分配的空间就是从这…

ORACLE如何修改默认端口号

oracle在安装的的时候&#xff0c;除了可以默认端口号&#xff0c;也可以填入自定义的端口号。但是项目上会出现这样的情况&#xff0c;项目在进展时期&#xff0c;需要对数据库安全升级&#xff0c;首先要修改数据库的默认端口号。那如何来设置oracle的默认端口号&#xff0c;…