为什么要探究这个问题?

作为基础设施供应商,自己的服务占用多少内存,为什么要占用这么多内存,需要能说的清楚。作为一个云计算开发,这点问题都弄不清楚,说不过去。

§ 0x01 范围

讨论的只限于Linux X86平台下,因为实用第一。内存页大小为4KiB。

目标:说清楚一个大型的Go进程内存消耗在了哪里。

Go具有自己的运行时,其内存构成比较复杂。想说清楚和容易理解,有些难度。先从较简单的C程序说起。

§ 0x02 先说普通的C程序

正常能写出的最简单的程序,没有任何依赖,当然用汇编写出来的。个人更喜欢AT&T风格的,如下:

.section .data
.section .text
.globl _start
_start:
movl $1, %eax
movl $5, %ebx
int $0x80

写个Makefile。

all: demo

demo: demo.o
# ld -dynamic-linker /lib64/ld-linux-x86-64.so.2 -o $@.d -lc $<
ld -static --no-dynamic-linker -o $@ -lc $< demo.o: demo.s
as -gstabs -o $@ $< clean:
rm ./demo ./demo.o

简单说明下过程,通过as编译,然后通过ld进行链接,最终生成一个static link的二进制。它不依赖libc,在Linux下能做到的最小程序。

需要安装gcc和gas等组件。执行make后,生成一个demo的二进制。运行这个二进制,只有一个系统调用就是设置退出码。执行一下,并没有任何输出,因为它只设置了一个退出码。可以通过 echo $?进行确认。想进一步实验,可以改成别的退出码。

$ ./demo; echo $?
5

使用gdb调试下这个程序让它暂停住,看下它的内存占用。

ulin      7116  0.0  0.0    168    24 pts/13   t    09:17   0:00 /mnt/d/code/lean/go-memory/asm/demo

它的RSS值是24,单位是KiB,也就是说它个最小程序的内存占用是24KiB。

下面我们来探究下为什么最小的程序也会占用24KiB内存。进程是由二进制加载到内存中形成的,所以起点还是看二进制,也就是elf的组成。

通过readelf -S看到各段大小如下:

section size(B) 说明
NULL 0 elf中规定的空置段,占了4KiB
.text 代码段 12 大小只有12B,但对齐后16B
.symtab 符号表 144 可以通过strip去除
.strtab 字符串表 25
.shstrtab 33

elf的布局如下:

通过readelf -a -W demo获取。

1. ELF header
2. Program header
3. sections
4. section Header table
区段 偏移 大小
ELF header 0 64
Program header 64 56 * 2 = 112
sections 176 4096 + 16 + 144 + 25 = 4281
section header table 4320 5 * 64 = 320

上各section中,只有.text和会加载到内存中。代码段只有12B,为什么进程的RSS是24KiB。

/proc/<pid>/smaps中有记载详细的内存布局,demo的具体内容摘要如下。先说下Pss与Rss的区别。

Rss是实际段大小,Pss是私有段大小。如果是一个.so文件,有其他进程共享,则Pss=Rss/N。实际占用的是所有段的Pss值之和。

Pss值 说明
00400000-00401000 4KiB 根据 Program Header看,应该是NULL段加载进来的。只读。
00401000-00402000 4KiB 代码段,rxp,可执行。根据 Program Header加载的。
7ffff7ff9000-7ffff7ffd000 4KiB 看注释应该是环境变量。只读。
7ffff7ffd000-7ffff7fff000 4KiB vdso段,内核映射的伪so。
7ffffffdd000-7ffffffff000 8KiB

刚好是24KiB大小。因为内存页的属性是按照最小单页的粒度,所以代码段不足4KiB,但也分配了4KiB。

§ 0x03 小结

我们探讨了最小C程序的内存占用,将其RSS的组成分析清楚了。整体的结论如下:

  1. elf中的部分section会映射到进程的内存中。
  2. 进程中最终的内存section由两种来源:
  • 在Program Header中告诉加载器,把哪些字段加载到内存中,并设置对应的Flag。
  • 加载器自己规定的共用的一些段,如vdso、栈、环境变量段。

疑问:为什么没有看到堆段(即heap)呢?猜测那是glibc规定的,目前我们用的是汇编实现的,不需要用堆。

