ToB企服应用市场:ToB评测及商务社交产业平台
标题:
记一次 .NET某数字化协同管理系统 内存暴涨分析
[打印本页]
作者:
万有斥力
时间:
2025-1-20 12:19
标题:
记一次 .NET某数字化协同管理系统 内存暴涨分析
一:背景
1. 讲故事
高级调试训练营里的一位朋侪找到我,说他们跑在linux上的.NET程序出现了内存泄漏的情况,上windbg观察发现内存都是IMAGE给吃掉了,那些image都标志了 doublemapper__deleted_ 字样,问我为啥会这样?说实话作为我们这些调试者非常喜好和这样的人打交道,毕竟沟通起来顺畅,也特别能激发对方的探索欲,这也是训练营给予的一种魅力吧。
二:内存暴涨分析
1. 为什么会暴涨
看过我这个系列的朋侪都知道观察内存用 !address -summary 命令,但这个命令是为 windows 打造的,所以在 linux 上行不通,为此sos提供了一个专门的命令 !maddress 来替代,接下来使用 !maddress -orderBySize 观察下内存分布情况。
0:000> !maddress -orderBySize
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Memory Kind | StartAddr | EndAddr-1 | Size | Type | State | Protect | Image |
+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Image | 7f4000000000 | 7f4007ff6000 | 127.96mb | MEM_IMAGE | MEM_COMMIT | PAGE_READWRITE | doublemapper__deleted_ |
| Image | 7f3fc4000000 | 7f3fcbff5000 | 127.96mb | MEM_IMAGE | MEM_COMMIT | PAGE_READWRITE | doublemapper__deleted_ |
| Image | 7f404c021000 | 7f4051b4c000 | 91.17mb | MEM_IMAGE | MEM_UNKNOWN | PAGE_UNKNOWN | doublemapper__deleted_ |
| Image | 7f3fae82e000 | 7f3fb4000000 | 87.82mb | MEM_IMAGE | MEM_COMMIT | PAGE_EXECUTE_READ | doublemapper__deleted_ |
| Image | 7f406c021000 | 7f40701ff000 | 65.87mb | MEM_IMAGE | MEM_UNKNOWN | PAGE_UNKNOWN | doublemapper__deleted_
...
+----------------------------------------------------------------------+
| Memory Type | Count | Size | Size (bytes) |
+----------------------------------------------------------------------+
| Image | 980 | 3.54gb | 3,801,517,056 |
| PAGE_READWRITE | 1,178 | 1.17gb | 1,255,059,968 |
| Stack | 66 | 499.35mb | 523,604,992 |
...
| NewStubPrecodeHeap | 4 | 64.00kb | 65,536 |
+----------------------------------------------------------------------+
| [TOTAL] | 8,254 | 6.01gb | 6,451,347,968 |
+----------------------------------------------------------------------+
复制代码
从卦象看,总计 6.4G 的内存使用,Image 就吃了 3.8G,从 details 看确实都标志了 doublemapper__deleted_,说实话我分析了300多例的dump,Image 吃了大头是第二次遇到,这种故障案例一样平常是可遇不可求的,接下来我们探究下 doublemapper__deleted_ 为何方神圣。
2. doublemapper__deleted_ 是什么
要想找到这个答案,先从 coreclr 源代码中探求蛛丝马迹,全局检索之后很快发现了关键词 doublemapper相关的代码:
bool VMToOSInterface::CreateDoubleMemoryMapper(void** pHandle, size_t *pMaxExecutableCodeSize)
{
#ifndef TARGET_OSX
#ifdef TARGET_FREEBSD
int fd = shm_open(SHM_ANON, O_RDWR | O_CREAT, S_IRWXU);
#elif defined(TARGET_SUNOS) // has POSIX implementation
char name[24];
sprintf(name, "/shm-dotnet-%d", getpid());
name[sizeof(name) - 1] = '\0';
shm_unlink(name);
int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600);
#else // TARGET_FREEBSD
int fd = memfd_create("doublemapper", MFD_CLOEXEC);
#endif // TARGET_FREEBSD
*pMaxExecutableCodeSize = MaxDoubleMappedSize;
*pHandle = (void*)(size_t)fd;
#else // !TARGET_OSX
*pMaxExecutableCodeSize = SIZE_MAX;
*pHandle = NULL;
#endif // !TARGET_OSX
return true;
}
复制代码
从卦象看,真尼玛乱,coreclr 为了兼容各种操作系统核,加了无数的 if,else 判断,无语了,最后在非OSX,非FREEBSD,非SUNOS的情况下走了 memfd_create 函数,到这里事情有了一些盼望了。
熟悉 Linux 的朋侪应该知道 memfd_create 是一个 Linux 系统调用,用于创建一个匿名文件描述符,假如在 Windows 上找等价函数的话,那就是 win32api 中的 CreateFileMapping 函数,即内存映射文件,这个在源码目次中也能观之一二:
大概有些朋侪对 memfd_create 的使用照旧有些含糊,我让 chatgpt 帮我生成一段简单的 demo 辅助大家理解下,简化后如下:
int main() {
const char *name = "example_memfd";
int fd;
size_t size = 1024; // 1 KB
void *map;
const char *text = "Hello, memfd_create!";
// Create the memory file descriptor
fd = memfd_create(name, MFD_CLOEXEC);
// Resize the memory file to the desired size
ftruncate(fd, size)
// Map the memory file into the address space
map = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
// Write some data to the memory file
strncpy(map, text, strlen(text));
// Print the data from the memory file
printf("Data in memory file: %s\n", (char *)map);
// Unmap the memory
munmap(map, size)
// Close the file descriptor
close(fd);
return 0;
}
复制代码
卦中的逻辑非常简单,必要注意的是这里有一个紧张步骤就是通过 mmap 将 fd 挂上物理内存,即 fd -> mmap s-a 7f3fc4000000 7f3fcbff5000-0x1 "MZ"00007f3f`c4059ce4 4d 5a 00 00 00 00 00 00-00 00 00 00 7c 00 00 00 MZ..........|...00007f3f`c44f2989 4d 5a 3c 40 7f 00 00 b1-05 00 00 94 99 00 00 80 MZ
欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/)
Powered by Discuz! X3.4