当前位置: 首页 > wzjs >正文

做网站合肥哪家公司好苏州建设项目备案网站

做网站合肥哪家公司好,苏州建设项目备案网站,北京注册公司代理机构,顺企网企业查询一:背景 1. 讲故事 如何跟踪.NET程序的mmap泄露,这个问题困扰了我差不多一年的时间,即使在官方的github库中也找不到切实可行的方案,更多海外大佬只是推荐valgrind这款工具,但这款工具底层原理是利用模拟器&#xff…

一:背景

1. 讲故事

如何跟踪.NET程序的mmap泄露,这个问题困扰了我差不多一年的时间,即使在官方的github库中也找不到切实可行的方案,更多海外大佬只是推荐valgrind这款工具,但这款工具底层原理是利用模拟器,它的地址都是虚拟出来的,你无法对valgrind 监控的程序抓dump,并且valgrind显示的调用栈无法映射出.NET函数以及地址,这几天我仔仔细细的研究这个问题,结合大模型的一些帮助,算是找到了一个相对可行的方案。

二:mmap 导致的内存泄露

1. 一个测试案例

为了方便讲述,我们通过 C 调用 mmap 方法分配256个 4M 的内存块,即总计 1G 的内存泄露,参考代码如下:


#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <sys/mman.h>
#include <unistd.h>#define BLOCK_SIZE (4096 * 1024)            // 每个块 4096KB (4MB)
#define TOTAL_SIZE (1 * 1024 * 1024 * 1024) // 总计 1GB
#define BLOCKS (TOTAL_SIZE / BLOCK_SIZE)    // 计算需要的块数void mmap_allocation() {uint8_t* blocks[BLOCKS]; // 存储每个块的指针// 使用 mmap 分配 1GB 内存,分成多个 4MB 块for (size_t i = 0; i < BLOCKS; i++) {blocks[i] = (uint8_t*)mmap(NULL, BLOCK_SIZE,PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS,-1, 0);if (blocks[i] == MAP_FAILED) {perror("mmap 失败");return;}// 确保每个块都被实际占用memset(blocks[i], 20, BLOCK_SIZE);}printf("已经使用 mmap 分配 1GB 内存(分成 %d 个 %dKB 块)!\n", BLOCKS, BLOCK_SIZE/1024);printf("程序将暂停 10 秒,可以使用 top/htop 查看内存使用情况...\n");sleep(10);
}int main() {mmap_allocation();return 0;
}

为了能够让 C# 调用,我们将这个 c 编译成 so 库,即 windows 中的 dll 文件,参考命令如下:


root@ubuntu2404:/data2/c# gcc -shared -o Example_18_1_5.so -fPIC -g -O0 Example_18_1_5.c
root@ubuntu2404:/data2/c# ls -lh
total 24K
-rw-r--r-- 1 root root 1.2K May  7 10:47 Example_18_1_5.c
-rwxr-xr-x 1 root root  18K May  7 10:47 Example_18_1_5.so

接下来创建一个名为 MyConsoleApp 的 Console控制台项目。


root@ubuntu2404:/data2# dotnet new console -n MyConsoleApp --framework net8.0 --use-program-main
The template "Console App" was created successfully.Processing post-creation actions...
Restoring /data2/MyConsoleApp/MyConsoleApp.csproj:Determining projects to restore...Restored /data2/MyConsoleApp/MyConsoleApp.csproj (in 1.73 sec).
Restore succeeded.root@ubuntu2404:/data2# cd MyConsoleApp
root@ubuntu2404:/data2/MyConsoleApp# dotnet run
Hello, World!

项目创建好之后,接下来就可以调用 Example_18_1_5.so 中的mmap_allocation方法了,在真正调用之前故意用Console.ReadLine();拦截,主要是方便用 perf 去介入监控,最后不要忘了将生成好的 Example_18_1_5.so文件丢到 bin 目录下,参考代码如下:


using System.Runtime.InteropServices;namespace MyConsoleApp;class Program
{[DllImport("Example_18_1_5.so", CallingConvention = CallingConvention.Cdecl)]public static extern void mmap_allocation();static void Main(string[] args){MyTest();for (int i = 0; i < int.MaxValue; i++){Console.WriteLine($"{DateTime.Now} :i={i} 执行完毕,自我轮询中...");Thread.Sleep(1000);}Console.ReadLine();}static void MyTest(){Console.WriteLine("MyTest 已执行,准备执行 mmap_allocation 方法");Console.ReadLine();mmap_allocation();Console.WriteLine("MyTest 已执行,准备执行 mmap_allocation 方法");}
}

2. 使用 perf 监控mmap事件

Linux 上的 perf 你可以简单的理解成 Windows 上的 perfview,前者是基于 perf_events 子系统,后者是基于 etw事件,这里就不做具体介绍了,这里我们用它监控 mmap 的调用,因为拿到调用线程栈之后,就可以知道到底是谁导致的泄露。

为了能够让 perf 识别到 .NET 的托管栈,微软做了一些特别支持,即开启 export DOTNET_PerfMapEnabled=1 环境变量,截图如下:

