出于很多目的,我从最新的 Go 系统内核开发源码复制了一份代码,在一个正常的运行环境中构建(和重新构建)它,在构建版本基础上周期性地重新构建 Go 程序。近期我在用 ps 查看我的一个程序的内存使用情况时,发现它占用了约 138 GB 的巨大虚拟空间(Linux ps 命令结果的 VSZ 字段),尽管它的常驻内存还不是很大。某个进程的常驻内存很小,但是需要内存很大,通常是表示有内存泄露,因此我心里一颤。

(用之前版本的 Go 构建后,根据运行时间长短不同,通常会有 32 到 128 MB 不同大小的虚拟内存占用,比最新版本小很多。)

还好这不是内存泄漏。事实上,之后的实验表明即使是个简单的 hello world 程序也会有占用很大的虚拟内存。通过查看进程的 /proc//smaps 文件((cf)可以发现几乎所有的虚拟空间是由两个不可访问的 map 占用的,一个占用了约 8 GB,另一个约 128 GB。这些 map 没有可访问权限(它们取消了读、写和可执行权限),所以它们的全部工作就是专门为地址空间预留的(甚至没有用任何实际的 RAM)。大量的地址空间。

这就是现在的 Go 在 64 位系统上的低级内存管理的工作机制。简而言之,Go (理论上)从连续的 arena 区域上进行低级内存分配,申请 8 KB 的页;哪些页可以无限申请存储在一个巨大的 bitmap。在 64 位机器上,Go 会把全部的内存地址空间预留给 bitmap 和 arena 区域本身。程序运行时,当你的 Go 程序真正使用内存时,arena bitmap 和内存 arena 片段会从简单的预留地址空间变为由 RAM 备份的内存,供其他部分使用。

(bitmap 和 arena 通常是通过给 mmap 传入 PROT_NONE 参数进行初始化的。当内存被使用时,会使用 PROT_READ|PROT_WRITE 重新映射。当释放时,我不确定它做了什么,所以对此我不发表意见。)

这个例子是用当前发布的 Go 1.4 开发版本复现的。之前的版本的 64 位程序运行时会占用更小的需要空间,虽然读 Go 1.4 源码时我也没找到原因。

以我的理解,一个有意思的影响是 64 位 Go 程序的大部分内存分配都可能占用至多 128 GB 的空间(也可能在整个运行周期内所有的内存分配都会,我不确定)。

了解更多细节,请看 src/runtime/malloc2.go 的注释和 src/runtime/malloc1.gomallocinit()

我不得不说,这个比我最初以为地更有意思也更有教育意义,尽管这意味着查看 ps 不再是一个检测你的 Go 程序中内存泄露的好方法(温馨提示,我不确定它曾经是不是)。结论是,检测这类内存使用最好的方法是同时使用 runtime.ReadMemStats()(可以通过 net/http/pprof 暴露出去)和 Linux 的 smem 程序或者养成对有意义的内存地址空间占用生成详细信息的习惯。

PS: Unix 通常足够智能,可以理解 PROT_NONE 映射不会耗尽内存,因此不应该对系统内存过量使用的限制进行统计。然而,它们会统计每一个进程的总地址空间进行统计,这意味着你运行 1.4 的 Go 程序时不能真的使用这么多。由于总内存地址空间的最大数几乎不会达到,因此这似乎不是一个问题。

附录:在 32 位系统上是怎样的

所有的信息都在 mallocinit() 注释中。简而言之,就是运行时预留了足够大的 arena 来处理 2 GB 的内存(「仅」占用 256 MB)但是仅预留 2 GB 中理论上它可以使用的 512 MB 地址空间。如果后续的运行过程中需要更多内存,就向操作系统申请另一个块的地址空间,优先 arena 区域剩下的 1.5 GB 的地址空间中分配。大多数情况下,运行的程序都会正常申请到需要分配的空间。


via: https://utcc.utoronto.ca/~cks/space/blog/programming/GoBigVirtualSize

作者:ChrisSiebenmann 译者:lxbwolf 校对:polaris1119

本文由 GCTT 原创编译,Go语言中文网 荣誉推出

