ptmalloc(glibc-2.12.1)源码解析2

目录

一 struct_state(分配区)

源码10

二 malloc_par(管理统计分配区信息)

源码11

三 分配区的初始化

源码12

四 配置参数

源码 13


一 struct_state(分配区)

源码10

struct malloc_state {
 /* Serialize access. */
 mutex_t mutex;
 /* Flags (formerly in max_fast). */
 int flags;
#if THREAD_STATS
 /* Statistics for locking. Only used if THREAD_STATS is defined. */
 long stat_lock_direct, stat_lock_loop, stat_lock_wait;
#endif
 /* Fastbins */
 mfastbinptr fastbinsY[NFASTBINS];
 /* Base of the topmost chunk -- not otherwise kept in a bin */
 mchunkptr top;
 /* The remainder from the most recent split of a small request */
 mchunkptr last_remainder;
 /* Normal bins packed as described above */
 mchunkptr bins[NBINS * 2 - 2];
 /* Bitmap of bins */
45
 unsigned int binmap[BINMAPSIZE];
 /* Linked list */
 struct malloc_state *next;
#ifdef PER_THREAD
 /* Linked list for free arenas. */
 struct malloc_state *next_free;
#endif
 /* Memory allocated from the system in this arena. */
 INTERNAL_SIZE_T system_mem;
 INTERNAL_SIZE_T max_system_mem;
};

#define FASTCHUNKS_BIT (1U)
#define have_fastchunks(M) (((M)->flags & FASTCHUNKS_BIT) == 0)
#ifdef ATOMIC_FASTBINS
#define clear_fastchunks(M) catomic_or (&(M)->flags, FASTCHUNKS_BIT)
#define set_fastchunks(M) catomic_and (&(M)->flags, ~FASTCHUNKS_BIT)
#else
#define clear_fastchunks(M) ((M)->flags |= FASTCHUNKS_BIT)
#define set_fastchunks(M) ((M)->flags &= ~FASTCHUNKS_BIT)
#endif

#define NONCONTIGUOUS_BIT (2U)
#define contiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) == 0)
#define noncontiguous(M) (((M)->flags & NONCONTIGUOUS_BIT) != 0)
#define set_noncontiguous(M) ((M)->flags |= NONCONTIGUOUS_BIT)
#define set_contiguous(M) ((M)->flags &= ~NONCONTIGUOUS_BIT)

#define BINMAPSHIFT 5
#define BITSPERMAP (1U << BINMAPSHIFT)
#define BINMAPSIZE (NBINS / BITSPERMAP)
#define idx2block(i) ((i) >> BINMAPSHIFT)
#define idx2bit(i) ((1U << ((i) & ((1U << BINMAPSHIFT)-1))))
#define mark_bin(m,i) ((m)->binmap[idx2block(i)] |= idx2bit(i))
#define unmark_bin(m,i) ((m)->binmap[idx2block(i)] &= ~(idx2bit(i)))
#define get_binmap(m,i) ((m)->binmap[idx2block(i)] & idx2bit(i))

mutex:每个分配区都有一个独立的自适应锁,用来避免多线程情况下的线程安全问题。

flags:标识该分配区的一些标志位,bit 位为 0 -> 是否包含至少一个 fast bin chunk,bit 位为 1 -> 是否能返回连续的虚拟地址。

stat_lock_direct/stat_lock_loop/stat_lock_wait:表示线程的直接获取锁的数量/自选等待锁的数量/阻塞等待锁的数量。

fastbinsY[NFASTBINS]:fast bins 数组。

top:top chunk 堆顶指针。

last_remainder:small bins 分配出去的 chunk 可能切一部分分配出去,剩余的保留给这个字段为后续分配,缓解内存碎片。

bins[NBINS * 2 - 2]:bins 数组,前面说的逻辑结构是 128 个,实际物理是 128 * 2 - 2,前面说过一个 bin 的哨兵节点需要 2 个指针,这里把 2 个指针拆开放到下标里,也就是两两下标为一组。

unsorted bin 占用 2 个下标。

small bin 占用 62 * 2 个下标。

large bin 占用 63 * 2 个下标。

逻辑上的第 0 和最后一个下标无效不用包含。

总共需要的物理下标为:2 + 124 + 126 = 252 ,但定义的时候是 254 个是因为把逻辑上第 0 个下标也算进去了。

bitmap[BINMAPSIZE]:表示 bins 数组里对应的 bin 是否有空闲的 chunk。

next:分配区的指针,用来链接分配区供线程申请内存。

next_free:表示链接的是空闲的分配区,也就是该分配区没有被线程访问。

system_mem:表示该分配区已分配的内存大小。

man_system_mem:表示该分配区能申请的内存的最大大小。

FASTCHUNK_BIT:储存 flag bit0 的值,0 表示有 fast chunk,1 表示没有。

