16 malloc 虚拟内存分配的调试(1)

这篇具有很好参考价值的文章主要介绍了16 malloc 虚拟内存分配的调试(1)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

呵呵 在 c 语言中 malloc 应该是初学者必须了解的一个函数了吧 

但凡 涉及到堆内存分配的相关, 必定会使用到 malloc, realloc, calloc 这几个函数 

其中 malloc 最常见, 也是最 实用 

在 HotspotVM 中也经常会看到 malloc 的身影 

我们这里 来调试一下 malloc 的相关的一些场景 

本文主要的主题有四个

1. malloc(20) 申请内存, 然后 brk 传递的参数是 132K
2. malloc 第一次内存分配 和 第二次内存分配
3. malloc 分配的内存的 header
4. malloc 20, 但实际可用的空间只有 16

测试用例

测试用例如下, 很简单的一个 case, 主要的目的在于使用 malloc, 以及观察 malloc 分配的内存的虚拟地址信息 

root@ubuntu:~/ClionWorkStations/HelloWorld# cat Test01Sum.c 
#include "stdio.h"

int main(int argc, char** argv) {

int x = 2;
int y = 3;
int z = x + y;

void *p1 = malloc(20);
void *p2 = malloc(20);
void *p3 = malloc(20);
printf("p1 : 0x%x\n", p1);
printf("p2 : 0x%x\n", p2);
printf("p3 : 0x%x\n", p3);

printf(" x + y = %d\n ", z);

}

编译使用我们自己手动编译的 glibc 库

export mainClass=Test01Sum

gcc $mainClass.c -o $mainClass -L /root/Desktop/linux/glibc-2.23/install/lib -Wl,--rpath=/root/Desktop/linux/glibc-2.23/install/lib -Wl,-I /root/Desktop/linux/glibc-2.23/install/lib/ld-linux-x86-64.so.2

程序输出结果如下 

p1 : 0x602010
p2 : 0x602030
p3 : 0x602050
 x + y = 5

1. malloc(20) 申请内存, 然后 brk 传递的参数是 132K

如上测试用例, malloc 20 申请内存的时候, 会使用系统调用 brk 来进行虚拟内存分配 

但是 奇怪的是分配的内存是 132k, 我命名只需要 20字节, 为什么 malloc 调用 brk 却申请了 132k 的虚拟内存, 这是为什么呢? 

如下图, 查看 brk 系统调用, 申请的 135168 字节, 合计 132k 的虚拟内存 

16 malloc 虚拟内存分配的调试(1)

接下来我们看一下 malloc 是怎么做的? 这个 132k 又是怎么来的呢?

大致看一下 上下文, 是在 main 中的第一个 malloc(20) 的地方调用了 brk 系统调用 

16 malloc 虚拟内存分配的调试(1)

看一下 increment, 果然是 135168 

16 malloc 虚拟内存分配的调试(1)

然后我们再来看一下 这个 135168 是怎么计算出来的 

这里有两个过程, 计算加对齐, 计算阶段为 32 + 131072 + 32 = 131136

然后和 4096 对齐一下, 之后的结果为 135168 

16 malloc 虚拟内存分配的调试(1)

然后另外一个问题是, 用户程序 申请的是 20 字节, 为什么实际申请的是 32 字节呢 

如下 checked_request2size 就是做的这个转换 

16 malloc 虚拟内存分配的调试(1)

checked_request2size 的计算方式如下 

代入 req 为 20, (20 + 8 + 15) & (0xfff0) = 32 

整个表达式的意思是找到比 (20 + 8) 大的最小的 16 的整数倍 

注意这里的 8, 额外只是加了一个 SIZE_SZ 

16 malloc 虚拟内存分配的调试(1)

 2. malloc 第一次内存分配 和 第二次内存分配

malloc 的分配流程为 fastbin, small_bin, unsorted_chunks, 迭代 bins, top 

在 Test01Sum 进入第一个 malloc 的时候均无可用空间, 直接到 use_top 

此时 av->top 无可用空间, 调用 sysmalloc 申请空间 

16 malloc 虚拟内存分配的调试(1)

 sysmalloc 中调用 brk 申请 所需要的 135168 字节的空间16 malloc 虚拟内存分配的调试(1)

然后之后是 申请需要的 32 字节 空间 

p / av->top 初始的值为 0x602000, 分配了 32 字节之后 remainder / av-> top 为 0x602020 

分别更新申请的 chunk 块的 size, 和 remainder 的 size 

然后 chunk2mem 的转换, 增加了 2 * SIZE_SZ = 16, 值为 0x602010

16 malloc 虚拟内存分配的调试(1)

分配的 chunk 的地址 + 两个头的大小[prev_size, size] 即为响应给用户的地址 

16 malloc 虚拟内存分配的调试(1)

第二次 malloc 的内存分配, 同上面 其他 fast_bin, small_bin 均无可用空间, 到 use_top 

第一次内存分配之后, av->top 为 0x602020, 用户需要空间为 32 字节 

因此 切割一个 32 byte 的 chunk 块 p 之后, av->top 为 0x602040