Go进程内存占用那些事(一)的更多相关文章

  1. Linux系统下输出某进程内存占用信息的c程序实现

    在实际工作中有时需要程序打印出某个进程的内存占用情况以作参考, 下面介绍一种通过Linux下的伪文件系统/proc 计算某进程内存占用的程序实现方法. 首先, 为什么会有所谓的 伪文件 呢. Linu ...

  2. Linux查看进程内存占用及内存使用情况

    LINUX进程内存占用查看方法(1)top可以直接使用top命令后,查看%MEM的内容.可以选择按进程查看或者按用户查看,如想查看oracle用户的进程内存使用情况的话可以使用如下的命令:$ top ...

  3. 监控Linux系统所选的服务所占进程内存占用

    [代码] #!/bin/bash #程序功能描述: # 监控系统所选的服务所占进程内存占用 #作者:孤舟点点 #版本:1.0 #创建时间:-- :: PATH=/bin:/sbin:/usr/bin: ...

  4. window 实用操作(结束已打开无法删除进程 内存占用)

    1.win7删除文件,文件夹或文件已在另一程序中打开:https://jingyan.baidu.com/article/e75057f2a41e88ebc91a8985.html 删除文件时,提示“ ...

  5. 查看LINUX进程内存占用情况

    可以直接使用top命令后,查看%MEM的内容.可以选择按进程查看或者按用户查看,如想查看oracle用户的进程内存使用情况的话可以使用如下的命令: (1)top top命令是Linux下常用的性能分析 ...

  6. 查看LINUX进程内存占用情况(转)

    可以直接使用top命令后,查看%MEM的内容.可以选择按进程查看或者按用户查看,如想查看oracle用户的进程内存使用情况的话可以使用如下的命令: (1)top top命令是Linux下常用的性能分析 ...

  7. 查看LINUX进程内存占用情况及启动时间

    可以直接使用top命令后,查看%MEM的内容.可以选择按进程查看或者按用户查看,如想查看oracle用户的进程内存使用情况的话可以使用如下的命令: (1) top top命令是Linux下常用的性能分 ...

  8. linux 查看某个进程内存占用情况命令

    1.先用ps查询进程号 ps -aux|grep 进程名字 2.查看更详细的内存占比 cat /proc/3664/status 返回结果:(其中VmRSS为进程所占用的内存)

  9. linux查看进程内存占用

    ps -aux | grep xxx USER        PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND 可以看到RSS ...

  10. 使用showmap分析android进程内存占用情况(转载)

    转自:http://my.oschina.net/shaorongjie/blog/105354 可以使用adb shell showmap pid查看一个进程的showmap,这对于我们来说非常有用 ...

随机推荐

  1. Android 7 修改启动动画和开机声音

    背景 在修改开机音量的时候,发现找不到对应的声音功能调用. 因此了解了一下安卓的开机声音是如何实现的. 安卓4~安卓7 都可以这么做. 参考: https://blog.csdn.net/chen82 ...

  2. Linux服务器从头配置

    安装配置jdk 下载 jdk jdk-8u171-linux-x64.tar.gz 将该压缩包放到/usr/local/jdk目录下然后解压(jdk目录需要自己手动创建) tar zxvf jdk-8 ...

  3. sshd管理限制登录配置(centos7.9)

    背景情况:为了公网的主机,被无限的密码爆破,需要对主机的ssh进行安装加固 1.首先要禁用root的远程登录和修改ssh的端口 vi /etc/ssh/sshd_config# 修改端口,不适用22端 ...

  4. 将传统应用带入浏览器的开源先锋「GitHub 热点速览」

    现代浏览器已经不再是简单的浏览网页的工具,其潜能正在通过技术不断地被挖掘和扩展.得益于 WebAssembly 等技术的出现,让浏览器能够以接近原生的速度执行非 JavaScript 语言编写的程序, ...

  5. TP5 连接多个数据库

    use think\Config; $config = Config::get('database2'); //读取第二个数据库配置 $connect = Db::connect($config); ...

  6. THINKPHP的CURD是什么意思?

    thinkphp的CURD是什么意思? 解释如下: 增:C    Create   在thinkphp的代码里面对应的方法是add(); 改:U   Update  在thinkphp的代码里面对应的 ...

  7. [MAUI 项目实战] 笔记App:程序设计

    前言 有人说现在记事类app这么多,市场这么卷,为什么还想做一个笔记类App? 一来,去年小孩刚出生,需要一个可以记录喂奶时间的app,发现市面上没有一款app能够在两步内简单记录一个时间,可能iOS ...

  8. AT_agc019_b 题解

    洛谷链接&Atcoder 链接. 题目简述 给定一个字符串 \(A\),可以选择区间 \([i,j]\) 翻转一次,求能得到多少本质不同的字符串.(\(A\) 的长度不超过 \(2 \time ...

  9. JavaScript 探究[] == ![]结果为true,而 {} == !{}却为false

    console.log( [] == ![] ) // true console.log( {} == !{} ) // false 在比较字符串.数值和布尔值的相等性时,问题还比较简单.但在涉及到对 ...

  10. java开发,入职第一天都干什么,带提前了解

    2024.7.24,帝都今晚大雨,在雨声磅礴的夜晚适合干什么,没错适合敲代码,写博客,今晚来聊下入职一个新公司,第一天都干什么. 无论是刚毕业的新手小白,还是工作十余年的职场老人,入职一家新公司,只要 ...