• ptmalloc源码分析 - malloc/free函数的实战篇(12)


    目录

    一、chunk的大小实验

    二、获取使用中的chunk信息的实验

    三、小内存块尝试获取fd信息的实验

    四、常用malloc/free函数使用注意事项


    看了前面11章节的内容,我们也基本了解了ptmalloc的内存管理逻辑。此处也可以通过一些手段,获取到chunk的信息,可以来实战实验一把。

    一、chunk的大小实验


    在《ptmalloc源码分析 - 内存组织单元malloc_chunk(03)》章里面我们讲解了chunk的数据结构和大小:

    1. chunk在使用中的时候,只使用mchunk_prev_size和mchunk_size两个字段
    2. chunk在空闲的时候,会复用用户空间,存储fd/bk/fd_nextsize/bk_nextsize,用户空间最小是16字节
    3. chunk主要存储mchunk_prev_size和mchunk_size两个字段,所以chunk的结构体8个字段就能满足
    4. 每次分配的大小:(16字节 + 分配的内存) & 进行2*SIZE_SZ对齐(64位系统是16字节/32位系统8字节)
    1. #include
    2. #include
    3. #include
    4. #include
    5. int main() {
    6. char *s = NULL;
    7. s = (char *) malloc (23 * sizeof(char));
    8. char *p = NULL;
    9. p = (char *) malloc (25 * sizeof(char));
    10. char *p1 = NULL;
    11. p1 = (char *) malloc (110 * sizeof(char));
    12. printf("s : %d\r\n", s);
    13. printf("p : %d\r\n", p);
    14. printf("p1 : %d\r\n", p1);
    15. }

    结果:

    1. $./main
    2. s : 6299664
    3. p : 6299696
    4. p1 : 6299744
    5. /****************/
    6. s的值:23 + 8 = 31 & 16 = 32
    7. p的值:25 + 8 = 33 & 16 = 48

    二、获取使用中的chunk信息的实验


    我们将malloc_chunk的结构拷贝过来,以及mem2chunk/chunk2men/chunksize等几个宏定义也拷贝过来。

    这样,我们可以通过mem2chunk的方式,获取得到chunk的对象指针地址。因为当前chunk在使用中,所以可以获取得到mchunk_size的值了。

    由于mchunk_size字段的最后三个bit位,复用用作了(AMP)的标记位置。后三位bit位的复用,不会影响size的数据大小。所以直接取mchunk_size的时候是带上了AMP的标记数据。通过chunksize的方式,可以获得真正的chunk size数据。

    1. #include
    2. #include
    3. #include
    4. #include
    5. struct malloc_chunk {
    6. size_t mchunk_prev_size; /* Size of previous chunk (if free). */
    7. size_t mchunk_size; /* 当前chunk的大小 Size in bytes, including overhead. */
    8. struct malloc_chunk* fd; /* double links -- used only if free. */
    9. struct malloc_chunk* bk;
    10. struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
    11. struct malloc_chunk* bk_nextsize;
    12. };
    13. typedef struct malloc_chunk* mchunkptr;
    14. #define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*sizeof(size_t)));
    15. #define chunk2mem(p) ((void*)((char*)(p) + 2*sizeof(size_t)));
    16. #define chunksize_nomask(p) ((p)->mchunk_size)
    17. #define chunksize(p) (chunksize_nomask (p) & ~(0x1|0x2|0x4))
    18. int main() {
    19. char *s = NULL;
    20. s = (char *) malloc (23 * sizeof(char));
    21. char *p = NULL;
    22. p = (char *) malloc (25 * sizeof(char));
    23. char *p1 = NULL;
    24. p1 = (char *) malloc (110 * sizeof(char));
    25. printf("s : %d\r\n", s);
    26. printf("p : %d\r\n", p);
    27. printf("p1 : %d\r\n", p1);
    28. printf("size:%d\r\n", sizeof(mchunkptr));
    29. mchunkptr pr = mem2chunk(p);
    30. printf("p chunk_size value:%d\r\n", pr->mchunk_size);
    31. printf("p chunk size:%d\r\n", chunksize(pr));
    32. }

    结果:

    1. $./main
    2. s : 6299664
    3. p : 6299696
    4. p1 : 6299744
    5. size:8
    6. p chunk_size value:49
    7. p chunk size:48

    三、小内存块尝试获取fd信息的实验


    我们分配一组小内存块,可以尝试将部分内存直接free。小内存块分配是是落在fastbin上的,这些内存块没有经过合并整理的操作,所以我们可以尝试从已经被free的chunk中获取得到一些信息,例如chunk->fd指针信息等。

    1. #include
    2. #include
    3. #include
    4. #include
    5. struct malloc_chunk {
    6. size_t mchunk_prev_size; /* Size of previous chunk (if free). */
    7. size_t mchunk_size; /* 当前chunk的大小 Size in bytes, including overhead. */
    8. struct malloc_chunk* fd; /* double links -- used only if free. */
    9. struct malloc_chunk* bk;
    10. struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
    11. struct malloc_chunk* bk_nextsize;
    12. };
    13. typedef struct malloc_chunk* mchunkptr;
    14. #define mem2chunk(mem) ((mchunkptr)((char*)(mem) - 2*sizeof(size_t)));
    15. #define chunk2mem(p) ((void*)((char*)(p) + 2*sizeof(size_t)));
    16. #define chunksize_nomask(p) ((p)->mchunk_size)
    17. #define chunksize(p) (chunksize_nomask (p) & ~(0x1|0x2|0x4))
    18. #define MALLOC_ALIGN_MASK (MALLOC_ALIGNMENT - 1)
    19. #define request2size(req) (((req) + 8 + 15 < 32) ? 32 : ((req) + 8 + 15) & ~15)
    20. int main() {
    21. malloc(20*sizeof(char));
    22. char *a1 =(char *) malloc(26*sizeof(char));
    23. char *a2 =(char *) malloc(27*sizeof(char));
    24. char *a3 =(char *) malloc (26 * sizeof(char));
    25. malloc(20*sizeof(char));
    26. /* 获取内存分配地址 */
    27. printf("a1 : %d\r\n", a1);
    28. printf("a2 : %d\r\n", a2);
    29. printf("a3 : %d\r\n", a3);
    30. /* 获取chunk 指针 */
    31. mchunkptr a1pr = mem2chunk(a1);
    32. mchunkptr a2pr = mem2chunk(a2);
    33. mchunkptr a3pr = mem2chunk(a3);
    34. printf("a1 chunk:%d\r\n", a1pr);
    35. printf("a2 chunk:%d\r\n", a2pr);
    36. printf("a3 chunk:%d\r\n", a3pr);
    37. /* 获取a2的chunk数据 */
    38. printf("p chunk_size value:%d\r\n", a2pr->mchunk_size);
    39. printf("p chunk size:%d\r\n", chunksize(a2pr));
    40. /* 执行free操作 */
    41. free(a1);
    42. free(a2);
    43. free(a3);
    44. printf("free a1 mchunk_size value:%d\r\n", a1pr->mchunk_size); //获取free掉的chunk的size,在fastbin上
    45. printf("free a2 fd value:%d\r\n", a2pr->fd);
    46. printf("free a3 fd value:%d\r\n", a3pr->fd);
    47. }
    1. $./main
    2. a1 : 6299696
    3. a2 : 6299744
    4. a3 : 6299792
    5. a1 chunk:6299680
    6. a2 chunk:6299728
    7. a3 chunk:6299776
    8. p chunk_size value:49
    9. p chunk size:48
    10. free a1 mchunk_size value:49
    11. free a2 fd value:6299680 //指向A1
    12. free a3 fd value:6299728 //指向A2

    四、常用malloc/free函数使用注意事项


    1. 后分配的内存先释放,因为ptmalloc收缩内存是从top chunk开始,如果与top chunk相邻的chunk不能释放,top chunk以下的chunk都无法释放。
    2. Ptmalloc不适合用于管理长生命周期的内存,特别是持续不定期分配和释放长生命周期的内存,这将导致ptmalloc内存暴增。
    3. 多线程分阶段执行的程序不适合用ptmalloc,这种程序的内存更适合用内存池管理
    4. 尽量减少程序的线程数量和避免频繁分配/释放内存。频繁分配,会导致锁的竞争,最终导致非主分配区增加,内存碎片增高,并且性能降低。
    5. 防止内存泄露,ptmalloc对内存泄露是相当敏感的,根据它的内存收缩机制,如果与top chunk相邻的那个chunk没有回收,将导致top chunk一下很多的空闲内存都无法返回给操作系统。
    6. 防止程序分配过多内存,或是由于Glibc内存暴增,导致系统内存耗尽,程序因OOM被系统杀掉。预估程序可以使用的最大物理内存大小,配置系统的/proc/sys/vm/overcommit_memory,/proc/sys/vm/overcommit_ratio,以及使用ulimt –v限制程序能使用虚拟内存空间大小,防止程序因OOM被杀掉。
  • 相关阅读:
    使用mysql语句操作数据库
    Linux 搭建Owncloud 私有云
    SnakeYaml的不出网反序列化利用分析
    CSS魔法!如何将任意CSS类型转换为数值?
    C++【继承】
    golang context原理
    Docker consul的容器服务更新与发现
    基于GAN的时序缺失数据填补前言(1)——RNN介绍及pytorch代码实现
    化工企业ERP对可回收包装物的管理
    C语言 | Leetcode C语言题解之第141题环形链表
  • 原文地址:https://blog.csdn.net/initphp/article/details/132971717