Golang Go语言中查看内存分配,用 strace 跟踪 mmap 调用的数据和 pmap 看到的内存不一致?

发布于 1周前 作者 songsunli 来自 Go语言

1 ,test 代码见: https://go.dev/play/p/2xa_tqKsjWH

2 ,使用命令 strace -e 'signal=!all' -e '!rt_sigreturn,rt_sigaction,rt_sigprocmask,futex' ./test 得到

execve("./test", ["./test"], 0x7fff95cd9970 /* 21 vars */) = 0
arch_prctl(ARCH_SET_FS, 0x4cba70)       = 0
sched_getaffinity(0, 8192, [0, 1, 2, 3, 4, 5, 6, 7]) = 32
openat(AT_FDCWD, "/sys/kernel/mm/transparent_hugepage/hpage_pmd_size", O_RDONLY) = 3
read(3, "2097152\n", 20)                = 8
close(3)                                = 0
mmap(NULL, 262144, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0b9870f000
mmap(NULL, 131072, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0b986ef000
mmap(NULL, 1048576, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0b985ef000
mmap(NULL, 8388608, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0b97def000
mmap(NULL, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0b93def000
mmap(NULL, 536870912, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0b73def000
mmap(0xc000000000, 67108864, PROT_NONE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0xc000000000
mmap(0xc000000000, 67108864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0xc000000000
mmap(NULL, 33554432, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0b71def000
mmap(NULL, 2165776, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0b71bde000
mmap(0x7f0b986ef000, 131072, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f0b986ef000
mmap(0x7f0b9866f000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f0b9866f000
mmap(0x7f0b981f5000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f0b981f5000
mmap(0x7f0b95e1f000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f0b95e1f000
mmap(0x7f0b83f6f000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f0b83f6f000
mmap(NULL, 1048576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0b71ade000
mmap(NULL, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0b71ace000
mmap(NULL, 65536, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0b71abe000
sigaltstack(NULL, {ss_sp=NULL, ss_flags=SS_DISABLE, ss_size=0}) = 0
sigaltstack({ss_sp=0xc000002000, ss_flags=0, ss_size=32768}, NULL) = 0
gettid()                                = 3689
clone(child_stack=0xc000050000, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS, tls=0xc000040090) = 3690
clone(child_stack=0xc000052000, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS, tls=0xc000040490) = 3691
clone(child_stack=0xc00004c000, flags=CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|CLONE_SYSVSEM|CLONE_SETTLS, tls=0xc000040890) = 3693
write(2, "0xc00003c768", 120xc00003c768)            = 12

3 ,使用pmap -p 3689得到

0000000000400000    380K r-x-- /root/logs/test
000000000045f000    420K r---- /root/logs/test
00000000004c8000     16K rw--- /root/logs/test
00000000004cc000    200K rw---   [ anon ]
000000c000000000  65536K rw---   [ anon ]
00007f0b71a7e000  36292K rw---   [ anon ]
00007f0b73def000 263680K -----   [ anon ]
00007f0b83f6f000      4K rw---   [ anon ]
00007f0b83f70000 293564K -----   [ anon ]
00007f0b95e1f000      4K rw---   [ anon ]
00007f0b95e20000  36692K -----   [ anon ]
00007f0b981f5000      4K rw---   [ anon ]
00007f0b981f6000   4580K -----   [ anon ]
00007f0b9866f000      4K rw---   [ anon ]
00007f0b98670000    508K -----   [ anon ]
00007f0b986ef000    384K rw---   [ anon ]
00007ffdc654c000    132K rw---   [ stack ]
00007ffdc65a0000     12K r----   [ anon ]
00007ffdc65a3000      8K r-x--   [ anon ]

我发现 2 个令人疑惑的地方,希望有大佬能指点:

  1. 地址 7f0b986ef000 处,mmap 调用明明只申请了 128K 内存,怎么 pmap 命令却显示此处有 384K 内存。
  2. pmap 显示地址 7f0b71a7e000 处的内存有 36292K ,这个内存地址是如何产生的? starce 跟踪到的 mmap 调用并没有看到这个地址。

Golang Go语言中查看内存分配,用 strace 跟踪 mmap 调用的数据和 pmap 看到的内存不一致?

更多关于Golang Go语言中查看内存分配,用 strace 跟踪 mmap 调用的数据和 pmap 看到的内存不一致?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

你没有 -f 跟踪子线程 可以先增加跟踪,看看是不是符合预期。

更多关于Golang Go语言中查看内存分配,用 strace 跟踪 mmap 调用的数据和 pmap 看到的内存不一致?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


kernel 会对进程的 memory mapping 作 merge,比如你先申请 128K 再申请 256K,如果 128K 后的 gap 里有 256K 空间可用,
这两块 vma 会被合并在到一起, 在 process mapping 上就体现为一块 384K 的虚拟地址区域.

在Golang(Go语言)中查看内存分配时,遇到使用 strace 跟踪 mmap 调用的数据和 pmap 看到的内存不一致的情况,通常是由以下几个原因导致的:

  1. 内存管理差异:Go语言有自己的内存管理机制,包括垃圾回收(GC)和内存池。这些机制可能会在运行时动态调整内存使用,导致直接通过 strace 跟踪 mmap 调用时看到的内存分配情况与 pmap 显示的结果不一致。

  2. 系统调用与进程状态strace 跟踪的是系统调用,它记录的是 mmap 请求的内存大小,而 pmap 显示的是进程当前实际使用的内存状态。这两者之间存在时间差和状态差异,可能导致数据不一致。

  3. 内存碎片和共享内存:Go的内存管理可能导致内存碎片,或者在不同goroutine之间共享内存。这些复杂的内存使用情况可能不会被 strace 准确捕捉,但会在 pmap 中有所体现。

  4. 工具使用限制stracepmap 在使用时都有其局限性。例如,strace 可能因为系统调用过多而丢失部分信息,而 pmap 显示的内存信息可能受到系统内核参数的影响。

为了更准确地分析内存使用情况,建议使用Go语言的内存分析工具,如 pprof,它可以直接与Go的运行时(runtime)交互,提供更准确和详细的内存分配和使用情况。同时,结合系统监控工具(如 tophtop)和日志分析,可以更全面地了解内存的使用状态。

回到顶部