更多资料参考:https://learn.microsoft.com/zh-cn/dotnet/core/runtime-config/debugging-profiling

  1. 终端1 上启动 C# 程序。

root@ubuntu2404:/data2/MyConsoleApp/bin/Debug/net8.0# export DOTNET_PerfMapEnabled=1
root@ubuntu2404:/data2/MyConsoleApp/bin/Debug/net8.0# dotnet MyConsoleApp.dll
MyTest 已执行,准备执行 mmap_allocation 方法
  1. 终端2 上开启 perf 对dontet程序的mmap进行跟踪。

root@ubuntu2404:/data2/MyConsoleApp# ps -ef | grep Console
root        3074    2197  0 11:14 pts/1    00:00:00 dotnet MyConsoleApp.dll
root        3241    3106  0 11:56 pts/3    00:00:00 grep --color=auto Console
root@ubuntu2404:/data2/MyConsoleApp# perf record -p 3074 -g -e syscalls:sys_enter_mmap

启动跟踪之后记得在 终端1 上按下Enter回车让程序继续执行,当跟踪差不多(大量的内存泄露)的时候,我们在 终端2 上按下 Ctrl+C 停止跟踪,截图如下:


root@ubuntu2404:/data2/MyConsoleApp# perf record -p 3074 -g -e syscalls:sys_enter_mmap
^C[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.139 MB perf.data (333 samples) ]

从输出看当前的 perf.data 有 333 个样本,0.13M 的大小,由于在 linux 上分析不方便,而且又是二进制的,所以我们将 perf.data 转成 perf.txt 然后传输到 windows 上分析,参考命令如下:


root@ubuntu2404:/data2/MyConsoleApp# ls
MyConsoleApp.csproj  Program.cs  bin  obj  perf.data
root@ubuntu2404:/data2/MyConsoleApp# perf script > perf.txt
root@ubuntu2404:/data2/MyConsoleApp# sz perf.txt

经过仔细的分析 perf.txt 的 mmap 调用栈,很快就会发现有人调了 256 次 4M 的 mmap 分配吃掉了绝大部分内存,那个上层的 memfd:doublemapper 就是 JIT 代码所存放的内存临时文件,由于有 DOTNET_PerfMapEnabled=1 的加持,可以看到 [unknown] 前面的方法返回地址,截图如下:

3. 这些地址对应的 C# 方法是什么

本来我以为 JIT很给力,在 perf 生成的 /tmp/perf-3074.map 文件中弄好了符号信息,结果搜了下没有对应的方法名,比较尴尬。


root@ubuntu2404:/data2/MyConsoleApp# grep "7f42f3f11967" /tmp/perf-3074.map
root@ubuntu2404:/data2/MyConsoleApp# grep "7f42f3f11a90" /tmp/perf-3074.map
root@ubuntu2404:/data2/MyConsoleApp# 

那怎么办呢?只能抓dump啦,这也是我非常擅长的,可以用 dotnet-dump抓一个,然后使用 !ip2md 观察便知。


root@ubuntu2404:/data2/MyConsoleApp# dotnet-dump collect -p 3074Writing full to /data2/MyConsoleApp/core_20250507_113516
Complete
root@ubuntu2404:/data2/MyConsoleApp# ls -lh 
total 1.2G
-rw-r--r-- 1 root root  242 May  7 10:50 MyConsoleApp.csproj
-rw-r--r-- 1 root root  769 May  7 11:05 Program.cs
drwxr-xr-x 3 root root 4.0K May  7 10:51 bin
-rw------- 1 root root 1.2G May  7 11:35 core_20250507_113516
drwxr-xr-x 3 root root 4.0K May  7 10:51 obj
-rw------- 1 root root 164K May  7 11:16 perf.data
-rw-r--r-- 1 root root 874K May  7 11:21 perf.txt
root@ubuntu2404:/data2/MyConsoleApp# dotnet-dump analyze core_20250507_113516
Loading core dump: core_20250507_113516 ...
Ready to process analysis commands. Type 'help' to list available commands or 'help [command]' to get detailed help on a command.
Type 'quit' or 'exit' to exit the session.
> ip2md 7f42f3f11967                                                                                                                            
MethodDesc:   00007f42f3f9f320
Method Name:          MyConsoleApp.Program.Main(System.String[])
Class:                00007f42f3fbb648
MethodTable:          00007f42f3f9f368
mdToken:              0000000006000002
Module:               00007f42f3f9cec8
IsJitted:             yes
Current CodeAddr:     00007f42f3f11920
Version History:ILCodeVersion:      0000000000000000ReJIT ID:           0IL Addr:            00007f437307e250CodeAddr:           00007f42f3f11920  (MinOptJitted)NativeCodeVersion:  0000000000000000
Source file:  /data2/MyConsoleApp/Program.cs @ 12
> ip2md 7f42f3f11a90                                                                                                                            
MethodDesc:   00007f42f3f9f338
Method Name:          MyConsoleApp.Program.MyTest()
Class:                00007f42f3fbb648
MethodTable:          00007f42f3f9f368
mdToken:              0000000006000003
Module:               00007f42f3f9cec8
IsJitted:             yes
Current CodeAddr:     00007f42f3f11a50
Version History:ILCodeVersion:      0000000000000000ReJIT ID:           0IL Addr:            00007f437307e2d2CodeAddr:           00007f42f3f11a50  (MinOptJitted)NativeCodeVersion:  0000000000000000
Source file:  /data2/MyConsoleApp/Program.cs @ 28
> ip2md 7f42f3f13557                                                                                                                            
MethodDesc:   00007f42f42f42b8
Method Name:          ILStubClass.IL_STUB_PInvoke()
Class:                00007f42f42f41e0
MethodTable:          00007f42f42f4248
mdToken:              0000000006000000
Module:               00007f42f3f9cec8
IsJitted:             yes
Current CodeAddr:     00007f42f3f134d0
Version History:ILCodeVersion:      0000000000000000ReJIT ID:           0IL Addr:            0000000000000000CodeAddr:           00007f42f3f134d0  (MinOptJitted)NativeCodeVersion:  0000000000000000
>                              

