Glibc内存管理--ptmalloc2源代码分析(十四)

.2.2  Large bins

SIZE_SZ4B 的平台上,大于等于 512B 的空闲 chunk ,或者,在 SIZE_SZ8B 的平台上,大小大于等于 1024B 的空闲 chunk ,由 sorted bins 管理。 Large bins 一共包括 63bin ,每个 bin 中的 chunk 大小不是一个固定公差的等差数列,而是分成 6bin ,每组 bin 是一个固定公差的等差数列,每组的 bin 数量依次为 32168421 ,公差依次为 64B512B4096B32768B262144B 等。

SIZE_SZ4B 的平台为例,第一个 large bin 的起始 chunk 大小为 512B ,共 32bin ,公差为 64B ,等差数列满足如下关系:

Chunk_size=512 + 64 * index

第二个 large bin 的起始 chunk 大小为第一组 bin 的结束 chunk 大小,满足如下关系:

Chunk_size=512 + 64 * 32 + 512 * index

同理,我们可计算出每个 bin 的起始 chunk 大小和结束 chunk 大小。这些 bin 都是很有规律的,其实 small bins 也是满足类似规律, small bins 可以看着是公差为 8 的等差数列,一共有 64bin (第 01bin 不存在),所以我们可以将 small binslarge bins 存放在同一个包含 128chunk 的数组上,数组的前一部分位 small bins ,后一部分为 large bins ,每个 binindexchunk 数组的下标,于是,我们可以根据数组下标计算出该 binchunk 大小( small bins )或是 chunk 大小范围( large bins ),也可以根据需要分配内存块大小计算出所需 chunk 所属 binindexptmalloc 使用了一组宏巧妙的实现了这种计算。

#define NBINS             128
#define NSMALLBINS         64
#define SMALLBIN_WIDTH    MALLOC_ALIGNMENT
#define MIN_LARGE_SIZE    (NSMALLBINS * SMALLBIN_WIDTH)

#define in_smallbin_range(sz)  \
  ((unsigned long)(sz) < (unsigned long)MIN_LARGE_SIZE)

#define smallbin_index(sz) \
  (SMALLBIN_WIDTH == 16 ? (((unsigned)(sz)) >> 4) : (((unsigned)(sz)) >> 3))

#define largebin_index_32(sz)                                                \
(((((unsigned long)(sz)) >>  6) <= 38)?  56 + (((unsigned long)(sz)) >>  6): \
 ((((unsigned long)(sz)) >>  9) <= 20)?  91 + (((unsigned long)(sz)) >>  9): \
 ((((unsigned long)(sz)) >> 12) <= 10)? 110 + (((unsigned long)(sz)) >> 12): \
 ((((unsigned long)(sz)) >> 15) <=  4)? 119 + (((unsigned long)(sz)) >> 15): \
 ((((unsigned long)(sz)) >> 18) <=  2)? 124 + (((unsigned long)(sz)) >> 18): \
                                        126)

// XXX It remains to be seen whether it is good to keep the widths of
// XXX the buckets the same or whether it should be scaled by a factor
// XXX of two as well.
#define largebin_index_64(sz)                                                \
(((((unsigned long)(sz)) >>  6) <= 48)?  48 + (((unsigned long)(sz)) >>  6): \
 ((((unsigned long)(sz)) >>  9) <= 20)?  91 + (((unsigned long)(sz)) >>  9): \
 ((((unsigned long)(sz)) >> 12) <= 10)? 110 + (((unsigned long)(sz)) >> 12): \
 ((((unsigned long)(sz)) >> 15) <=  4)? 119 + (((unsigned long)(sz)) >> 15): \
 ((((unsigned long)(sz)) >> 18) <=  2)? 124 + (((unsigned long)(sz)) >> 18): \
                                        126)

#define largebin_index(sz) \
  (SIZE_SZ == 8 ? largebin_index_64 (sz) : largebin_index_32 (sz))

