学习比较JVM篇(六):解读GC日志
一、前言
在之前的文章中,我们对JVM的结构、垃圾回收算法、垃圾回收器做了一些列的讲解,同时也使用了JVM自带的命令行工具进行了实际操作。今天我们继续讲解JVM。
我们学习JVM的目的是为了了解JVM,然后优化对应的参数。那么如何了解JVM运行的状态呢,日志就是一个很好的入口。和我们写的程序一样,JVM本身也是一个程序,只不过这个程序的作用是为了运行我们写的Java代码,既然是程序,那么运行的过程中肯定是会产生日志,今天我们要讲解的就是GC日志。
Java、Python等这些高级语言,之所以相对上手简单有一部分的原因就是因为其自带的垃圾回收机制,让程序员不用再手动的管理内存,对于我们开发人员来说就好比是一个黑盒,但是很多时候我们又需要知道垃圾回收是怎么进行的,此时我们就需要借助GC日志,话不多说让我们开始吧。
二、GC日志
所谓GC日志,值得就是垃圾回收器在运行过程中产生的日志,如果想要查看运行中GC产生的日志,我们就需要在启动参数上配置对应的属性。这里我们从一个简单的Demo入手
public class HeapOOMDemo {// 模拟大对象的大小,这里设置为 1MBprivate static final int _1MB = 1024 * 1024;public static void main(String[] args) {// 用于存储对象的列表List<byte[]> list = new ArrayList<>();// 循环创建对象for (int i = 0; ; i++) {// 每次创建一个 1MB 大小的对象byte[] bigObject = new byte[_1MB];list.add(bigObject);try {// 模拟业务处理延迟Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}}}
}
这段代码非常简单,就是一个死循环一直不断的往List中添加对象,同时我们设置启动参数
-Xms20m -Xmx20m -Xmn1m -XX:+PrintGCDetails -XX:+PrintGCDateStamps
| 参数 | 含义 | 作用 | 示例值说明 |
|---|---|---|---|
-Xms20m | 设定 JVM 启动时的初始堆内存大小 | 避免程序运行初期频繁进行内存分配和调整,提升性能 | 20m表示初始堆内存为 20MB |
-Xmx20m | 设定 JVM 所能使用的最大堆内存大小 | 限制 JVM 堆内存使用上限,防止系统内存耗尽;若与 -Xms设为相同值,可减少运行时堆内存大小调整带来的性能开销 | 20m表示最大堆内存为 20MB |
-Xmn1m | 设定新生代的大小 | 新生代是新创建对象存储区域,合理设置其大小可提高垃圾回收效率 | 1m表示新生代大小为 1MB |
-XX:+PrintGCDetails | 让 JVM 在进行垃圾回收时打印详细的 GC 信息 | 用于分析程序的内存使用状况和垃圾回收性能,包含堆内存各区域 GC 前后使用情况、GC 类型、触发原因及耗时等 | 无 |
-XX:+PrintGCDateStamps | 在 GC 日志中添加时间戳,精确到日期和时间 | 便于确定 GC 发生的具体时间,结合系统负载和业务操作分析 GC 与系统行为的关联 | 无 |
接着我们运行这个Demo,很显然会抛出OOM,我们的重点是上面的GC日志

