记录一下 malloc 是如何分配内存的

系统深入学习笔记-malloc

以 32 位系统为例,,通过这张图你可以看到,用户空间内存从低到高分别是 6 种不同的内存段:
在这里插入图片描述

  • 代码段,包括二进制可执行代码;
  • 数据段,包括已初始化的静态常量和全局变量
  • BSS 段,包括未初始化的静态变量和全局变量;
  • 堆段,包括动态分配的内存,从低地址开始向上增长;
  • 文件映射段,包括动态库、共享内存等,从低地址开始向上增长(跟硬件和内核版本有关 (opens new window));
  • 栈段,包括局部变量和函数调用的上下文等。栈的大小是固定的,一般是 8 MB。当然系统也提供了参数,以便我们自定义大小;

在这 6 个内存段中,堆和文件映射段的内存是动态分配的。比如说,使用 C 标准库的 malloc() 或者 mmap() ,就可以分别在堆和文件映射段动态分配内存。

关于malloc分配方式

实际上,malloc() 并不是系统调用,而是 C 库里的函数,用于动态分配内存。

malloc 申请内存的时候,会有两种方式向操作系统申请堆内存。

方式一:通过 brk() 系统调用从堆分配内存
方式二:通过 mmap() 系统调用在文件映射区域分配内存;

brk方式

通过 brk() 函数将「堆顶」指针向高地址移动,获得新的内存空间
在这里插入图片描述

mmap方式

通过 mmap() 系统调用中「私有匿名映射」的方式,在文件映射区分配一块内存,也就是从文件映射区“偷”了一块内存
在这里插入图片描述

什么场景下 malloc() 会通过 brk() 分配内存?又是什么场景下通过 mmap() 分配内存?

malloc() 源码里默认定义了一个阈值:

如果用户分配的内存小于 128 KB,则通过 brk() 申请内存;
如果用户分配的内存大于 128 KB,则通过 mmap() 申请内存;
注意,不同的 glibc 版本定义的阈值也是不同的。
(glibc是GNU发布的libc库,即c运行库。glibc是linux系统中最底层的api,几乎其它任何运行库都会依赖于glibc。glibc除了封装linux操作系统所提供的系统服务外,它本身也提供了许多其它一些必要功能服务的实现)

attention

malloc() 分配的是虚拟内存。
如果分配后的虚拟内存没有被访问的话,虚拟内存是不会映射到物理内存的,这样就不会占用物理内存了。
只有在访问已分配的虚拟地址空间的时候,操作系统通过查找页表,发现虚拟内存对应的页没有在物理内存中,就会触发缺页中断,然后操作系统会建立虚拟内存和物理内存之间的映射关系。

malloc() 在分配内存的时候,并不是老老实实按用户预期申请的字节数来分配内存空间大小,而是会预分配更大的空间作为内存池。

具体会预分配多大的空间,跟 malloc 使用的内存管理器有关系,我们就以 malloc 默认的内存管理器(Ptmalloc2)来分析。

free 释放内存,会归还给操作系统吗?

  1. malloc 通过 brk() 方式申请的内存,free 释放内存的时候,并不会把内存归还给操作系统,而是缓存在 malloc 的内存池中,待下次使用;
  2. malloc 通过 mmap() 方式申请的内存,free 释放内存的时候,会把内存归还给操作系统,内存得到真正的释放

那为什么不全用mmap方式或全用brk方式

  • 不全用mmap
    因为向操作系统申请内存,是要通过系统调用的,执行系统调用是要进入内核态的,然后在回到用户态,运行态的切换会耗费不少时间。

所以,申请内存的操作应该避免频繁的系统调用,如果都用 mmap 来分配内存,等于每次都要执行系统调用。

另外,因为 mmap 分配的内存每次释放的时候,都会归还给操作系统,于是每次 mmap 分配的虚拟地址都是缺页状态的,然后在第一次访问该虚拟地址的时候,就会触发缺页中断。

也就是说,频繁通过 mmap 分配内存的话,不仅每次都会发生运行态的切换,还会发生缺页中断(在第一次访问虚拟地址后),这样会导致 CPU 消耗较大。

为了改进这两个问题,malloc 通过 brk() 系统调用在堆空间申请内存的时候,由于堆空间是连续的,所以直接预分配更大的内存来作为内存池,当内存释放的时候,就缓存在内存池中。