分别更新申请的 chunk 块的 size, 和 remainder 的 size 

p 的值为 0x602020, 然后 chunk2mem 的转换, 增加了 2 * SIZE_SZ = 16, 值为 0x602030

16 malloc 虚拟内存分配的调试(1)

至于第三次内存分配, 就和 第二次差不多了, 然后 我们再来回顾一下 程序的输出 

p1 : 0x602010
p2 : 0x602030
p3 : 0x602050
 x + y = 5

3. malloc 分配的内存的 header

看一下我们的 malloc_chunk, 头部有 2 * 8 个字节, 分别的逻辑意义为 prev_size, size 

在上面的流程中我们没有看到 prev_size 的使用, 呵呵 这个我们后续再来记录 

如果 前一个 chunk 空闲, 则 prev_size 记录的事该 chunk 的空间大小, 如果前一个 chunk 在使用, 则 prev_size 可以被前一个 chunk 使用 16 malloc 虚拟内存分配的调试(1)

size 记录的数据如下 

其中 PREV_INUSE 为 1, NON_MAIN_AREA 为 4, 这里可以知道最后 1bit 标记的是 PREV_INUSE, 倒数第三bit 标记的是 NON_MAIN_ARENA 

倒数第二 bit 标记的是, 内存块是使用的 brk 还是 mmap 来分配的空间 

然后 mask 掉后面 3bit, 的大小即为当前 chunk 的空间大小 

16 malloc 虚拟内存分配的调试(1)

第一次 malloc 分配的 chunk, chunk 的起始地址为 0x602000, 给用户的起始地址为 0x602010 

0x602000 起始的的 long 值为 0, 默认第一个 chunk 的前一个 chunk 在使用, prev_size 没有作用 

0x602008 起始的的 long 值为 chunk 的 size, 为 0x21, 表示了当前 chunk 空间是 32byte, MAIN_ARENA, 系统调用 brk 分配的内存

16 malloc 虚拟内存分配的调试(1)

第二次 malloc 分配的 chunk, chunk 的起始地址为 0x602020, 给用户的起始地址为 0x602030 

0x602020 起始的的 long 值为 0, 前一个 chunk 在使用, prev_size 没有作用 

0x602028 起始的的 long 值为 chunk 的 size, 为 0x21, 表示了当前 chunk 空间是 32byte, MAIN_ARENA, 系统调用 brk 分配的内存

当然 这里也可以看到 上面 第一个 chunk 的情况 

16 malloc 虚拟内存分配的调试(1)

4. malloc 20, 但实际可用的空间只有 16

如果 您够仔细的话, 你会发现 用户申请的是 20 字节 

然后 malloc 头, 占用的空间为 16 字节, 那么 至少应该申请 48 字节才对 

但是 为什么只申请了 32 字节? 

去掉头的 16 字节, 只有 16 字节了?? 这是 怎么回事 

如果 前一个 chunk 空闲, 则 prev_size 记录的事该 chunk 的空间大小, 如果前一个 chunk 在使用, 则 prev_size 可以被前一个 chunk 使用 

这个需要理解 prev_size 的使用了, 按照上面的反向推导一下 

如果当前 chunk 正在使用, 那么 他是可以使用下一个 chunk 的 prev_size 的 

因此 可以回顾一下上面这一段, 根据 用户申请的字节数, 计算需要申请的字节数 这一段, 计算的时候 只是额外增加了一个 SIZE_SZ, 因为当前 chunk 正在使用, 可以额外使用下一个 chunk 的 prev_size, 因此 这里的理解 和 上面的 checked_request2size 

注意这里的 8, 额外只是加了一个 SIZE_SZ 

5. malloc 0, 会怎么处理?

如果 bytes 为 0, 在 checked_request2size 的时候, malloc 会有最小分配空间的需求, 按照 MINSIZE 来进行分配空间, MINSIZE 为 32 字节 

所以 得到的还是一块 32 字节的空间, 但是 至于你如何使用 并没有具体强制约束 

16 malloc 虚拟内存分配的调试(1)

checked_request2size 如下, 调整了 sz, 如果 小于 MINSIZE 设置 sz 为 MINSIZE 

16 malloc 虚拟内存分配的调试(1)

完 文章来源地址https://www.toymoban.com/news/detail-405548.html

到了这里,关于16 malloc 虚拟内存分配的调试(1)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包赞助服务器费用

