惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

T
Tailwind CSS Blog
C
CERT Recently Published Vulnerability Notes
P
Proofpoint News Feed
Vercel News
Vercel News
博客园 - 三生石上(FineUI控件)
IT之家
IT之家
Help Net Security
Help Net Security
月光博客
月光博客
N
News and Events Feed by Topic
Cloudbric
Cloudbric
博客园 - 司徒正美
L
LangChain Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
T
Tenable Blog
The Register - Security
The Register - Security
The Hacker News
The Hacker News
I
InfoQ
The Last Watchdog
The Last Watchdog
MyScale Blog
MyScale Blog
Schneier on Security
Schneier on Security
WordPress大学
WordPress大学
小众软件
小众软件
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
宝玉的分享
宝玉的分享
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
K
Kaspersky official blog
L
LINUX DO - 热门话题
N
News | PayPal Newsroom
F
Fortinet All Blogs
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
S
Security @ Cisco Blogs
Recorded Future
Recorded Future
大猫的无限游戏
大猫的无限游戏
H
Help Net Security
Google Online Security Blog
Google Online Security Blog
S
Schneier on Security
C
Cisco Blogs
N
News and Events Feed by Topic
V2EX - 技术
V2EX - 技术
Latest news
Latest news
PCI Perspectives
PCI Perspectives
T
The Blog of Author Tim Ferriss
P
Palo Alto Networks Blog
T
Tor Project blog
Project Zero
Project Zero
云风的 BLOG
云风的 BLOG
Webroot Blog
Webroot Blog
Attack and Defense Labs
Attack and Defense Labs
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org

重归混沌的BLOG

给silly实现了一个ernro模块 | 重归混沌的BLOG 给silly实现了一个ernro模块 | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG 第一次在生产环境使用 Vibe Coding | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG API 设计的艰难抉择 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 十年 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 在Go语言中如何使XML加载内存无限趋近于0 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG 对跨服玩法中的分布式一致性问题进行简单抽象 | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG Go语言逃逸分析之slice和map | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 谈谈观测 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 写了个AI Agent服务端 | 重归混沌的BLOG 谈谈代码设计中“严丝合缝” | 重归混沌的BLOG 谈谈代码设计中“严丝合缝” | 重归混沌的BLOG 一次艰难的线上游戏服务器内存排查经历 | 重归混沌的BLOG 如何基于LanguageServerProtocol来编写lint工具 谈谈游戏服务器中RPC模块的设计 谈谈游戏服务器代码抽象 最近碰到的一个分布式一致性问题 谈谈游戏服务器的自动化测试 对Raft协议的一点理解 使用mmap来学习/proc/pid/smaps 2023(完) 再次实现了一个Lua性能分析器 终于给Silly的定时器增加了取消功能 一次虚拟内存排查经历 游戏服务器分布式数据的一种同步的思路 为silly增加了互斥锁 2022(完) Go语言之闭包篇 一例误用unsafe包引起的内存问题 Go语言之内存篇 初识Go语言 重新抽象图形API 给Lua实现了一个数学库 谈谈跨平台图形API的抽象 寻路和Flocking算法的结合 行为树的一种高效实现 内测过程中Shader出现的问题 彻底解决多国语言 谈谈数据库的选型 再谈Lua热更新(终) 初窥Rust 关于游戏服务器的服务拆分 ECS的初步实现 ECS初探 屏幕空间(SreenSpace)的想象力 一些对辐射度量学的理解 深度缓冲和半透明渲染 Mysql的间隙锁 更新一些GPU相关知识 2020 地形渲染之爬过的坑 Lua5.3 GC源码阅读(5) 实现一个数据库存储队列 再学计算机图形学入门 再谈分布式服务架构 游戏上线一个月后的反思 一次并发Bug 双向链表的三种实现 谈谈随机数的使用 再谈性能优化 2019 Lua中的函数式编程 重构登录逻辑 Unity资源管理(续) 谈谈Unity的资源管理 一次关于Cache的性能分析 历史之2018 DC3算法 移动平台native代码遭遇的坑 从CPU层面谈谈优化 开卷有益(UNIX编程艺术篇) GC竞争问题 通过Mesh投影来实现贴花系统 谈谈我对数据同步的理解 又一个类型提升引起的Bug Lua5.3 GC源码阅读(4) Lua5.3 GC源码阅读(3) Lua5.3 GC源码阅读(2) Lua5.3 GC源码阅读(1) 三角形光栅化时遇到的坑 一次git事故 再见2017 又一个lua调试器 客户端缓存落地方案 Paxos算法 HTTP服务器的特点 一次性能优化经历 关于CPU分支预测 C程序中让两个不同版本的库共存 实现了一个AOI模块 一个高可伸缩的游戏服务器架构 关于网络协议封装的一些新想法
一次艰难的线上游戏服务器内存排查经历
重归混沌 · 2025-01-10 · via 重归混沌的BLOG

