JVM_垃圾收集器详解
1、 前言
JVM就是Java虚拟机,说白了就是为了屏蔽底层操作系统的不一致而设计出来的一个虚拟机,让用户更加专注上层,而不用在乎下层的一个产品。这就是JVM的跨平台,一次编译,到处运行。
而JVM中的核心功能其实就是自动化的垃圾回收机制。

在Java中你使用对象,使用内存,不用担心回收,只管new对象就行了,不用管垃圾的回收。
因为Java当中是自动化的垃圾回收机制。JVM有专门的垃圾回收器,把垃圾回收这件事给干了。
2、分代收集理论
当前虚拟机的垃圾收集都采用分代收集算法,根据对象存活周期的不同将内存分为几块。一般将java堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。
新生代:每次收集都会有大量对象(近99%)死去,所以可以选择复制算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集。就比如新生代中的 S0 和 S1 区域。
老年代:对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。
PS:标记-清除”或“标记-整理”算法会比复制算法慢10倍以上。
2.1、垃圾收集算法
标记-复制算法

标记-复制算法是把内存分为大小相等的两块,每次使用其中的一块,当这块内存使用完以后,就会把还存活的对象放到另外一块内存中去,清理刚才使用到的那块空间。分为标记和复制两个阶段:
第一阶段:标记;就是根据Gc Roots标记存活的对象。
第二阶段:复制;把存活的对象复制到另外一块内存中。
缺点:会浪费多余的存储空间。
2.2、标记-清除算法

标记-清除算法分为标记和清除两个阶段:
第一阶段:标记;就是根据Gc Roots标记存活的对象。
第二阶段:清除;统一回收未被标记的对象。
缺点:
效率问题 (如果需要标记的对象太多,效率不高) ;
空间问题(标记清除后会产生大量不连续的碎片)。
2.3、标记-整理算法

标记-整理算法是根据老年代的特点设计出的一种标记算法。他的标记过程和标记清除算法一样的,后续不同的是,它会先把所有存活的对象规整的移动到一起,然后把边界外的对象统一回收。
3、垃圾回收中的STW
对于Java项目来说,JVM进行垃圾收集会有一个很大的问题,就是STW(Stop The World)。
就是在垃圾回收的时候会把应用线程(项目中运行的线程)给停止,然后进行垃圾回收,如果STW的时间过程,用户就会感觉到明显的卡顿现象。
3.1、STW带来的问题
1)安卓手机系统的卡顿
Google 主导的 Android 系统需要解决的一大问题就是显示卡顿问题,通过对 GC 算法的不断演进,停顿时间控制在几个ms 级别。所以这也是Android与苹果IOS系统竞争的一大利器。
2)证券交易系统的实时性
证券交易系统的实时性要求很高,一般都是需要低卖高卖,差之毫厘谬以千里,如果STW的时间很长,到达几秒钟的级别,这几秒钟后的市场完全不同了。所以用Java来做证券系统,STW的时间越短越好。
3.2、垃圾收集器的发展
为了满足不同的业务场景,Java的GC算法和垃圾收集器都在不同的迭代,对于这些延迟敏感的应用来说,GC停顿已经成为了阻碍Java发展的一大顽疾。
近些年来,服务器的性能越来越好,各种应用可以使用的堆内存也越来越大,其实垃圾收集器的发展都是为了解决STW的停顿时间而一直迭代。为了让STW的停顿时间也跟随着堆大小的增长而不呈现指数级的增长而努力,推出了一系列垃圾收集器。
4、垃圾收集器

如果说垃圾回收算法是内存收回的理论,那么垃圾收集器就是内存回收的具体实现。
虽然我们对各个收集器进行比较,但并非为了挑选出一个最好的收集器。因为直到现在为止还没有最好的垃圾收集器出现,更加没有万能的垃圾收集器, 我们能做的就是根据具体应用场景选择适合自己的垃圾收集器 。
4.1、单线程垃圾收集器
1)Serial

Serial(串行)垃圾收集器,一看名字就知道它是一个单线程收集器。它的单线程的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾手机工作,更重要的是它在进行垃圾手机工作的时候还必须暂停其他的所有工作线程(STW),直到它收集结束。
新生代采用标记-复制算法,老年代采用标记-整理算法。
Serial 虽然是单线程,STW的时间有点长,但是它简单且高效(与其它垃圾收集器相比),它没有线程交互的开销,自然而然的可以获得很高的单线程收集效率。
参数设置:-XX:+UseSerialGC(年轻代) -XX:+UseSerialOldGC(老年代)
2)Serial Old
Serial Old收集器是Serial收集器的老年代版本,它同样是单线程收集器。
两大用途:
一种用途是在JDK1.5 以及以前的版本中与Parallel Scavenge收集器搭配使用。
一种用途是作为CMS收集器的后备方案。
4.2、多线程垃圾收集器
1)Parallel Scavenge