#define bin_index(sz) \
 ((in_smallbin_range(sz)) ? smallbin_index(sz) : largebin_index(sz))

      宏 bin_index(sz) 根据所需内存大小计算出所需 bin 的 index ,如果所需内存大小属于 small bins 的大小范围,调用 smallbin_index(sz) ,否则调用 largebin_index(sz)) 。 smallbin_index(sz) 的计算相当简单,如果 SIZE_SZ 为 4B ,则将 sz 除以 8 ,如果 SIZE_SZ 为 8B ,则将 sz 除以 16 ,也就是除以 small bins 中等差数列的公差。 largebin_index(sz) 的计算相对复杂一些,可以用如下的表格直观的显示 chunk 的大小范围与 bin index 的关系。以 SIZE_SZ 为 4B 的平台为例, chunk 大小与 bin index 的对应关系如下表所示:(博客编辑器太弱,不能全部贴出来)

 

开始( 字节)

结束(字节)

Bin index

0

7

不存在

8

15

不存在

16

23

2

24

31

3

32

39

4

40

47

5

48

55

6

56

63

7

64

71

8

72

79

9

80

87

10

88

95

11

96

103

12

104

111

13

112

119

14

120

127

15

128

135

16

136

143

17

144

151

18

152

159

19

160

167

20

168

175

21

176

183

22

184

191

23

192

199

24

200

207

25

208

215

26

216

223

27

224

231

28

232

239

29

240

247

30

248

255

31

256

263

32

 

    注意:上表是 chunk 大小与 bin index 的对应关系,如果对于用户要分配的内存大小 size ,必须先使用 checked_request2size(req, sz) 计算出 chunk 的大小,再使用 bin_index(sz) 计算出 chunk 所属的 bin index

         对于 SIZE_SZ4B 的平台, bin[0]bin[1] 是不存在的,因为最小的 chunk16Bsmall bins 一共 62 个, large bins 一共 63 个,加起来一共 125bin 。而 NBINS 定义为 128 ,其实 bin[0]bin[127] 都不存在, bin[1]unsorted binchunk 链表头。

typedef struct malloc_chunk* mbinptr;

/* addressing -- note that bin_at(0) does not exist */
#define bin_at(m, i) \
  (mbinptr) (((char *) &((m)->bins[((i) - 1) * 2]))                           \
             - offsetof (struct malloc_chunk, fd))

/* analog of ++bin */
#define next_bin(b)  ((mbinptr)((char*)(b) + (sizeof(mchunkptr)<<1)))

/* Reminders about list directionality within bins */
#define first(b)     ((b)->fd)
#define last(b)      ((b)->bk)