三、日志解读
完整的GC日志如下
2025-04-07T08:49:00.478-0800: [GC (Allocation Failure) [PSYoungGen: 512K->495K(1024K)] 512K->531K(19968K), 0.0020026 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
2025-04-07T08:49:00.493-0800: [GC (Allocation Failure) [PSYoungGen: 1007K->495K(1024K)] 1043K->587K(19968K), 0.0005194 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2025-04-07T08:49:00.653-0800: [GC (Allocation Failure) [PSYoungGen: 1007K->495K(1024K)] 9291K->9027K(19968K), 0.0026903 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2025-04-07T08:49:00.693-0800: [GC (Allocation Failure) [PSYoungGen: 1007K->495K(1024K)] 12611K->12339K(19968K), 0.0014364 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2025-04-07T08:49:00.735-0800: [GC (Allocation Failure) [PSYoungGen: 1007K->495K(1024K)] 16947K->16675K(19968K), 0.0020088 secs] [Times: user=0.01 sys=0.01, real=0.00 secs]
2025-04-07T08:49:00.773-0800: [GC (Allocation Failure) [PSYoungGen: 845K->511K(1024K)] 19073K->18864K(19968K), 0.0030740 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2025-04-07T08:49:00.776-0800: [GC (Allocation Failure) [PSYoungGen: 511K->224K(1024K)] 18864K->18888K(19968K), 0.0005910 secs] [Times: user=0.01 sys=0.00, real=0.00 secs]
2025-04-07T08:49:00.777-0800: [Full GC (Allocation Failure) [PSYoungGen: 224K->0K(1024K)] [ParOldGen: 18664K->18642K(18944K)] 18888K->18642K(19968K), [Metaspace: 5256K->5256K(1056768K)], 0.0090459 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
2025-04-07T08:49:00.786-0800: [GC (Allocation Failure) [PSYoungGen: 0K->0K(1024K)] 18642K->18642K(19968K), 0.0003195 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2025-04-07T08:49:00.787-0800: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1024K)] [ParOldGen: 18642K->18548K(18944K)] 18642K->18548K(19968K), [Metaspace: 5256K->5256K(1056768K)], 0.0074082 secs] [Times: user=0.03 sys=0.01, real=0.01 secs]
接下来我们详细的解读一下这个日志
2025-04-07T08:49:00.478-0800:[GC (Allocation Failure) [PSYoungGen: 512K->495K(1024K)] 512K->531K(19968K), 0.0020026 secs] [Times: user=0.01 sys=0.00, real=0.01 secs]
(1)首先第一次GC发生在 2025-04-07 08:49:00.478
(2)GC (Allocation Failure:GC的原因是因为分配失败,这里需要分析一下,我们设置的堆内存为20M,新生代和老年代默认比例是1:2,但是我们这里设置了新生代为1M。一开始我们就创建了一个List对象会占用一部分内存,然后又尝试去新建一个1M的对象,此时新生代肯定是装不下了(因为总共就1M),所以触发了MinorGC
(3)第三部分:PSYongGen表示年轻代采用的是Parallel Scavenge垃圾收集器,第一次MinorGC后,新生代的内存从使用了512K变成了使用了495K(总内存是1024K),由此我们可以推断出我们的对象其实并没有创建在新生代(压根不够)而是直接进入了老年代。
补充说明
tips:这里要补充说明一点,很多时候我们会认为对象初始都是在新生代的,这个不一定某些大对象会直接创建在堆内存(可配),同时JVM也有动态年龄机制,在进行Minor GC 时,JVM 会对 Survivor区(新生代中用于存放存活对象的区域)中所有对象按照年龄从小到大进行排序,然后计算不同年龄对象的大小总和。当某个年龄及小于该年龄的所有对象大小总和超过了 Survivor 区一半的空间时,大于等于该年龄的对象就会直接晋升到老年代,而不需要等到达到 MaxTenuringThreshold 所设定的年龄。
(4)忽略其他的MinorGC,直接进入FullGC
2025-04-07T08:49:00.777-0800: [Full GC (Allocation Failure) [PSYoungGen: 224K->0K(1024K)] [ParOldGen: 18664K->18642K(18944K)] 18888K->18642K(19968K), [Metaspace: 5256K-5256K(1056768K)], 0.0090459 secs] [Times: user=0.03 sys=0.00, real=0.01 secs]
解读:很明显从日志里可以看出,这次触发的是Full GC,原因也很明显分配失败。FullGC是整堆的回收,所以也会对年轻代进行回收。
年轻代:年轻代被整体回收,从使用了224K变成了使用0K;
老年代:而老年代从使用了18664K变成使用了18642K(很明显效果并不好,因为在当前Demo里我们的创建的对象并不是垃圾对象)。
Metaspace(元空间):元空间并没有什么影响没变
(5)最后一次FullGC:2025-04-07T08:49:00.787-0800: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(1024K)] [ParOldGen: 18642K->18548K(18944K)] 18642K->18548K(19968K), [Metaspace: 5256K->5256K(1056768K)], 0.0074082 secs] [Times: user=0.03 sys=0.01, real=0.01 secs]
从这段日志里可以看出老年代实在是回收不动了,因为源源不断的对象被新建出来了存放到了老年代,一种产生了OOM。
HeapPSYoungGen total 1536K, used 540K [0x00000007bfe00000, 0x00000007c0000000, 0x00000007c0000000)eden space 1024K, 5% used [0x00000007bfe00000,0x00000007bfe0ee58,0x00000007bff00000)from space 512K, 93% used [0x00000007bff80000,0x00000007bfff81f0,0x00000007c0000000)to space 512K, 0% used [0x00000007bff00000,0x00000007bff00000,0x00000007bff80000)ParOldGen total 18432K, used 18089K [0x00000007bec00000, 0x00000007bfe00000, 0x00000007bfe00000)object space 18432K, 98% used [0x00000007bec00000,0x00000007bfdaa490,0x00000007bfe00000)Metaspace used 5231K, capacity 5246K, committed 5504K, reserved 1056768Kclass space used 609K, capacity 618K, committed 640K, reserved 1048576K
最后附上完整的使用情况。
四、结束语
今天我们学习了如何解读GC日志,这是非常重要的一项技能,我们的JVM优化很多时候也是要参考GC日志的。希望今天的文章对你有所帮助
五、未完待续
相关文章:
学习比较JVM篇(六):解读GC日志
一、前言 在之前的文章中,我们对JVM的结构、垃圾回收算法、垃圾回收器做了一些列的讲解,同时也使用了JVM自带的命令行工具进行了实际操作。今天我们继续讲解JVM。 我们学习JVM的目的是为了了解JVM,然后优化对应的参数。那么如何了解JVM运行…...
I²S协议概述与信号线说明
IIS协议概述 IS(Inter-IC Sound)协议,又称 IIS(Inter-IC Sound),是一种专门用于数字音频数据传输的串行总线标准,由飞利浦(Philips)公司提出。该协议通常用于微控制器…...
b4a安卓开发技术和建议,VB6开发Android APK
b4a功能建议实现方法想法创意Wait For可以在参数中直接返回结果吗?Wait For (cam.OpenCamera(front)) Complete (TaskIndex As Int) Wait For B4XPage_PermissionResult (Permission As String, Result As Boolean) 函数别名,减少代码,通用函…...
计算机网络-子网划分试题七
计算机网络中IP地址为172.16.20.60、172.16.30.60、172.16.80.60,子网掩码为255.255.192.0的三台计算机的网络号,子网号及主机号,并确定三台计算机是否处于同一个子网,如果不是请指出哪些在同一个子网,哪些不是&#x…...
免费Deepseek-v3接口实现Browser-Use Web UI:浏览器自动化本地模拟抓取数据实录
源码 https://github.com/browser-use/web-ui 我们按照官方教程,修订几个环节,更快地部署 步骤 1:克隆存储库 git clone https://github.com/browser-use/web-ui.git cd web-ui Step 2: Set Up Python Environment 第 2 步:设置…...
[蓝桥杯] 求和
题目链接 P8772 [蓝桥杯 2022 省 A] 求和 - 洛谷 题目理解 这道题就是公式题,我们模拟出公式后,输出最终结果即可。 本题不难,相信很多同学第一次见到这道题都是直接暴力解题。 两个for循环,测试样例,直接拿下。 #in…...
大数据学习(100)-kafka详解
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言📝支持一…...
通过Ollama本地部署DeepSeek R1模型(Windows版)
嗨,大家好,我是心海 以下是一份详细的Windows系统下通过Ollama本地部署DeepSeek R1模型的教程,内容简洁易懂,适合新手用户参考 本地部署大模型,就有点像在你自己的电脑或者服务器上,安装并运行这样一个“私…...
【C++】vector的底层封装和实现
目录 目录前言基本框架迭代器容量第一个测试,野指针异常第二轮测试,浅拷贝的问题 元素访问修改操作push_backinsert迭代器失效问题 erase 默认成员函数构造函数双重构造引发调用歧义 拷贝构造赋值重载析构函数 源码end 目录 前言 废话不多说࿰…...
Open CASCADE学习|读取点集拟合样条曲线(续)
问题 上一篇文章已经实现了样条曲线拟合,但是仍存在问题,Tolerance过大拟合成直线了,Tolerance过大头尾波浪形。 正确改进方案 1️⃣ 核心参数优化 通过调整以下参数控制曲线平滑度: Standard_Integer DegMin 3; // 最低阶…...
ARM Cortex-M用于控制中断和异常处理的寄存器:BASEPRI、PRIMASK 和 FAULTMASK
在ARM Cortex-M处理器中,BASEPRI、PRIMASK 和 FAULTMASK 是用于控制中断和异常处理的系统级寄存器。它们的主要区别在于作用范围和灵活性,以下是详细说明: 1. PRIMASK • 功能: 禁用除以下情况的异常和所有中断(Maska…...
Kafka 中的生产者分区策略
Kafka 中的 生产者分区策略 是决定消息如何分配到不同分区的机制。这个策略对 Kafka 的性能、负载均衡、消息顺序性等有重要影响。了解它对于高效地使用 Kafka 进行消息生产和消费至关重要。 让我们一起来看 Kafka 中 生产者的分区策略,它如何工作,以及…...
【Django】教程-11-ajax弹窗实现增删改查
【Django】教程-1-安装创建项目目录结构介绍 【Django】教程-2-前端-目录结构介绍 【Django】教程-3-数据库相关介绍 【Django】教程-4-一个增删改查的Demo 【Django】教程-5-ModelForm增删改查规则校验【正则钩子函数】 【Django】教程-6-搜索框-条件查询前后端 【Django】教程…...
结构化需求分析:专业方法论与实践
结构化需求分析是一种用于软件开发或其他项目中的系统分析方法,旨在全面、准确地理解和描述用户对系统的需求。以下是关于结构化需求分析的详细介绍: 一、概念 结构化需求分析是采用自顶向下、逐步分解的方式,将复杂的系统需求分解为若干个…...
R语言:气象水文领域的数据分析与绘图利器
R 语言是一门由统计学家开发的用于统计计算和作图的语言(a Statistic Language developed for Statistic by Statistician),由 S 语言发展而来,以统计分析功能见长。R 软件是一款集成 了数据操作、统计和可视化功能的优秀的开源软…...
Kotlin与HttpClient编写视频爬虫
想用Apache HttpClient库和Kotlin语言写一个视频爬虫。首先,我需要确定用户的具体需求。视频爬虫通常涉及发送HTTP请求,解析网页内容,提取视频链接,然后下载视频。可能需要处理不同的网站结构,甚至可能需要处理动态加载…...
图形化编程语言:低代码赛道的技术革命与范式突破
在 2024 年 Gartner 低代码平台魔力象限报告中,传统低代码厂商市场份额增速放缓至 12%,而图形化编程语言赛道融资额同比激增 370%。本文深度剖析低代码平台的技术瓶颈,系统阐释图形化编程语言的核心优势,揭示其如何重构软件开发范…...
蓝桥杯每日刷题c++
目录 P9240 [蓝桥杯 2023 省 B] 冶炼金属 - 洛谷 (luogu.com.cn) P8748 [蓝桥杯 2021 省 B] 时间显示 - 洛谷 (luogu.com.cn) P10900 [蓝桥杯 2024 省 C] 数字诗意 - 洛谷 (luogu.com.cn) P10424 [蓝桥杯 2024 省 B] 好数 - 洛谷 (luogu.com.cn) P8754 [蓝桥杯 2021 省 AB2…...
快速上手示例(以BEVFormer为例)
快速上手示例(以BEVFormer为例) 安装依赖: bash git clone https://github.com/fundamentalvision/BEVFormer.git cd BEVFormer pip install -r requirements.txt下载预训练模型: wget https://github.com/fundament…...
GitHub 上开源一个小项目的完整指南
GitHub 上开源一个小项目的完整指南 🚀 第一步:准备你的项目 在开源之前,确保项目是可用且有一定结构的: ✅ 最低要求 项目文件清晰、结构合理(比如:src/、README.md、LICENSE)项目能在本地正…...
当实体类中的属性名和表中的字段名不一样 ,怎么办
在不同的持久化框架中,当实体类中的属性名和表中的字段名不一致时,有不同的解决办法,下面为你详细介绍: 1. MyBatis MyBatis 是一个流行的持久层框架,有两种主要方式来处理属性名和字段名不一致的情况。 方式一&…...
arthas之dump/classloader命令的使用
文章目录 1. dump2. classloader 1. dump 作用:将已加载类的字节码文件保存到特定目录:logs/arthas/classdump/ 参数 数名称参数说明class-pattern类名表达式匹配[c:]类所属 ClassLoader 的 hashcode[E]开启正则表达式匹配,默认为通配符匹…...
linux 使用 usermod 授权 普通用户 属组权限
之前写过这篇文章 linux 普通用户 使用 docker 只不过是使用 root 用户编辑 /etc/group用户所属组文件的方式 今天带来一种 usermod 命令行方式 以下3步,在root用户下操作 第一步,先创建一个普通用户测试使用 useradd miniuser第二步,授权到…...
大文件上传之断点续传实现方案与原理详解
一、实现原理 文件分块:将大文件切割为固定大小的块(如5MB) 进度记录:持久化存储已上传分块信息 续传能力:上传中断后根据记录继续上传未完成块 块校验机制:通过哈希值验证块完整性 合并策略:所…...
第一次3D打印,一个简单的小方块(Rhino)
一、建模 打开犀牛,我们选择立方体 我们点击上册的中心点 输入0,然后回车0 而后我们输长度:10,回车确认 同样的,宽度10 高度同样是10 回车确认后,我们得到一个正方形 二、导出模型 我们选择文件—>保存…...
java基础使用- 泛型
泛型 泛型作用泛型语法(1) 泛型类/接口(2) 泛型方法 类型参数命名习惯类型通配符(Wildcards)(1) 无界通配符 <?>表示“未知类型”(2) 上界通配符 <? extends T>表示“T 或 T 的子类”。(3) 下界通配符 <? super T>表示“T 或 T 的父…...
VMware-workstation-full-12.5.2 install OS X 10.11.1(15B42).cdr
手把手虚拟机安装苹果操作系统 VMware_workstation_full_12.5.2 unlocker208 Apple Max OS X(M)-CSDN博客 vcpu-0:VERIFY vmcore/vmm/main/physMem_monitor.c:1180 FILE: FileCreateDirectoryRetry: Non-retriable error encountered (C:\ProgramData\VMware): Cann…...
5分钟上手GitHub Copilot:AI编程助手实战指南
引言 近年来,AI编程工具逐渐成为开发者提升效率的利器。GitHub Copilot作为由GitHub和OpenAI联合推出的智能代码补全工具,能够根据上下文自动生成代码片段。本文将手把手教你如何快速安装、配置Copilot,并通过实际案例展示其强大功能。 一、…...
deepseek使用记录26——从体力异化到脑力异化
我们的一切发现和进步,似乎结果是使物质力量具有理智生命,而人的生命则化为愚钝的物质力量。AI快速发展的现实中,人面临着比工业革命更深刻的异化。在工业革命中,人的身躯沦为了机器的一部分,而现在人的脑袋沦为了AI的…...
数字身份DID协议:如何用Solidity编写去中心化身份合约
本文提出基于以太坊的自主主权身份(SSI)实现方案,通过扩展ERC-734/ERC-735标准构建链上身份核心合约,支持可验证声明、多密钥轮换、属性隐私保护等特性。设计的三层架构体系将身份控制逻辑与数据存储分离,在测试网环境…...
