• Go运行时的内存分配器以及消耗指定大小的内存(C语言)


    对于go语言在运行时的一些内存分配,想要详细的了解,我们会用到自带的runtime.MemStats,有很多具体的细节实现,而不是简单的只看任务管理器中的内存分配。

    我们先来看下这个记录内存分配器的结构体

    1. type MemStats struct {
    2. Alloc uint64 #堆空间分配的字节数
    3. TotalAlloc uint64 #服务运行一直累积的总分配的堆空间,释放也不减少
    4. Sys uint64 #操作系统获取的内存
    5. Lookups uint64 #运行时执行的指针查找数,用于调试
    6. Mallocs uint64 #服务malloc的次数
    7. Frees uint64 #释放堆对象字节数
    8. HeapAlloc uint64 #服务分配的堆内存字节数
    9. HeapSys uint64 #系统分配的堆内存字节数
    10. HeapIdle uint64 #申请但未分配的堆内存或者回收了的堆内存字节数
    11. HeapInuse uint64 #正在使用的堆内存字节数
    12. HeapReleased uint64 #返回给OS的堆内存,类似C/C++中的free
    13. HeapObjects uint64 #堆内存块申请的量
    14. StackInuse uint64 #正在使用的栈字节数
    15. StackSys uint64 #系统分配的作为运行栈的内存
    16. MSpanInuse uint64 #用于测试用的结构体使用的字节数
    17. MSpanSys uint64 #系统为测试用的结构体分配的字节数
    18. MCacheInuse uint64 #结构体申请的字节数,不被视为垃圾回收
    19. MCacheSys uint64 #操作系统申请的堆空间用于mcache的量
    20. BuckHashSys uint64 #用于剖析桶散列表的堆空间
    21. GCSys uint64 #垃圾回收标记元信息使用的内存
    22. OtherSys uint64 #golang系统架构占用的额外空间
    23. NextGC uint64 #垃圾回收器检视的内存大小
    24. LastGC uint64 #垃圾回收器最后一次执行时间
    25. PauseTotalNs uint64 #垃圾回收或者其他信息收集导致服务暂停的次数
    26. PauseNs [256]uint64 #一个循环队列,记录最近垃圾回收系统中断的时间
    27. PauseEnd [256]uint64 # 一个循环队列,记录最近垃圾回收系统中断的时间开始点
    28. NumGC uint32 #垃圾回收完成的次数
    29. NumForcedGC uint32 #服务调用runtime.GC()强制使用垃圾回收的次数
    30. // GODEBUG=gctrace=1.
    31. GCCPUFraction float64 #垃圾回收占用服务CPU工作的时间总和
    32. #如果有100个goroutine,垃圾回收的时间为1s,总占用100s
    33. EnableGC bool #是否启用GC,如果启用,即便GOGC=off也是启用的
    34. DebugGC bool
    35. #内存分配器使用情况
    36. BySize [61]struct {
    37. Size uint32
    38. Mallocs uint64
    39. Frees uint64
    40. }
    41. }

    看个具体的示例:

    1. package main
    2. import (
    3. "fmt"
    4. "runtime"
    5. "time"
    6. )
    7. // 内存使用情况
    8. func PrintMemUsage() {
    9. var m runtime.MemStats
    10. runtime.ReadMemStats(&m)
    11. fmt.Printf("Alloc = %v MiB", bToMb(m.Alloc))
    12. fmt.Printf("\tTotalAlloc = %v MiB", bToMb(m.TotalAlloc))
    13. fmt.Printf("\tSys = %v MiB", bToMb(m.Sys))
    14. fmt.Printf("\tNumGC = %v\n", m.NumGC)
    15. }
    16. // 转国际标(兆字节)
    17. func bToMb(b uint64) uint64 {
    18. return b / 1024 / 1024
    19. }
    20. func main() {
    21. var s []string
    22. for i := 0; i < 100000000; i++ {
    23. s = append(s, "你好,寅恪光潜!")
    24. }
    25. PrintMemUsage()
    26. for {
    27. //runtime.GC() //调用GC进行内存回收
    28. PrintMemUsage()
    29. fmt.Println(s[0])
    30. time.Sleep(time.Second * 1)
    31. }
    32. }
    33. /*
    34. C:\Users\Tony>go run test.go
    35. Alloc = 5625 MiB TotalAlloc = 9192 MiB Sys = 8066 MiB NumGC = 25
    36. Alloc = 4486 MiB TotalAlloc = 9192 MiB Sys = 8066 MiB NumGC = 26
    37. 你好,寅恪光潜!
    38. Alloc = 4486 MiB TotalAlloc = 9192 MiB Sys = 8066 MiB NumGC = 26
    39. 你好,寅恪光潜!
    40. Alloc = 4486 MiB TotalAlloc = 9192 MiB Sys = 8066 MiB NumGC = 26
    41. 你好,寅恪光潜!
    42. Alloc = 4486 MiB TotalAlloc = 9192 MiB Sys = 8066 MiB NumGC = 26
    43. ...
    44. */

    然后去掉runtime.GC()注释,启用内存回收,出现如下效果:

    1. /*
    2. C:\Users\Tony>go run test.go
    3. Alloc = 4486 MiB TotalAlloc = 9192 MiB Sys = 7050 MiB NumGC = 26
    4. Alloc = 1838 MiB TotalAlloc = 9192 MiB Sys = 7050 MiB NumGC = 27
    5. 你好,寅恪光潜!
    6. Alloc = 1838 MiB TotalAlloc = 9192 MiB Sys = 7050 MiB NumGC = 28
    7. 你好,寅恪光潜!
    8. Alloc = 1838 MiB TotalAlloc = 9192 MiB Sys = 7050 MiB NumGC = 29
    9. 你好,寅恪光潜!
    10. Alloc = 1838 MiB TotalAlloc = 9192 MiB Sys = 7050 MiB NumGC = 30
    11. ...
    12. */

    上面选取了几个成员变量,可以看出这个运行时的内存查看还是挺方便详细的。

    有时候需要做内存压力测试等,我们可能会用到消耗一定的内存,没有Linux纯环境,我在WSL中运行,如果你也是Windows系统想要熟悉Linux也可以查阅:Windows版的Linux子系统(WSL)安装

    malloc_mb.c

    1. #include
    2. #include
    3. #include
    4. #define UNIT (1024*1024)
    5. int main(int argc, char *argv[])
    6. {
    7. long long i = 0;
    8. int size = 0;
    9. if (argc != 2)
    10. {
    11. printf("请输入需要分配的内存值\n");
    12. return 1;
    13. }
    14. #unsigned long int strtoul(const char *nptr, char **endptr, int base);
    15. #unsigned long long int strtoull(const char *nptr, char **endptr,int base);
    16. size = strtoull(argv[1], NULL, 10);#字符串转换成数字,十进制
    17. if (size == 0)
    18. {
    19. printf("请输入大于0的内存值");
    20. return 1;
    21. }
    22. char *buff = (char *) malloc(size * UNIT);
    23. if (buff)
    24. printf("已分配%dMB\n", size);
    25. buff[0] = 1;
    26. for (i = 1; i < (size * UNIT); i++)
    27. {
    28. if (i%1024 == 0)
    29. buff[i] = buff[i-1]/8;
    30. else
    31. buff[i] = i/2;
    32. }
    33. pause();
    34. }

    编译成一个可执行文件:gcc malloc_mb.c -o malloc,目录下面将有个malloc文件,然后运行它:./malloc 1000
    这样就会成功分配1000MB的内存!

  • 相关阅读:
    Unreal回放系统剖析(下)
    java计算机毕业设计基于springboo+vue的学生活动组织管理系统
    pytorch TORCH.NN 到底是什么?
    vue项目中在scss代码中使用data中的变量
    MySQL去重中 distinct 和 group by 的区别
    C++中String类详解以及仿写
    论文阅读--异常检测中实时大数据处理的研究挑战
    unity使用UniStorm 5.1.0.unitypackage增加天气
    全世界IT人苦竞业久矣!美国FTC宣布全面废除员工竞业协议
    软件工程知识总结梳理
  • 原文地址:https://blog.csdn.net/weixin_41896770/article/details/128035486