TL;DR: 透明大页导致的。

我们的游戏服务器程序是采用Go程序编写的,后面在经过各种努力之后,终于将启动内存从350M降低到150M左右。

但是上线之后,奇怪的事情发生了。

由于一些原因,我们使用了两个云服务商来部署服务器。

一个空的游戏服进程, 在其中一个服务商的ECS上表现正常,即是正常的150+M内存, 但是另一个云服务商的ECS上,则启动内存有240+M之多,整整差了80M。

这我觉得非常诡异,我先是使用Go语自带的pprof拉了一下heap数据。两个进程的内存分配信息虽然不能说完全相同,但是也相关不大。

当然我知道,pprof是建立在应用程序的基础上进行的,也就是说内存管理那一块,其实是没法暴露出来的。

比如,在malloc之后,虽然调用了free, 但是你去top这个进程,一般来讲RES列不会立即变小,这和内存归还策略有关。

比如,每个操作系统线程也占了4M栈内存, 但是两个进程的线程数量并没有显著不同。

再比如,由于goroutine的调度问题,导致分配和释放顺序的差异造成的内存碎片率不一致,内存管理库有可能因为碎片的原因,无法归还大块内存。我查看了一下两台机器的CPU和内存,果然不一样。但是我仔细分析了一下,进程在启动阶段开启会分配内存的goroutine只有2~3个,理论上来讲就算对碎片率有影响,那也是有限,不可能会造成如此显著的差异。

于是我想,也许是进程打开了很多数据表文件,加载之后没有释放。由于操作系统页调度的原因,其中一个平台的操作系统将其从内存中移除了,所以表现的是150M。

我去查看了两台机器上进程的/proc/pid/smaps,发现并没有任何数据表文件的映射,我把别的文件映射大小全统计了一下,也不过几M而已。

最后,我其实已经没有办法了,去找ChatGPT找找灵感,他让我使用getconf PAGESIZE来查看一下系统的页大小。

当我看到这个答案时,我几乎觉得他就是正确答案,失望再次随之而来,两个系统的页大小均为4K

于是我将目光投向了claude, 再次问出同样的问题,并说出两个机器的页大小一致。

claude给了一个思路,让我将使用pmap -x <pid>来分别打印出来两个机器上的服务器进程,以期望能找出点什么规律。

我太懒了,所以我将pmap的输出结果直接丢给了claude让其对比,claude输出如下:

通过对比两个进程的内存信息,我发现了一些关键差异:

虚拟内存大小差异:

进程1: VmSize = 1762456 kB
进程2: VmSize = 1688404 kB
差异约 74MB,这与你观察到的 80MB 接近

实际内存使用差异:

进程1: VmRSS = 248608 kB

RssAnon: 191344 kB
RssFile: 57264 kB

进程2: VmRSS = 161064 kB

RssAnon: 101188 kB
RssFile: 59876 kB

主要差异来自:

匿名内存(RssAnon)相差约 90MB:191MB vs 101MB
线程数不同:进程1有12个线程,进程2有11个线程
数据段大小(VmData)略有差异:

进程1: 336904 kB
进程2: 328260 kB

然而这个信息仅能说明内存的差异真的是来自于内存分配造成的,而不是别的什么原因。

最惨的是,这时我的免费次数用完了,于是我又切回了ChatGPT

这一次我将两个进程的/proc/pid/smaps的输出喂给了ChatGPT

ChatGPT发现了一个有意思的结论:

这74M的匿名内存块的映射大小不一致,一台机器上的部分内存块竟然是另一台机器上的两倍左右(并不是所有内存块都这样,由于这是前几天的事情,具体数值我忘记了)。

然而事情也仅仅到此了,再继续询问ChatGPT,他也只会车轱辘话来回说,说明他已经黔驴技穷了。


两个小时之后,运维同学说,怀疑是透明大页导致, 两个云服务商的机器,一个开了透明大页, 另一个没开。

而开了透明大页的机器刚好就是进程内存占用过多的机器。

运维同学将机器配置改完之后,重启进程果然内存变得一致了。

事实上,这次的排查我费了很大的力气,从早上9:30就开始了,一直查到11:00多。

但是翻来覆去,一直将目光锁定在操作系统之内。而没有怀疑是操作系统本身的机制引起的。

甚至都没往PAGESIZE上面想,更别说透明大页了,这次的排查经历给了我很大的警醒,这几年我太聚焦业务之上的代码分析了,已经很少会将其和内核的一些特殊机制去联想了。后期需要改进一下。