等下次在申请内存的时候,就直接从内存池取出对应的内存块就行了,而且可能这个内存块的虚拟地址与物理地址的映射关系还存在,这样不仅减少了系统调用的次数,也减少了缺页中断的次数,这将大大降低 CPU 的消耗

  • 不全用brk
    前面我们提到通过 brk 从堆空间分配的内存,并不会归还给操作系统,那么我们那考虑这样一个场景。

如果我们连续申请了 10k,20k,30k 这三片内存,如果 10k 和 20k 这两片释放了,变为了空闲内存空间,如果下次申请的内存小于 30k,那么就可以重用这个空闲内存空间。

但是如果下次申请的内存大于 30k,没有可用的空闲内存空间,必须向 OS 申请,实际使用内存继续增大。

因此,随着系统频繁地 malloc 和 free ,尤其对于小块内存,堆内将产生越来越多不可用的碎片,导致“内存泄露”。而这种“泄露”现象使用 valgrind 是无法检测出来的。

所以,malloc 实现中,充分考虑了 brk 和 mmap 行为上的差异及优缺点,默认分配大块内存 (128KB) 才使用 mmap 分配内存空间。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/88373.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

燃气安全如何保障?万宾燃气管网监测系统时刻感知管网运行态势

近年来随着我国城镇化建设的加快,燃气已经成为每个家庭的必需品。然而,每年夏季频繁发生的燃气爆炸事故,已经严重危害人民生命财产安全危害社会公共安全和公共利益。为了保障燃气安全运行,近日,许多城市都在大力推进燃…

深入浅出Java的多线程编程——第二篇

目录 前情回顾 1. 中断一个线程 1.1 中断的API 1.2 小结 2. 等待一个线程 2.1 等待的API 3. 线程的状态 3.1 贯彻线程的所有状态 3.2 线程状态和状态转移的意义 4. 多线程带来的的风险-线程安全 (重点) 4.1 观察线程不安全 4.2 线程安全的概念 4.3 线程不安全的原因…

代码随想录算法训练营第23期day7| 454.四数相加II 、383. 赎金信 、15. 三数之和、18. 四数之和