Parallel收集器是Serial收集器的多线程版本,除了使用多线程垃圾收集以外,其余和Serial收集器类似。默认的收集线程数跟cpu核数相同,当然也可以用参数(-XX:ParallelGCThreads)指定收集线程数,但是一般不推荐修改。
Parallel Scavenge收集器关注点是吞吐量(高效率的利用CPU)。所谓吞吐量就是CPU中用于运行用户代码的时间与CPU总消耗时间的比值。
新生代采用标记-复制算法,老年代采用标记-整理算法。参数设置:-XX:+UseParallelGC(年轻代),-XX:+UseParallelOldGC(老年代)
2)Parallel Old
Parallel Old收集器是Parallel Scavenge收集器的老年代版本。
使用多线程和“标记-整理”算法。在注重吞吐量以及 CPU资源的场合,都可以优先考虑 Parallel Scavenge收集器和Parallel Old收集器(JDK8默认的新生代和老年代收集器)。
3)ParNew
ParNew收集器其实跟Parallel收集器很类似,区别主要在于它可以和CMS收集器配合使用。
新生代采用标记-复制算法,老年代采用标记-整理算法。
参数设置:-XX:+UseParNewGC
4.3、多线程+并发收集器
1)CMS
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体 验的应用上使用,它是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。
从它的名字中可以看出它是一种 “标记-清除” 算法的实现