have_fastchunks(M):判断是否有 fast chunk。

ATOMIC_FASTBINS:表示下面 2 个宏是否是原子的,提供了 2 个版本。

clear_fastchunks(M):设置 flag bit0 为 1,表示没有 fast chunk。

set_fastchunks(M):设置 flag bit0 为 0,表示有 fast chunk。

  • 这里最开始会将 flag bit0 置为 0 表示有 fast chunk,随后会调用malloc_consolidate()来合并 fast bin 的所有 chunk 并放到其他的 bins 里面,随后将  flag bit0 置为 1 ,这里为什么不直接置为 1 ?可能有的情况下 flag bit0 不能置为1,如果该分配区是 calloc 出来的那么就是全部都是 0。

NONCONTIGUOUS_BIT:储存 flag bit1 的值。

contiguous(M):表示是否是连续地址空间。

noncontiguous(M):表示是否是非连续地址空间。

set_noncontiguous(M):设置为非连续地址空间。

set_contiguous(M):设置为连续地址空间,

BINMAPSHIFT:表示每个 int 管理的 bins 个数,2^5 -> 32 个。

BITSPERMAP:32。

BINMAPSIZE:表示 bitmap 大小,128 / 32 = 4。这里实际 bins 大小是 254,但两两为一组,只需要标识一个即可。

二 malloc_par(管理统计分配区信息)

源码11

struct malloc_par {
 /* Tunable parameters */
 unsigned long trim_threshold;
48
 INTERNAL_SIZE_T top_pad;
 INTERNAL_SIZE_T mmap_threshold;
#ifdef PER_THREAD
 INTERNAL_SIZE_T arena_test;
 INTERNAL_SIZE_T arena_max;
#endif
 /* Memory map support */
 int n_mmaps;
 int n_mmaps_max;
 int max_n_mmaps;
 /* the mmap_threshold is dynamic, until the user sets
 it manually, at which point we need to disable any
 dynamic behavior. */
 int no_dyn_threshold;
 /* Cache malloc_getpagesize */
 unsigned int pagesize;
 /* Statistics */
 INTERNAL_SIZE_T mmapped_mem;
 INTERNAL_SIZE_T max_mmapped_mem;
 INTERNAL_SIZE_T max_total_mem; /* only kept for NO_THREADS */
 /* First address handed out by MORECORE/sbrk. */
 char* sbrk_base;
};

trim_threshold:当 top chunk 大于这个阈值则会收缩堆顶指针,默认为 128K。

top_pad:在调用 sbrk 时额外多申请的内存,缓解可能频繁调用 sbrk 的开销。

mmap_threshold:申请的内存大小超过这个阈值则调用 mmap 进行分配,默认为 128K。

arena_test:现有的分配区的数量。

arena_max:最大分配区的数量。

n_mmaps:表示当前进程使用 mmap 分配的内存块的个数。

n_mmaps_max:表示当前进程调用 mmap 次数,默认为 65535 次。

max_n_mmaps:表示当前进程已经调用 mmap 且取最大值,不能超过 n_mmaps_max。

no_dyn_threshold:是否开启 mmap 分配阈值的动态调整。

pagesize:表示物理内存块的大小,一般为 4K。

mmaped_mem:当前进程调用 mmap 申请内存的总量累加。

max_mmaped_mem:记录 mmaped_mem 曾经申请内存的总大小的最大值。

max_total_mem:在单线程情况下,申请内存的总大小包括 sbrk mmap。

sbrk_base:堆的起始地址。

三 分配区的初始化

源码12

/* There are several instances of this struct ("arenas") in this
 malloc. If you are adapting this malloc in a way that does NOT use
 a static or mmapped malloc_state, you MUST explicitly zero-fill it
 before using. This malloc relies on the property that malloc_state
 is initialized to all zeroes (as is true of C statics). */
static struct malloc_state main_arena;
/* There is only one instance of the malloc parameters. */
static struct malloc_par mp_;
/* Maximum size of memory handled in fastbins. */
static INTERNAL_SIZE_T global_max_fast;
/*
 Initialize a malloc_state struct.
 This is called only from within malloc_consolidate, which needs
 be called in the same contexts anyway. It is never called directly
 outside of malloc_consolidate because some optimizing compilers try
 to inline it at all call points, which turns out not to be an
 optimization at all. (Inlining it in malloc_consolidate is fine though.)
*/
#if __STD_C
static void malloc_init_state(mstate av)
#else
static void malloc_init_state(av) mstate av;
#endif
{
 int i;
 mbinptr bin;
 /* Establish circular links for normal bins */
 for (i = 1; i < NBINS; ++i) {
 bin = bin_at(av,i);
 bin->fd = bin->bk = bin;
 }
#if MORECORE_CONTIGUOUS
 if (av != &main_arena)
#endif
 set_noncontiguous(av);
 if (av == &main_arena)
 set_max_fast(DEFAULT_MXFAST);
 av->flags |= FASTCHUNKS_BIT;
 av->top = initial_top(av);
}