目录 一、(leetode 454)四数相加II 二、(leetcode 383)赎金信 暴力解法 哈希法 三、(leetcode 15)三数之和 四、(leetcode 18)四数之和 一、(leetode 454&#xf…

电涌保护器外部专用脱离器(SCB)后备保护器产品说明

为了更好的满足不同应用场景的市场需求,地凯科技经过两年多不断的研发与试验,对电涌保护器外部专用脱离器 SCB 后备保护器产品(以下简称 SCB)进行了技术升级,升级后的SCB 产品在电气性能、外观尺寸、智能化和可靠性等方…

springboot+vue智能诊后随访系统 java医院挂号预约诊断系统

本系统是基于java前端架构Vue用java编程语言及javascript、CSS、HTML语言进行编写设计并实现相关功能的。 设计步骤及措施: (1)确定项目名称、项目研究内容,开题报告提交及修改。 (2)项目开发准备&#xff…

全志H616在低温reboot过程中进入休眠解决方法

主题 H618在DDR物料适配支持时候,reboot实验异常进休眠,在reboot老化测试中报如下log1 [2023-07-11,16:56:44][ 40.325238][ T1] init: Untracked pid 1888 exited with status 0 [2023-07-11,16:56:44][ 40.325295][ T5] binder: undeliver…

比起“如果环境这样这样,那便那样那样”,我更喜欢听到“要怎样怎样变成想要的样子”

比起“如果环境这样这样,那便那样那样”,我更喜欢听到“要怎样怎样变成想要的样子” 许多事情不只是选择题、判断题,还可以是填空题、论文,重点是你怎么看待,格局有没有打开.

中睿天下参展2023海军工程大学首届网络安全文化周并发表主题演讲

2023年9月3日至9月8日,海军工程大学首届网络安全文化周活动于武汉举办。本次活动以“守护蓝疆网安有我”为主题,设有特邀嘉宾前沿讲座、网络安全圆桌交流论坛、网络安全科技展、网络对抗实战竞技、网络安全保密视频创作和信息安全知识竞赛等系列活动。 海…

自学WEB后端03-Node.js 语法

学习后端路线: JavaScript 基础语法 Node,js 内置 API 模块 (fs、 path、 http等) 第三方 API 模块 (express、mysql等) 今天主要回顾下Node.js 语法 Node.js 是基于 Chrome V8 引擎的 JavaScript 运行环境,它提供了一种能够在服务器端运行 JavaScr…

Selenium和Requests搭配使用

Selenium和Requests搭配使用 前要1. CDP2. 通过requests控制浏览器2. 1 代码一2. 2 代码2 3. 通过selenium获取cookie, requests携带cookie请求 前要 之前有提过, 用selenium控制本地浏览器, 提高拟人化,但是效率比较低,今天说一种selenium和requests搭配使用的方法 注意: 一定…

2023网络安全面试题(附答案)+面经

前言 随着国家政策的扶持,网络安全行业也越来越为大众所熟知,相应的想要进入到网络安全行业的人也越来越多,为了拿到心仪的Offer之外,除了学好网络安全知识以外,还要应对好企业的面试。 所以在这里我归纳总结了一些网…

软件的开发步骤,需求分析,开发环境搭建,接口文档 ---苍穹外卖1

目录 项目总览 开发准备 开发步骤 角色分工 软件环境 项目介绍 产品原型 技术选型 开发环境搭建 前端:默认已有 后端 使用Git版本控制 数据库环境搭建 前后端联调 ​登录功能完善 导入接口文档 使用swagger​ 和yapi的区别 常用注解 项目总览 开发准备 开发步骤…

如何通过bat批处理实现快速生成文件目录,一键生成文件名和文件夹名目录

碰对了情人,相思一辈子。 具体方法步骤: 一、创建一个执行bat文件(使用记事本即可); 1、新建一个txt文本空白记事本文件 2、复制以下内容进记事本内 dir/a/s/b>LIST.TXT (其中LIST.TXT文件名是提取后将…

小皮面板配置Xdebug,调试单个php文件

小皮面板配置Xdebug 首先下载phpstrom,和小皮面板 打开小皮面板,选中好要使用的php版本 然后点击【管理】> 【php扩展】> 【xdebug】 然后打开选中好版本的php位置 D:\Program_Files\phpstudy_pro\Extensions\php\php7.4.3nts打开php.ini文件…

Java8实战-总结34

Java8实战-总结34 重构、测试和调试使用 Lambda 重构面向对象的设计模式观察者模式责任链模式 重构、测试和调试 使用 Lambda 重构面向对象的设计模式 观察者模式 观察者模式是一种比较常见的方案,某些事件发生时(比如状态转变)&#xff0…

it网络设备监控系统

企业对网络监控系统的需求也在增加。网络监控系统是一种软件和硬件的组合,用于监控和管理企业的网络系统。它帮助企业实时了解网络情况,防范和处理网络问题,保证企业业务的正常使用。那么,IT网络监控系统监控什么设备呢&#xff1…

前端求职指南

简历求职指南 为什么没有面试? 1、简历写的不好 2、简历投递不好 简历的定义是什么? 是求职者向未来雇主展示自己专业技能和职业素养的自我推销工具,以找到工作为目的。 什么时候改简历? 每半年或一年更新一次工作中的成长 再工…

基于VR元宇宙技术搭建林业生态模拟仿真教学系统

随着科技的飞速发展,教学方式也正在经历着巨大的变革。林业经济学元宇宙虚拟教学系统作为一种新兴的教学方式,为学生和教师提供了一个全新的、沉浸式的学习和教学环境。 森林管理和监测 元宇宙技术可以用于森林管理和监测。通过无人机、传感器和虚拟现实…

docker 安装 nessus新版、awvs15-简单更快捷

一、docker 安装 nessus 参考项目地址: https://github.com/elliot-bia/nessus 介绍:几行代码即可一键安装更新 nessus -推荐 安装好 docker后执行以下命令 #拉取镜像创建容器 docker run -itd --nameramisec_nessus -p 8834:8834 ramisec/nessus …

Spring Boot自动装配原理超详细解析

目录 前言一、什么是SPI?1. JDK中的SPI2. Spring中的SPI2.1 加载配置2.2 实例化 二、Import注解和ImportSelector是什么?1. 代码示例2. 过程解析3. 源码分析 三、Spring Boot的自动装配1.源码分析2.代码示例3.Spring Boot自带的自动装配 四、总结 前言 …