Java 性能调优:优化 GC 线程设置
垃圾回收器使用一组称为 GC 线程的线程来执行回收工作。有时 JVM 可能会分配过多或过少的 GC 线程。本文将讨论 JVM 为什么会出现这种情况、其影响以及可能的解决方案。
1 咋查找应用程序的 GC 线程数量
进行线程转储分析来确定应用程序的 GC 线程数量:
从生产服务器捕获 thread dump
使用 thread dump 分析工具进行分析
立即显示 GC 线程数量,如图
还可通过 JMX(Java Management Extensions)或 VisualVM、JConsole 等查看 GC 线程数量。
2 咋设置 GC 线程数量?
JVM 参数手动调整 GC 线程数:
-XX:ParallelGCThreads=n:设置垃圾回收器并行阶段使用的线程数量
-XX:ConcGCThreads=n:控制垃圾回收器并发阶段使用的线程数量
注意这些参数适用于并行垃圾回收器(如 ParallelGC 和 ParallelOldGC)和并发垃圾回收器(如 G1GC)。
3 默认 GC 线程数量多少?
根据服务器或容器中的 CPU 数量自动计算。
-XX:ParallelGCThreads 默认值:在 Linux/x86 系统上,默认值公式:
因此,如果 JVM 运行在拥有 32 个处理器的服务器上,那么 ParallelGCThread 的值将是 23。
-XX:ConcGCThreads 默认值:公式:
因此,如果 JVM 运行在 32 个处理器的服务器上:
ParallelGCThread 的值将是 23(即 8 + (32 – 8) * (5/8))
ConcGCThreads 的值将是 6(即 max(25/4, 1))
4 JVM 会分配过多的 GC 线程吗?
JVM 可能在你不知情下分配过多 GC 线程。因为默认 GC 线程数量是根据服务器或容器中的 CPU 数量自动确定。
如在拥有 128 个 CPU 机器,JVM 可能会为垃圾回收的并行阶段分配大约 80 个线程,并为并发阶段分配大约 20 个线程,总计 100 个 GC 线程。
如你在这台 128 CPU 的机器上运行多个 JVM,每个 JVM 可能会分配大约 100 个 GC 线程。这会导致资源的过度使用,因为所有这些线程都在争夺相同的 CPU 资源。这种情况在容器化环境中特别常见,因为多个应用程序共享相同的 CPU 核心,导致 JVM 分配的 GC 线程超过所需数量,从而降低整体性能。
容器化环境
JVM 可能根据容器分配的 CPU 资源来计算 GC 线程数量,而非物理机器的 CPU 数量。这可能导致在共享 CPU 资源的容器环境中分配过多的 GC 线程。
5 过多 GC 线程也是问题?
虽然 GC 线程对高效的内存管理非常重要,但过多 GC 线程可能会导致 Java 应用程序性能问题。
上下文切换增加
当 GC 线程过多时,操作系统需要频繁地在这些线程之间切换,导致上下文切换的开销增加,更多的 CPU 时间花在管理线程上,而不是执行应用程序代码,结果应用程序可能会明显变慢。
CPU 开销增加
每个 GC 线程都会消耗 CPU 资源,过多的线程同时活跃时,它们会争夺 CPU 时间,减少应用程序的主要任务的处理能力,特别是在 CPU 资源有限的情况下。
内存争用
过多的 GC 线程会增加内存资源争用,多个线程同时访问和修改内存会导致锁争用,从而进一步降低应用程序性能。
GC 暂停时间增加,吞吐量下降
过多的 GC 线程会使垃圾回收过程低效,导致更长的 GC 暂停时间,应用程序会被暂时中断,延长的暂停时间可能会造成明显的延迟或卡顿。此外,更多的时间花在垃圾回收上而不是处理请求,应用程序的整体吞吐量会下降,从而影响其在高负载下的扩展性和性能。
延迟增加
由于过多线程导致 GC 活动增加,响应用户请求或处理任务的延迟也会增加,这对需要低延迟的应用程序来说尤其严重,例如实时系统或高频交易平台。
边际效益递减
增加 GC 线程到一定程度后,并不会继续提高性能,反而会出现边际效益递减,管理这些线程的开销超过了更快垃圾回收的好处,这会导致应用性能下降。
6 过少 GC 线程还是问题?
过少的 GC 线程同样会给 Java 应用程序带来问题。原因如下:
垃圾回收时间延长:GC 线程过少时,垃圾回收所需时间变长,线程少,处理时间长,GC 暂停时间也随之延长。
应用程序延迟增加:垃圾回收时间过长会增加应用程序的延迟,特别是对于需要低延迟的应用程序,用户可能会感到应用程序无响应。
吞吐量降低:GC 线程数量不足会导致垃圾回收器工作效率降低,进而影响整体吞吐量,应用程序每秒处理的请求或事务变少,影响其扩展能力。
CPU 利用率低下:线程过少时,CPU 核心可能无法充分利用,部分核心闲置,部分核心负载过重,资源利用率不均衡。
增加 OOM 和内存泄漏风险:GC 线程过少可能导致垃圾回收器无法跟上内存分配的速度,回收不及时,可能出现 OOM,甚至导致内存泄漏和崩溃。
7 优化 GC 线程数的解决方案
若应用程序因 GC 线程数量不当导致性能问题,可通过 JVM 参数手动调整 GC 线程数:
-XX:ParallelGCThreads=n
-XX:ConcGCThreads=n
在生产环境中应用这些更改前,先研究应用程序的 GC 行为,收集并分析 GC 日志。根据分析结果,判断当前线程设置是否导致性能瓶颈,然后进行相应调整。
务必在受控环境中测试这些更改,以确保它们的确能改善性能,然后再应用于生产环境。调整 GC 线程数量时,应结合应用程序的实际工作负载、内存使用情况和硬件配置进行综合考虑。此外,可以使用工具如 GCViewer 来分析 GC 日志,以更好地理解 GC 行为并进行优化。
8 总结
平衡 GC 线程数量对 Java 应用程序的平稳运行至关重要。通过仔细监控和调整这些设置,可以避免潜在的性能问题,并保持应用程序的高效运行。
关注我,紧跟本系列专栏文章,咱们下篇再续!
作者简介:魔都架构师,多家大厂后端一线研发经验,在分布式系统设计、数据平台架构和 AI 应用开发等领域都有丰富实践经验。各大技术社区头部专家博主。具有丰富的引领团队经验,深厚业务架构和解决方案的积累。负责:
中央/分销预订系统性能优化
活动 &券等营销中台建设
交易平台及数据中台等架构和开发设计
车联网核心平台-物联网连接平台、大数据平台架构设计及优化
LLM Agent 应用开发
区块链应用开发
大数据开发挖掘经验
推荐系统项目目前主攻市级软件项目设计、构建服务全社会的应用系统。
作为程序员,持续学习和充电非常重要,作为开发者,我们需要保持好奇心和学习热情,不断探索新的技术,只有这样,我们才能在这个快速发展的时代中立于不败之地。低代码也是一个值得我们深入探索的领域,让我们拭目以待,它将给前端世界带来怎样的变革,推荐一个低代码工具。
开发语言:Java/.net
这是一个基于 Flowable 引擎(支持 java、.NET),已支持 MySQL、SqlServer、Oracle、PostgreSQL、DM(达梦)、 KingbaseES(人大金仓)6 个数据库,支持私有化部署,前后端封装了上千个常用类,方便扩展,框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用。
至少包含表单建模、流程设计、报表可视化、代码生成器、系统管理、前端 UI 等组件,这种情况下我们避免了重复造轮子,已内置大量的成熟组件,选择合适的组件进行集成或二次开发复杂功能,即可自主开发一个属于自己的应用系统。
评论