加入收藏 | 设为首页 | 会员中心 | 我要投稿 唐山站长网 (https://www.0315zz.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长资讯 > 评论 > 正文

飞速发展的云计算

发布时间:2021-02-06 13:46:15 所属栏目:评论 来源:互联网
导读:在 Go Runtime 中存在着一系列的系统级内存调用方法,本文涉及的主要如下: sysAlloc:从 OS 系统上申请清零后的内存空间,调用参数是 _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE,得到的结果需进行内存对齐。 sysReserve:从 OS 系统中保留内存的地址

在 Go Runtime 中存在着一系列的系统级内存调用方法,本文涉及的主要如下:

  • sysAlloc:从 OS 系统上申请清零后的内存空间,调用参数是 _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_PRIVATE,得到的结果需进行内存对齐。
  • sysReserve:从 OS 系统中保留内存的地址空间,这时候还没有分配物理内存,调用参数是 _PROT_NONE, _MAP_ANON|_MAP_PRIVATE,得到的结果需进行内存对齐。
  • sysMap:通知 OS 系统我们要使用已经保留了的内存空间,调用参数是 _PROT_READ|_PROT_WRITE, _MAP_ANON|_MAP_FIXED|_MAP_PRIVATE。

看上去好像很有道理的样子,但是 mallocinit 方法在初始化时,到底是在哪里涉及了 mmap 方法呢,表面看不出来,如下:
 

  • spans:记录 arena 区域页号和 mspan 的映射关系。
  • bitmap:标识 arena 的使用情况,在功能上来讲,会用于标识 arena 的哪些空间地址已经保存了对象。
  • arean:arean 其实就是 Go 的堆区,是由 mheap 进行管理的,它的 MaxMem 是 512GB-1。而在功能上来讲,Go 会在初始化的时候申请一段连续的虚拟内存空间地址到 arean 保留下来,在真正需要申请堆上的空间时再从 arean 中取出来处理,这时候就会转变为物理内存了。

在这里的话,你需要理解 arean 区域在 Go 内存里的作用就可以了。

mmap

我们刚刚通过上述的分析,已经知道 mallocinit 的用途了,但是你可能还是会有疑惑,就是我们之前所看到的 mmap 系统调用,和它又有什么关系呢,怎么就关联到一起了,接下来我们先一起来看看更下层的代码,如下:
 

  • 判断当前是 64 位还是 32 位的系统。
  • 从 0x7fc000000000~0x1c000000000 开始设置保留地址。
  • 判断当前 GOARCH、GOOS 或是否开启了竞态检查,根据不同的情况申请不同大小的连续内存地址,而这里的 p 是即将要要申请的连续内存地址的开始地址。
  • 保存刚刚计算的 arena 的信息到 arenaHint 中。

可能会有小伙伴问,为什么要判断是 32 位还是 64 位的系统,这是因为不同位数的虚拟内存的寻址范围是不同的,因此要进行区分,否则会出现高位的虚拟内存映射问题。而在申请保留空间时,我们会经常提到 arenaHint 结构体,它是 arenaHints链表里的一个节点,结构如下:
 

  • runtime-osinit:获取 CPU 核心数。
  • runtime-schedinit:初始化程序运行环境(包括栈、内存分配器、垃圾回收、P等)。
  • runtime-newproc:创建一个新的 G 和 绑定 runtime.main。
  • runtime-mstart:启动线程 M。

注:来自@曹大的 《Go 程序的启动流程》和@全成的 《Go 程序是怎样跑起来的》,推荐大家阅读。

初始化运行环境

显然,我们要研究的是 runtime 里的 schedinit 方法,如下:
 

在这小节中,我们通过 macOS 的 dtruss 命令监听并查看了运行这个程序所进行的所有系统调用,发现了与内存管理有一定关系的方法如下:

  • mmap:创建一个新的虚拟内存区域,但这里需要注意,就是当系统调用 mmap 时,它只是从虚拟内存中申请了一段空间出来,并不会去分配和映射真实的物理内存,而当你访问这段空间的时候,才会在当前时间真正的去分配物理内存。那么对应到我们实际应用的进程中,那就是 VSZ 的增长后,而该内存空间又未正式使用的话,物理内存是不会有增长的。
  • madvise:提供有关使用内存的建议,例如:MADV_NORMAL、MADV_RANDOM、MADV_SEQUENTIAL、MADV_WILLNEED、MADV_DONTNEED 等等。
  • mprotect:设置内存区域的保护情况,例如:PROT_NONE、PROT_READ、PROT_WRITE、PROT_EXEC、PROT_SEM、PROT_SAO、PROT_GROWSUP、PROT_GROWSDOWN 等等。
  • sysctl:在内核运行时动态地修改内核的运行参数。

在此比较可疑的是 mmap 方法,它在 dtruss 的最终统计中一共调用了 10 余次,我们可以相信它在 Go Runtime 的时候进行了大量的虚拟内存申请。

我们再接着往下看,看看到底是在什么阶段进行了虚拟内存空间的申请。

注:若是 Linux 系统,可使用 strace 命令。

查看 Go Runtime

启动流程

通过上述的分析,我们可以知道在 Go 程序启动的时候 VSZ 就已经不低了,并且确定不是共享库等的原因,且程序在启动时系统调用确实存在 mmap 等方法的调用。

那么我们可以充分怀疑 Go 在初始化阶段就保留了该内存空间。那我们第一步要做的就是查看一下 Go 的引导启动流程,看看是在哪里申请的。

引导过程如下:

(编辑:唐山站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读