为什么你的 64-bit 程序可能占用巨大的虚拟空间的更多相关文章

  1. WPF 使用 AppBar 将窗口停靠在桌面上,让其他程序不占用此窗口的空间(附我封装的附加属性)

    原文:WPF 使用 AppBar 将窗口停靠在桌面上,让其他程序不占用此窗口的空间(附我封装的附加属性) 本文介绍如何使用 Windows 的 AppBar 相关 API 实现固定停靠在桌面上的特殊窗 ...

  2. 在linux下,查看一个运行中的程序, 占用了多少内存

    1. 在linux下,查看一个运行中的程序, 占用了多少内存, 一般的命令有 (1). ps aux: 其中  VSZ(或VSS)列 表示,程序占用了多少虚拟内存. RSS列 表示, 程序占用了多少物 ...

  3. Linux:使用rpcgen实现64位程序调用32位库函数

    摘要:本文介绍使用rpcgent实现64位程序调用32位库函数的方法,并给出样例代码. 我的问题 我的程序运行在64位Linux系统上,需要使用一个从外部获得的共享库中的函数,这个共享库是32位的,无 ...

  4. 32 bit 与 64 bit 程序(2)比较

    32 bit  与 64 bit 程序(2)区别 由于操作系统内存分配的不同,导致软件开发过程中,需要编译不同版本的软件. 几个重要概念:  (1)这里所说的的32位与64位程序,是指经过编译器编译后 ...

  5. 32 bit 与 64 bit 程序(1)如何识别?

    一, 怎样判断一个exe可执行程序是32位的还是64位的? 简单的方法: 一般来说在64位的windows7下,打开任务管理器可以知道哪些程序是32位的哪些是64位的,但是因为自己的电脑是64位的wi ...

  6. Linux:32/64位程序(应用程序、共享库、内核模块)

    摘要: Linux系统区分32/64位,相应地,应用程序.共享库和内核模块也区分32/64位. 本文以Ubuntu系统为例,介绍如何编译和使用32/64位的应用程序.共享库和内核模块. 1. 应用程序 ...

  7. 64位程序,long*转long 出错

    原因: long*在64位程序中占8个字节,long占4个字节.强转会出错. 解决方法: 把long用long long替换,long long 占8个字节

  8. C# Winform程序CPU占用高的原因和解决方法

    程序CPU占用高的可能原因: 1.存在死循环: 为什么死循环会导致CPU占用高呢?      虽然分时操作系统是采用时间片的机制对CPU的时间进行管理的,也就是说到了一定时间它会自动从一个进程切换到下 ...

  9. 64位程序调用32DLL解决方案

    最近做一个.NETCore项目,需要调用以前用VB6写的老程序,原本想重写,但由于其调用了大量32DLL,重写后还需要编译为32位才能运行,于是干脆把老代码整个封装为32DLL,然后准备在64位程序中 ...

随机推荐

  1. Python Ethical Hacking - Persistence(2)

    Polish the Python code by adding the become_persistent function. #!/usr/bin/env python import json i ...

  2. T2 监考老师 题解

    第二题,他并不是多难的算法.甚至连搜索都不用,他的题目要求和数据断定了他第二题的地位. 在一个大试场里,有 n 行 m 列的考生,小王和众多同学正在考试,这时,有一部分考生 作弊,当然,监考老师能发现 ...

  3. FileNotFoundError: [WinError 2] 系统找不到指定的文件。 解决方案

    用Idle运行Python脚本的时候发现如下错误: Traceback (most recent call last): File "C:\Users\DangKai\Desktop\pyt ...

  4. Springboot整合SpringSecurity--对静态文件进行权限管理

    文章目录 一.要求 二.依赖管理 三.配置config文件 四.扩展 一.要求 index.html 可以被所有用户访问 1.html只能被VIP1访问 2.html只能被VIP2访问 3.html只 ...

  5. C++ 优先队列priority_queue用法

    头文件:#include<queue> 操作: top 访问队头 empty 队列是否为空 size 返回队列元素个数 push 插入元素到队尾 pop 弹出队头 swap 交换内容 定义 ...

  6. redis基本操作介绍

    一.字符串 单个设置:set key value,如果key不存在则设置键值对,如果存在则修改 批量设置:mset key1 value1 [key2 value2] 单个获取:get key,如果k ...

  7. 线程_apply堵塞式

    ''' 创建三个进程,让三个进程分别执行功能,关闭进程 Pool 创建 ,apply执行 , close,join 关闭进程 ''' from multiprocessing import Pool ...

  8. 撸了一个 Feign 增强包

    前言 最近准备将公司的一个核心业务系统用 Java 进行重构,大半年没写 Java ,JDK 都更新到 14 了,考虑到稳定性等问题最终还是选择的 JDK11. 在整体架构选型时,由于是一个全新的系统 ...

  9. 不看会后悔系列之idea的使用小技巧

    虽然用idea已多达N年,但你对其所有的功能都了如指掌吗?了解如下小tips助你开发更通畅. 调试专题 (1)不用每次都重启debugdebug程序时,只修改了一点代码,怎么在不重启程序的前提下,看到 ...

  10. PHP password_needs_rehash() 函数

    password_hash() 函数用于检测散列值是否匹配指定的选项. PHP 版本要求: PHP 5 >= 5.5.0, PHP 7高佣联盟 www.cgewang.com 语法 bool p ...