GC步骤:
初始标记(STW):暂停所有的其它线程,期间会STW,并记录GC Roots直接能引用的对象,速度很快。
并发标记:并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程, 这个过程耗时较长但 是不需要停顿用户线程, 可以与垃圾收集线程一起并发运行。因为用户程序继续运行,可能会有导致已经标记过的对象状态发生改变。
重新标记(STW):重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。主要用到三色标记里的增量更新算法做重新标记。
并发清理:开启用户线程,同时GC线程开始对未标记的区域做清扫。这个阶段如果有新增对象会被标记为黑色不做任何处理。
并发重置:重置本次GC过程中的标记数据。
优点:并发收集、低停顿。
缺点:
对CPU资源敏感(会和服务抢资源);
无法处理浮动垃圾(在并发标记和并发清理阶段又产生垃圾,这种浮动垃圾只能等到下一次gc再清理了);
它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生,当然通过参数(- XX:+UseCMSCompactAtFullCollection)可以让jvm在执行完标记清除后再做整理 。
执行过程中的不确定性,会存在上一次垃圾回收还没执行完,然后垃圾回收又被触发的情况,特别是在并 发标记和并发清理阶段会出现,一边回收,系统一边运行,也许没回收完就再次触发full gc,也就是"concurrent mode failure",此时会进入STW,用serial old垃圾收集器来回收。
核心参数设置:
参数 含义
-XX:+UseConcMarkSweepGC 启用CMS
-XX:ConcGCThreads 并发的GC线程数
-XX:+UseCMSCompactAtFullCollection FullGC之后做压缩整理(减少碎片)
-XX:CMSFullGCsBeforeCompaction 多少次FullGC之后压缩一次,默认是0,代表每次FullGC后都会压缩一次
-XX:CMSInitiatingOccupancyFraction 当老年代使用达到该比例时会触发FullGC(默认是92,这是百分比)
-XX:+UseCMSInitiatingOccupancyOnly
只使用设定的回收阈值
(-XX:CMSInitiatingOccupancyFraction设定的值),如果不指定,JVM仅在第一次使用设定值,后续则会自动调整
-XX:+CMSScavengeBeforeRemark 在CMS GC前启动一次minor gc,目的在于减少老年代对年轻代的引 用,降低CMS GC的标记阶段时的开销,一般CMS的GC耗时 80%都在标记阶段
-XX:+CMSParallellnitialMarkEnabled 表示在初始标记的时候多线程执行,缩短STW
-XX:+CMSParallelRemarkEnabled 在重新标记的时候多线程执行,缩短STW
4.4、G1收集器
G1垃圾收集器的工作原理和特性:
G1垃圾收集器是一种面向服务器的垃圾收集器,它将堆划分为多个大小相等的独立区域(Region),这些Region可以是年轻代或老年代。G1保留了年轻代和老年代的概念,但它们不再是物理隔阂,而是Region的集合。G1通过监视应用程序的行为和垃圾收集暂停信息来建立相关成本模型,以实现可预测的暂停时间目标。它采用分步递增和并行的方式来执行空间回收,以缩短停顿时间。
大内存应用:G1垃圾收集器适用于堆大小约为6GB或更大的应用程序。当堆大小超过50%的Java堆内存被实时数据占用时,G1能够提供更好的性能。
多处理器机器:G1垃圾收集器特别适合配备多颗处理器的服务器,因为它能够利用多核处理器的优势,实现高效的垃圾收集。
需要稳定且可预测的暂停时间:G1垃圾收集器旨在实现稳定的暂停时间目标,通常不超过0.5秒,这对于需要低延迟的应用程序非常重要。
对象分配和晋升速率变化显著:在堆中,对象分配和晋升的速率可能会随时间显著变化,G1能够适应这种变化,提供更好的性能。
存在大量碎片:当堆中存在大量碎片时,G1能够通过分步递增和并行的方式来执行空间回收,减少碎片化对性能的影响。
使用G1垃圾收集器的命令行参数:
-XX:+UseG1GC:显式启用G1垃圾收集器。
-XX:G1HeapRegionSize:手动指定Region的大小。
-XX:G1NewSizePercent:设置新生代初始占比。
-XX:G1MaxNewSizePercent:调整新生代的最大占比。
这些参数可以帮助用户根据具体需求调整G1垃圾收集器的行为,以优化应用程序的性能和暂停时间。
4.5、ZGC
jdk11 低延迟,浮动垃圾多,适合几百G 以上
5 总结
本章主要讲了JVM中垃圾收集器的发展,从单线程到多线程,再到GC线程和用户线程并发收集的垃圾收集器。
如何选择垃圾收集器:
优先调整堆的大小让服务器自己来选择
如果内存小于100M,使用串行收集器
如果是单核,并且没有停顿时间的要求,串行或JVM自己选择
如果允许停顿时间超过1秒,选择并行或者JVM自己选
如果响应时间最重要,并且不能超过1秒,使用并发收集器
4G以下可以用parallel,4-8G可以用ParNew+CMS,8G以上可以用G1,几百G以上用ZGC。
相关文章:
JVM_垃圾收集器详解
1、 前言 JVM就是Java虚拟机,说白了就是为了屏蔽底层操作系统的不一致而设计出来的一个虚拟机,让用户更加专注上层,而不用在乎下层的一个产品。这就是JVM的跨平台,一次编译,到处运行。 而JVM中的核心功能其实就是自动…...
Javascript Insights: Visualizing Var, Let, And Const In 2024
11/2024 出版 MP4 |视频:h264, 19201080 |音频:AAC,44.1 KHz 语言:英语 |大小: 2.96 GB |时长: 5 小时 34 分钟 为所有认真的 JavaScript 开发人员可视化与 VAR、LET、CONST 和 EXECUTON CONTE…...
KL散度改写为一个可用于优化的形式
理解 KL 散度及其公式推导过程 在信息论和概率论中,KL散度(Kullback-Leibler Divergence)是衡量两个概率分布之间差异的重要工具。本文将从 KL 散度的定义入手,详细解析其公式来源以及如何将其改写为一个可用于优化的形式。 1. 什…...
Java代码操作Zookeeper(使用 Apache Curator 库)
1. Zookeeper原生客户端库存在的缺点 复杂性高:原生客户端库提供了底层的 API,需要开发者手动处理很多细节,如连接管理、会话管理、异常处理等。这增加了开发的复杂性,容易出错。连接管理繁琐:使用原生客户端库时&…...
【Linux】Make/Makefile
这个3/4行的语法和1/2行是一样的。也是依赖关系和依赖方法。 make命令扫描makefile文件时,从上向下扫描,默认形成一个目标文件。 指定make clean的时候才回去执行对应的清除。 为什么要给我们的clean.PHONY:clean声明它是伪目标呢? PHONY类…...
C++练级计划->《多态》虚函数表,菱形继承多态
目录 什么是多态? 多态的条件 虚函数: 虚函数的重写: 协变 析构函数的重写 C11 final 和 override final: override: 总结: 三重对比:重载重写重定义对比 抽象类 多态的原理 虚函数…...
OkHttp3 - 2. OkHttp的核心组件与架构
1 OkHttp的工作原理 OkHttp3 的核心设计遵循以下原则: 请求与响应的分离:通过 Request 和 Response 对象解耦请求构建与结果处理。异步与同步支持:使用 Call 对象管理请求,可以同步或异步执行。高效连接复用:通过连接…...
异或操作解决一些问题
前提: 异或操作符合交换律,结合律(因为其根本上来抽象理解,就是查看所有项二进制数相同位是否有奇数个1,对运算结果二进制数而言,没有该位为0,有该位为1,与顺序无关)。 …...
操作系统之输入输出
🧑 博主简介:CSDN博客专家,历代文学网(PC端可以访问:https://literature.sinhy.com/#/literature?__c1000,移动端可微信小程序搜索“历代文学”)总架构师,15年工作经验,…...
Centos 安装 Node.js 和 npm
方法2:使用 NVM(Node Version Manager)安装 安装 NVM curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash 重新加载配置 source ~/.bashrc 安装最新的 LTS 版本的 Node.js nvm install --lts 验证安装…...
C语言——指针初阶(一)
目录 一.什么是指针??? 指针是什么? 指针变量: 总结: 总结: 二.指针和指针类型 指针-整数: 总结: 指针的解引用 总结: 三.野指针 如何规避野指针 往期…...
React Native 原生开发指南
写在前面 React Native (RN) 是一个用于构建跨平台移动应用的框架。它允许开发者使用 JavaScript 和 React 来编写应用程序,并将其转换为原生代码。虽然 RN 提供了许多内置的组件和 API,但有时候你可能需要访问原生平台的特定功能或性能优化。为此&…...
【前端】JavaScript中的柯里化(Currying)详解及实现
博客主页: [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: 前端 文章目录 💯前言💯什么是柯里化?💯柯里化的特点💯柯里化的简单示例💯通用的柯里化实现💯柯里化让代码更易读的原因💯…...
解决 docker 部署 vsftpd 速度慢问题
解决 docker 部署 vsftpd 速度慢问题 Docker 部署 ftp version: 3.8services:ftps:image: fauria/vsftpdcontainer_name: my-ftpsenvironment:- FTP_USERyourusername- FTP_PASSyourpassword- PASV_ADDRESS192.168.0.123 # 使用环境变量或直接指定IP地址- PASV_MIN_PORT4900…...
Java基础夯实——2.9 多线程如何共享数据
在 Java 多线程编程中,共享数据通过以下几种方式实现: 1. 使用共享对象 多个线程可以通过引用同一个对象来实现数据共享。例如: class SharedData {private int count;public synchronized void increment() {count;}public synchronized …...
【Leetcode Top 100】234. 回文链表
问题背景 给你一个单链表的头节点 h e a d head head,请你判断该链表是否为 回文链表(回文 序列是向前和向后读都相同的序列)。如果是,返回 t r u e true true;否则,返回 f a l s e false false。 数据…...
GitLab指定用户分配合并权限
进入项目 -》 Project Settings Repository -》展开 Protected branches -》 添加要保护的分支,设置角色 管理用户角色权限 查看到不同用户的角色,一般设置Developer只有Merger Request权限,Maintainer还有Merge审批权限 GitLab 中的权限…...
五,[GXYCTF2019]Ping Ping Ping1
进入靶场,有提示 我们在url试着输入本地IP,返回了ping命令 既然要在url处传参,那就用postman,再输入ip127.0.0.1 & ls,试着列出目录内容 ok,好像是个脏话,它过滤了空格 试着穿越又看到了脏话࿰…...
基于STM32的智能无人机自主飞行与目标识别系统设计
目录 引言系统需求分析 2.1 功能需求 2.2 硬件需求 2.3 软件需求系统设计 3.1 总体架构 3.2 各模块设计系统实现 4.1 硬件实现 4.2 软件实现系统调试与优化总结与展望 1. 引言 随着无人机技术的快速发展,无人机在军事侦察、环境监测、物流配送等领域的应用逐渐增多…...
C 语言数组与函数:核心要点深度剖析与高效编程秘籍
我的个人主页 我的专栏:C语言,希望能帮助到大家!!!点赞❤ 收藏❤ 目录 引言数组基础 2.1 数组的定义与初始化 2.2 一维数组的基本操作 2.3 二维数组及其应用 2.4 数组与指针的关系函数基础 3.1 函数的定义与调用 3.2…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...
Spring Boot面试题精选汇总
🤟致敬读者 🟩感谢阅读🟦笑口常开🟪生日快乐⬛早点睡觉 📘博主相关 🟧博主信息🟨博客首页🟫专栏推荐🟥活动信息 文章目录 Spring Boot面试题精选汇总⚙️ **一、核心概…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
AirSim/Cosys-AirSim 游戏开发(四)外部固定位置监控相机
这个博客介绍了如何通过 settings.json 文件添加一个无人机外的 固定位置监控相机,因为在使用过程中发现 Airsim 对外部监控相机的描述模糊,而 Cosys-Airsim 在官方文档中没有提供外部监控相机设置,最后在源码示例中找到了,所以感…...
解读《网络安全法》最新修订,把握网络安全新趋势
《网络安全法》自2017年施行以来,在维护网络空间安全方面发挥了重要作用。但随着网络环境的日益复杂,网络攻击、数据泄露等事件频发,现行法律已难以完全适应新的风险挑战。 2025年3月28日,国家网信办会同相关部门起草了《网络安全…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
springboot 日志类切面,接口成功记录日志,失败不记录
springboot 日志类切面,接口成功记录日志,失败不记录 自定义一个注解方法 import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target;/***…...