static void
ptmalloc_init_minimal (void)
{
#if DEFAULT_TOP_PAD != 0
 mp_.top_pad = DEFAULT_TOP_PAD;
#endif
 mp_.n_mmaps_max = DEFAULT_MMAP_MAX;
 mp_.mmap_threshold = DEFAULT_MMAP_THRESHOLD;
 mp_.trim_threshold = DEFAULT_TRIM_THRESHOLD;
 mp_.pagesize = malloc_getpagesize;
#ifdef PER_THREAD
# define NARENAS_FROM_NCORES(n) ((n) * (sizeof(long) == 4 ? 2 : 8))
 mp_.arena_test = NARENAS_FROM_NCORES (1);
 narenas = 1;
51
#endif
}

main_arena:主分配区。

mp_:全局唯一实例,记录分配区的信息,如分配区个数,申请内存大小的总容量。

global_max_fast:表示 fast bins 最大的 chunk 大小。

static void malloc_init_state(mstate av):初始化分配区 area。

接下来就是初始化分配区里的 bins 数组,拿到每个下标的哨兵头节点,并初始化。

在下面就是判断是否能连续分配虚拟地址,如果不能则是非主分配区,则标记该分配区为不可分配连续虚拟地址,否则主分配区默认 brk 是连续的虚拟地址。

在下面就是如果该分配区是主分配区则初始化 global_max_fast 的大小,其他分配区共享该参数。

在下面就是设置该分配区允许使用 fast bins 分配小内存。

在下面就是初始化 top chunk 指针。

然后就是对 mp_ 也就是全局唯一实例对分配区的参数配置和统计内存使用情况等信息。

首先是初始化 top_pad sbrk 时额外分配的内存。

n_mmaps_max:初始化 mmap 调用次数。

trim_threshold:初始化堆顶指针的收缩阈值。

pagesize:初始化一页的大小。

在下面就是初始化分配区的个数。

四 配置参数

源码 13

#if __STD_C
int mALLOPt(int param_number, int value)
#else
int mALLOPt(param_number, value) int param_number; int value;
#endif
{
 mstate av = &main_arena;
 int res = 1;
 if(__malloc_initialized < 0)
 ptmalloc_init ();
 (void)mutex_lock(&av->mutex);
 /* Ensure initialization/consolidation */
 malloc_consolidate(av);
 switch(param_number) {
 case M_MXFAST:
 if (value >= 0 && value <= MAX_FAST_SIZE) {
 set_max_fast(value);
 }
 else
 res = 0;
 break;
 case M_TRIM_THRESHOLD:
 mp_.trim_threshold = value;
 mp_.no_dyn_threshold = 1;
 break;
 case M_TOP_PAD:
 mp_.top_pad = value;
 mp_.no_dyn_threshold = 1;
 break;
 case M_MMAP_THRESHOLD:
#if USE_ARENAS
 /* Forbid setting the threshold too high. */
 if((unsigned long)value > HEAP_MAX_SIZE/2)
 res = 0;
 else
#endif
 mp_.mmap_threshold = value;
 mp_.no_dyn_threshold = 1;
 break;
 case M_MMAP_MAX:
#if !HAVE_MMAP
 if (value != 0)
 res = 0;
 else
#endif
 mp_.n_mmaps_max = value;
 mp_.no_dyn_threshold = 1;
 break;
 case M_CHECK_ACTION:
 check_action = value;
 break;
 case M_PERTURB:
 perturb_byte = value;
 break;
#ifdef PER_THREAD
 case M_ARENA_TEST:
 if (value > 0)
 mp_.arena_test = value;
 break;
 case M_ARENA_MAX:
 if (value > 0)
 mp_.arena_max = value;
 break;
#endif
 }
 (void)mutex_unlock(&av->mutex);
 return res;
}

mALLOPt:函数入口。

av:获取主分配区。

res:返回值,默认是 1 成功。

__malloc_initialized :如果主分配区未初始化则调用函数初始化。

(void)mutex_lock(&av->mutex);:加锁保护。

malloc_consolidate(av):合并所有的相邻空闲的 chunk。

 case M_MXFAST:设置 fast bins chunk 的最大大小。

 case M_TRIM_THRESHOLD:设置堆顶的收缩阈值。

case M_TOP_PAD:设置 sbrk 额外分配的内存。

case M_MMAP_THRESHOLD:设置 mmap 的分配阈值。

case M_MMAP_MAX:设置 mmap 的调用次数。

case M_CHECK_ACTION:检测内存错误的处理行为。

case M_PERTURB:填充申请/是否的内存。

case M_ARENA_TEST:设置分配区的个数。

case M_ARENA_MAX:设置分配区的最大个数。

最后解锁返回。

本文通过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(
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值