/* Take a chunk off a bin list */
#define unlink(P, BK, FD) {                                            \
  FD = P->fd;                                                          \
  BK = P->bk;                                                          \
  if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                \
    malloc_printerr (check_action, "corrupted double-linked list", P); \
  else {                                                               \
    FD->bk = BK;                                                       \
    BK->fd = FD;                                                       \
    if (!in_smallbin_range (P->size)                                   \
        && __builtin_expect (P->fd_nextsize != NULL, 0)) {             \
      assert (P->fd_nextsize->bk_nextsize == P);                       \
      assert (P->bk_nextsize->fd_nextsize == P);                       \
      if (FD->fd_nextsize == NULL) {                                   \
        if (P->fd_nextsize == P)                                       \
          FD->fd_nextsize = FD->bk_nextsize = FD;                      \
        else {                                                         \
          FD->fd_nextsize = P->fd_nextsize;                            \
          FD->bk_nextsize = P->bk_nextsize;                            \
          P->fd_nextsize->bk_nextsize = FD;                            \
          P->bk_nextsize->fd_nextsize = FD;                            \
        }                                                              \
      } else {                                                         \
        P->fd_nextsize->bk_nextsize = P->bk_nextsize;                  \
        P->bk_nextsize->fd_nextsize = P->fd_nextsize;                  \
      }                                                                \
    }                                                                  \
  }                                                                    \
}

    宏 bin_at(m, i) 通过 bin index 获得 bin 的链表头, chunk 中的 fbbk 用于将空闲 chunk 链入链表中,而对于每个 bin 的链表头,只需要这两个域就可以了, prev_sizesize 对链表都来说都没有意义,浪费空间, ptmalloc 为了节约这点内存空间,增大 cpu 高速缓存的命中率,在 bins 数组中只为每个 bin 预留了两个指针的内存空间用于存放 bin 的链表头的 fbbk 指针。

         bin_at(m, i) 的定义可以看出, bin[0] 不存在,以 SIZE_SZ4B 的平台为例, bin[1] 的前 4B 存储的是指针 fb ,后 4B 存储的是指针 bk ,而 bin_at 返回的是 malloc_chunk 的指针,由于 fbmalloc_chunk 的偏移地址为 offsetof (struct malloc_chunk, fd))=8 ,所以用 fb 的地址减去 8 就得到 malloc_chunk 的地址。但切记,对 bin 的链表头的 chunk ,一定不能修改 prev_sizesize 域,这两个域是与其他 bin 的链表头的 fbbk 内存复用的。

     宏 next_bin(b) 用于获得下一个 bin 的地址,根据前面的分析,我们知道只需要将当前 bin 的地址向后移动两个指针的长度就得到下一个 bin 的链表头地址。

     每个 bin 使用双向循环链表管理空闲 chunkbin 的链表头的指针 fb 指向第一个可用的 chunk ,指针 bk 指向最后一个可用的 chunk 。宏 first(b) 用于获得 bin 的第一个可用 chunk ,宏 last(b) 用于获得 bin 的最后一个可用的 chunk ,这两个宏便于遍历 bin ,而跳过 bin 的链表头。

     宏 unlink(P, BK, FD) 用于将 chunk 从所在的空闲链表中取出来,注意 large bins 中的空闲 chunk 可能处于两个双向循环链表中, unlink 时需要从两个链表中都删除。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本文通过Glibc内存暴增问题,主要介绍了系统的内存管理问题,具体如下: 目录 1. 问题 2. 基础知识 2.1 X86平台Linux进程内存布局 2.1.1 32位模式下进程内存经典布局 2.1.2 32位模式下进程默认内存布局 2.1.3 64位模式下进程内存布局 2.2 操作系统内存分配的相关函数 2.2.1 Heap操作相关函数 2.2.2 Mmap映射区域操作相关函数 3. 概述 3.1 内存管理一般性描述 3.1.1 内存管理的方法 3.1.2 内存管理器的设计目标 3.1.3 常见C内存管理程序 3.2 Ptmalloc内存管理概述 3.2.1 简介 3.2.2 内存管理的设计假设 3.2.3 内存管理数据结构概述 3.2.4 内存分配概述 3.2.5 内存回收概述 3.2.6 配置选项概述 3.2.7 使用注意事项 4. 问题分析及解决 5. 源代码分析 5.1 边界标记法 5.2 分箱式内存管理 5.2.1 Small bins 5.2.2 Large bins 5.2.3 Unsorted bin 5.2.4 Fast bins 5.3 核心结构体分析 5.3.1 malloc_state 5.3.2 Malloc_par 5.3.3 分配区的初始化 5.4 配置选项 5.5 Ptmalloc的初始化 5.5.1 Ptmalloc未初始化时分配/释放内存 5.5.2 ptmalloc_init()函数 5.5.3 ptmalloc_lock_all(),ptmalloc_unlock_all(),ptmalloc_unlock_all2() 5.6 多分配区支持 5.6.1 Heap_info 5.6.2 获取分配区 5.6.3 Arena_get2() 5.6.4 _int_new_arena() 5.6.5 New_heap() 5.6.6 get_free_list()和reused_arena() 5.6.7 grow_heap(),shrink_heap(),delete_heap(),heap_trim() 5.7 内存分配malloc 5.7.1 public_mALLOc() 5.7.2 _int_malloc() 5.8 内存释放free 5.8.1 Public_fREe() 5.8.2 _int_free() 5.8.3 sYSTRIm()和munmap_chunk(
### 回答1: glibc是GNU计划的一部分,是一套C语言标准库。内存管理是其中的一个重要组件。而ptmalloc2是glibc内存管理的一种算法,用于分配和释放内存块。 要下载glibc内存管理ptmalloc2源代码,可以通过以下几个步骤进行: 1. 打开GNU官方网站,找到glibc的相关页面,通常在https://www.gnu.org/software/libc/ 。 2. 在该页面上,找到下载链接或源代码仓库地址,这个地址通常会提供给用户下载最新版本的glibc。 3. 点击下载链接或者复制源代码仓库地址,将其粘贴到浏览器地址栏中。 4. 打开该链接后,您将能够下载一个压缩文件(通常是tar.gz或tar.bz2格式),包含了glibc的全部源代码。 5. 下载完毕后,解压压缩文件。您可以使用解压软件,如WinRAR或7-Zip。解压缩后,您将获得一个包含许多目录和文件的文件夹。 6. 在解压后的文件夹中,找到与ptmalloc2相关的源代码文件。通常这些文件会位于glibc源代码的malloc目录下。 7. 在malloc目录中,您将能够找到ptmalloc2源代码文件,这些文件名通常以"ptmalloc"或"ptmalloc2"开头。 以上是下载glibc内存管理ptmalloc2源代码的一个大致过程。通过该源代码,您可以深入了解ptmalloc2算法是如何在glibc中实现内存分配和释放的。但是请注意,阅读和理解源代码需要一定的计算机编程经验和相关背景知识。 ### 回答2: glibc是Linux操作系统中非常重要的一个C标准库,ptmalloc2是glibc中负责内存管理的模块之一。该模块负责动态分配和释放内存,并提供了多种内存分配器算法。 ptmalloc2源代码分析是深入研究该模块源代码的过程。通过分析ptmalloc2源代码,可以了解到它的实现原理、内存分配算法以及性能优化等方面的细节。 在下载ptmalloc2源代码之后,我们可以通过阅读和分析源代码来了解其内部结构和工作原理。在源代码中,我们可以找到一些关键的数据结构和函数,如malloc、free、realloc等。这些函数实现了动态内存分配和释放的基本功能。 通过阅读源代码,我们可以学习到ptmalloc2内存管理器的特点和优势。例如,ptmalloc2采用了分离的空闲链表来管理不同大小的内存块,利用了空闲块合并和分割等技术来提高内存的利用率和性能。此外,源代码还可能包含一些与内存操作相关的底层函数和宏定义。 分析ptmalloc2源代码不仅可以帮助我们理解其内部实现,还可以为我们定位和解决内存管理相关的问题提供指导。如果遇到性能问题或者内存泄漏等现象,我们可以通过分析源代码来找到问题的根源,并提出相应的优化措施。 总之,通过对glibc内存管理模块ptmalloc2的源代码进行分析,我们可以深入了解其实现原理和内部机制,为我们在实际项目中正确、高效地使用内存管理功能提供帮助。 ### 回答3: glibc是Linux系统上使用最广泛的C语言函数库,而ptmalloc2则是glibc中负责内存分配和管理的部分源代码。 首先,需要明确的是,glibc的ptmalloc2源代码并不是一个独立的项目,而是glibc库中的一部分。如果需要下载该源代码,可以通过访问glibc的官方网站或者使用git等工具来获取。 分析glibc内存管理ptmalloc2源代码可以帮助开发者更好地理解和使用glibc内存分配功能。ptmalloc2实现了一种基于堆的内存分配算法,它采用了多种策略来管理内存,如bin和fastbin等。源代码分析可以帮助我们了解这些策略的具体实现细节,以及它们在不同场景下的行为。 要对ptmalloc2源代码进行分析,可以首先阅读相关文档,如glibc的官方文档或论文。 掌握ptmalloc2的整体架构、数据结构和算法等基本知识后,可以通过逐行或逐函数地阅读源代码来深入理解其内部工作机制。可以关注一些关键函数的实现,如malloc、free、realloc等,以及相关的数据结构和算法。 此外,还可以参考开源社区中对ptmalloc2源代码分析和解读,如一些博客文章、论文或代码注释等。这些资源通常提供了对源代码更深入的解释和讨论,对于理解ptmalloc2的实现细节会有所帮助。 总之,通过下载并分析glibc内存管理ptmalloc2源代码,可以帮助我们更好地理解和使用glibc库中的内存分配功能。同时,也可以通过分析源代码来提高我们的代码调试和性能优化能力,并为开发更高效的内存管理算法提供参考。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值