从 dotnet-dump 给的输出看,可以清楚的看到调用关系为: Main -> MyTest -> ILStubClass.IL_STUB_PInvoke -> mmap_allocation -> mmap

至此真相大白于天下。

三:总结

这类问题的泄露真的费了我不少心思,曾经让我纠结过,迷茫过,我也捣鼓过 strace,最终都无法找出栈上的托管函数,真的,目前 .NET 在 Linux 调试生态上还是很弱,好无奈,这篇文章我相信弥补了国内,甚至国外在这一块领域的空白,也算是这一年来对自己的一个交代。


文章转载自:

http://OV4ROzQd.ffksr.cn
http://qWTnKnjT.ffksr.cn
http://Rhc6AE5H.ffksr.cn
http://XIXA5Zlk.ffksr.cn
http://u6qos8Dm.ffksr.cn
http://qPGHDtW2.ffksr.cn
http://5kGShTaZ.ffksr.cn
http://gk3LqFMA.ffksr.cn
http://ghFLlvqW.ffksr.cn
http://FF3pced0.ffksr.cn
http://ZdNJelcu.ffksr.cn
http://nXusuqnB.ffksr.cn
http://Fk3rZB1d.ffksr.cn
http://PvGNz2rs.ffksr.cn
http://uH4cBJhm.ffksr.cn
http://cTQR0iYM.ffksr.cn
http://b0LAjVcG.ffksr.cn
http://ReweqZFL.ffksr.cn
http://lYKDf3BH.ffksr.cn
http://Vr7bzLAB.ffksr.cn
http://xw7Y5voX.ffksr.cn
http://RwglVOEZ.ffksr.cn
http://WHiF2A1k.ffksr.cn
http://pWk9EMkj.ffksr.cn
http://TVJOKPBQ.ffksr.cn
http://dSzPjVLJ.ffksr.cn
http://yo1NUb0a.ffksr.cn
http://qHTJTfu7.ffksr.cn
http://xn7lCniB.ffksr.cn
http://rio2w9kS.ffksr.cn
http://www.dtcms.com/wzjs/608864.html

相关文章:

  • 然后建设自营网站免费网站安全检测
  • 网站运营与管理的目的是城市建设模拟游戏网站中文注解
  • 辽宁省住房建设厅网站seo网络推广软文的格式
  • 网站建设wang.cd技术
  • 怎么制作网站教程下载企业网站建设找外包公司做
  • 文化公司做网站交文化事业费吗查排名的网站
  • 中山网站建设与设计wordpress钩子介绍
  • 百度公司可以建设网站规划建立一个网站 项目
  • 安徽省质量提升工程建设网站企业网站建设项目
  • 足球比分网站怎么建设李宁网站建设的可行性
  • 做微商建自己的网站有用吗wordpress加图标
  • 聊城哪里有做网站的网站中医建设
  • 某公司人事管理网站开发网页界面设计要重点掌握哪四个要点
  • 网站文字列表页模板002822中装建设股吧
  • 商丘做网站sqlongliqi个人网站相册怎么做
  • 网站开发用什么数据库桂林网站推广
  • 江西省住房建设部官方网站网站注销怎么做消
  • 太原做网站价格机械加工王
  • 昆明营销网站建设广州网站设计开发招聘
  • 网站单个页面潍坊免费网站制作
  • 毕节市城乡住房建设网站北京市朝阳区网站制作
  • 做网站运营工资多少湖北正规网站建设检修
  • 注册网站的免费网址com网站内容如何自动关联新浪微博
  • 淄博中企动力公司网站电脑培训速成班多少钱
  • 银川网站建设志达四方wordpress后台教程
  • 烟台制作网站的公司简介帝国cms建站实例教程
  • 网站开发与管理课程网络营销的主要内容
  • 永久免费网站建商城WordPress主题页面模板不见了
  • 公众号推文制作网站学网站平面设计
  • 阿里云怎么做网站深圳深圳网站制作