相关文章

  • 深入理解Java虚拟机——内存分配与回收策略

    深入理解Java虚拟机——内存分配与回收策略

    在读这篇博客之前,你需要了解分代收集理论中,收集器应该将Java堆划分出不同的区域**,**然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储。 例如 appel式回收 ,HotSpot虚拟机中的新生代收集器都采用了appel式回收来设计新生代内

    2024年02月04日
    浏览(13)
  • 《深入理解Java虚拟机》读书笔记:内存分配与回收策略

    《深入理解Java虚拟机》读书笔记:内存分配与回收策略

    Java技术体系中所提倡的自动内存管理最终可以归结为自动化地解决了两个问题:给对象分配内存以及回收分配给对象的内存。关于回收内存这一点,我们已经使用了大量篇幅去介绍虚拟机中的垃圾收集器体系以及运作原理,现在我们再一起来探讨一下给对象分配内存的那点事

    2024年02月13日
    浏览(17)
  • 《深入理解Java虚拟机(第三版)》读书笔记:Java内存区域与内存溢出异常、垃圾收集器与内存分配策略

    《深入理解Java虚拟机(第三版)》读书笔记:Java内存区域与内存溢出异常、垃圾收集器与内存分配策略

    下文是阅读《深入理解Java虚拟机(第3版)》这本书的读书笔记,如有侵权,请联系删除。 Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有

    2024年02月03日
    浏览(12)
  • 【云计算与虚拟化】第五章 实验一 vCenter Server的高级功能—vMotion、虚拟内存、虚拟CPU、磁盘分配及资源池

    【云计算与虚拟化】第五章 实验一 vCenter Server的高级功能—vMotion、虚拟内存、虚拟CPU、磁盘分配及资源池

    实验一    vCenter Server的高级功能—vMotion、虚拟内存、虚拟CPU、磁盘分配及资源池 1.通过vSphere client 客户端登陆vCenter服务器 (1)再次新建一个共享存储,iSCSI类型的,大小60G,名称为iSCSI-2。 (2)将运行在ESXi01主机上的虚拟机,在关机的状态下,数据存储位置不动,运行的

    2024年02月04日
    浏览(14)
  • Java虚拟机(JVM)垃圾收集器、新生代、老年代、永久代以及内存分配策略

    Java虚拟机(JVM)垃圾收集器、新生代、老年代、永久代以及内存分配策略

    在 Java 中,堆被划分成两个不同的区域:新生代 ( Young )、老年代 ( Old )。而新生代 ( Young ) 又被划分为三个区域:Eden、From Survivor、To Survivor。这样划分的目的是为了使 JVM 能够更好的管理堆内存中的对象,包括内存的分配以及回收。 新生代中一般保存新出现的对象,所以每次

    2024年02月04日
    浏览(15)
  • 【Java虚拟机学习2】HotSpot虚拟机下对象的创建及在Java堆中对象的内存分配、布局和对象的访问

    【Java虚拟机学习2】HotSpot虚拟机下对象的创建及在Java堆中对象的内存分配、布局和对象的访问

    对象的生命周期主要分为创建、使用、销毁这三大阶段。从它被创建的一刻开始,到它被垃圾收集器(Garbage Collector)回收的一刻结束 对象的创建 。包括:1、类的加载 2、内存的分配 3、初始化零值 4、设置对象头 5、执行init方法(具体操作步骤请看上述内容,其中步骤1的类

    2024年02月16日
    浏览(8)
  • 如何实现动态分配,malloc,realloc,calloc的使用方法,数组,链表,结构体实现动态分配(含代码实现)

    如何实现动态分配,malloc,realloc,calloc的使用方法,数组,链表,结构体实现动态分配(含代码实现)

    🎊【数据结构与算法】专题正在持续更新中,各种数据结构的创建原理与运用✨,经典算法的解析✨都在这儿,欢迎大家前往订阅本专题,获取更多详细信息哦🎏🎏🎏 🪔本系列专栏 -  数据结构与算法_勾栏听曲_0 🍻欢迎大家  🏹  点赞👍  评论📨  收藏⭐️ 📌个人主

    2023年04月27日
    浏览(11)
  • 动态内存函数malloc,calloc,realloc详解

    动态内存函数malloc,calloc,realloc详解

    🍍个人主页🍍:🔜勇敢的小牛儿🚩 🔱推荐专栏🔱:C语言知识点 ⚠️座右铭⚠️: 敢于尝试才有机会 🐒今日鸡汤🐒: 出色一点 从能力到容貌 目录 思维导图: 一,malloc : 1.1:malloc函数简介: 1.2:malloc函数的使用:  代码: 二,calloc函数 2.1calloc函数简介: 2.2calloc函数

    2024年02月04日
    浏览(9)
  • C语言——动态内存函数(malloc、calloc、realloc、free)

    C语言——动态内存函数(malloc、calloc、realloc、free)

    在C语言中,动态内存函数是块重要的知识点。以往,我们开辟空间都是固定得,数组编译结束后就不能继续给它开辟空间了,开辟的空间满了,就不能在开辟空间了(就是不能在添加数据了)。学习本文章,我们就可以解决这个问题,向内存申请空间,满啦可以继续申请空间

    2024年02月13日
    浏览(13)
  • 【C++】深入探讨内存管理:malloc/free与new/delete的区别以及如何避免内存泄漏

    在软件开发中,正确处理内存管理是至关重要的一环。在C++编程中,我们经常会用到动态内存管理的工具,比如 malloc/free 和 new/delete 。本文将深入探讨 malloc/free 与 new/delete 之间的区别,以及如何有效地避免内存泄漏问题。 都是用于从堆上申请空间,并需要手动释放。 mallo

    2024年02月22日
    浏览(13)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包