美文网首页
内存管理—— malloc 细节

内存管理—— malloc 细节

作者: 太刀 | 来源:发表于2021-09-08 21:19 被阅读0次
古装美女

1. 应用程序内存布局

在 Linux 系统中,应用程序的内存被分为若干个逻辑段,如图:


应用程序内存布局

其中,各个分段的意义是:

  • 代码段:程序编译后的可执行代码(指令)存放区域,在编译时确定了,同一个程序在不同机器上、在同一个机器上的不同次运行,同一个方法的入口地址都是确定的
  • 数据段:存放程序已初始化的静态常量和全局变量
  • BSS 段:存放未初始化的静态常量和全局变量
  • 堆:动态分配的内存区域,从低地址向高地址增长,大小不固定
  • 栈:存放局部变量、函数调用上下文等,栈的大小在程序启动时就固定了,一般是 8MB,系统提供了参数可以修改
    在上述分段中,文件映射段和堆的内存是由程序动态分配的,通常使用 C 标准库的 mallocmmap 方法来执行

2. malloc 是如何分配内存的

2.1 malloc 概述

实际上,malloc 是 C 标准库函数,而不是系统调用,mmapbrk 是系统调用,malloc 申请内存时有两种方式:

  • 方式一:通过系统调用 brk 从堆分配内存,具体方法是将堆空间的最高地址指针往高地址扩展,扩充堆区的大小
  • 方式二:通过系统调用 mmap 从文件映射区分配内存,具体方法是在文件映射区中找一块足够的空间,进行分配
    需要注意的是,此两种方式分配的都是 虚拟内存,并没有分配物理内存,那么什么时候进行物理内存的分配呢?在第一次访问虚拟空间时,查找页表失败,产生缺页中断,会进行物理分配的内存,并建立虚拟内存地址和物理内存地址的映射关系(生成页表项)

C 标准库中提供 malloc / free 函数分配和释放内存,这两个函数底层由 brk / mmap /unmap 等系统调用实现。

分别什么情况用 brkmmap 呢?
malloc 源码中定义了一个阈值 M_MMAP_THRESHOLD,默认为 128K

  • 当分配的值小于该阈值时,调用 brk
  • 否则,调用 mmap

2.2 一个例子

2.2.1 首先看看 brk 内存分配

图1-图3
  • 程序启动后,虚拟内存空间初始布局如图1 所示
  • 程序执行 A = malloc(30K) 后,执行系统调用 brk,将堆顶指针王高地址增加 30K,得到图2所示的内存布局。注意,此时只是完成了虚拟内存的分配,对应的物理内存还没分配,页表项也没创建,等到程序第一次读取 A 这块内存时,发生缺页中断,内核才会分配物理内存并建立对应页表项
  • 程序执行 B = malloc(40K) 后,同样的堆顶指针往高地址增加,如图3 所示

2.2.2 当 mmap 分配较大内存

图3-图6
  • 程序执行 C = malloc(200K),待分配的值超过了阈值,使用 mmap 分配,在堆和栈中间找一块空闲内存,并初始化为0,如图4所示。这样做的一个重要原因是,使用brk移动堆顶地址的方式分配内存,只有高地址的内存被释放后,才能释放低地址的内存,对于内存释放的顺序有依赖,如图4中,必须先释放 B ,才能释放 A,对于大块内存分配如果使用此种方式,将造成很多大块内存无法按需释放,而 mmap 分配的内存无依赖,可以单独释放

  • 程序执行 D=malloc(100k) 后,内存空间如图5所示

  • 程序调用 free(C) 释放内存,将 C 对应的 虚拟内存和物理内存一起释放,得到图6所示

2.2.3 内存的释放

图7-图9
  • 调用 free(B) 后的内存布局如图7,B 对应的虚拟内存和物理内存都没有释放,因为只有一个栈顶指针,由于 D 内存的存在,无法回推。当然,B 部分的内存是可重用的,此时如果来一个 40K 的分配请求,很可能就把 B 给分配返回了。
  • 程序执行 free(D) 后,内存布局如图 8 所示,B和D构成了一块 140K 的空闲内存
  • 系统默认当高地址空间的空闲内存超过 128K(由 M_TRIM_THRESHOLD 选项调节)时,会自动执行内存紧缩操作 (trim),在上一步 free(D)完成后,进行内存紧缩,内存布局变成图9所示,栈顶地址降低,释放对应的虚拟内存和物理内存

3. 总结

malloc 细节包括以下几点:

  • 当分配请求超过阈值时,使用 mmap 进行分配
  • 分配请求小于阈值时,使用 brk 分配
  • 分配时并没有立即分配物理地址,只是分配了虚拟地址,第一次访问时才建立页表项,分配物理地址
  • 释放 mmap 分配的地址时,可以立即释放
  • 释放 brk 分配的内存时,不会立即释放,但可以重用,执行释放时会检查堆顶指针附近的最大空闲块,如果超过阈值,则会执行内存紧缩策略,真正释放物理地址

相关文章

  • 虚幻 Unreal Engine 4内存管理

    不受内存管理的内存 malloc & free new & deletenew与malloc的区别在于,new在分...

  • Linux进程内存管理(一)

    本文主要讲用户态进程的内存管理,而不是内核的内存管理。简单地说,就是和 malloc 和 free 相关的内存管理...

  • android 内存泄漏全面解析

    引言: C/C++ 自己去分配内存和释放内存--手动管理 malloc free 什么是内存泄露:内存不在GC掌...

  • 内存优化(一)内存泄漏

    1.内存泄漏 C/C++ 自己去分配内存和释放内存——手动管理malloc和free 1.1.什么是内存泄露:内存...

  • JavaScript 内存机制

    每种编程语言都有它的内存管理机制,比如C语言这样的底层语言,有原生内存管理接口,像malloc()动态的分配内存空...

  • Day14

    内存管理 程序是静态的,进程是动态的 进程空间图示 栈内存 堆内存 基本概念 malloc函数 memset函数 ...

  • JavaScript 内存机制

    简介每种编程语言都有它的内存管理机制,比如简单的C有低级的内存管理基元,像malloc(),free()。同样我们...

  • JavaScript 内存机制&垃圾回收

    前言 每种编程语言都有它的内存管理机制,比如简单的C有低级的内存管理基元,像malloc(),free()。而对于...

  • Java虚拟机(一)——JVM内存分类

    传统程序语言:由程序员手动内存管理。C/C++,malloc申请内存和free释放内存,经常导致内存泄漏。 现代程...

  • 编程概念(二):python相关

    1、内存管理 内存计数 垃圾回收 内存池小于256字节使用Pymalloc,大对象用系统malloc 2、进程间通...

网友评论

      本文标题:内存管理—— malloc 细节

      本文链接:https://www.haomeiwen.com/subject/niywwltx.html