malloc heap_trim

ptmalloc2 中用于收缩非主 Arena 堆段的核心函数。当堆段顶部有大块空闲空间时,它尝试通过 shrink_heap 或 munmap 将多余的内存归还给操作系统,以降低进程的 RSS。

函数签名

static int heap_trim (heap_info *heap, size_t pad)

参数说明

参数含义
heap指向要收缩的堆段的 heap_info
pad收缩后至少保留的空闲空间(填充量)

返回值0 表示成功收缩,-1 表示无法收缩。

核心逻辑拆解

1. 获取 Arena 和 Top Chunk
mstate ar_ptr = heap->ar_ptr;
unsigned long pagesz = GLRO (dl_pagesize);
mchunkptr top_chunk = top (ar_ptr), p;
heap_info *prev_heap;
long new_size, top_size, top_pad, extra, prev_size, misalign;
  • ar_ptr:堆段所属的 Arena。

  • top_chunk:当前 Arena 的 Top Chunk 指针。

  • top_size:Top Chunk 的总大小。

2. 检查 Top Chunk 是否属于当前堆段
if (top_chunk != (mchunkptr) ((char *) heap + heap->size - CHUNK_HDR_SZ))
  return -1;

这是最关键的前置检查:Top Chunk 必须正好位于当前堆段的末尾

  • heap + heap->size - CHUNK_HDR_SZ 是堆段的最后一个 chunk 的起始位置。

  • 如果 Top Chunk 不在这里,说明堆段顶部还有活跃的 chunk,无法收缩,直接返回 -1

3. 计算 Top Chunk 的实际大小
top_size = chunksize (top_chunk);
4. 检查前一个堆段
if (heap->prev)
  prev_heap = heap->prev;
else
  prev_heap = NULL;

if (prev_heap != NULL) {
    /* 计算前一个堆段顶部是否有空闲空间 */
    ...
}

非主 Arena 的堆段通过 prev 指针构成链表。如果当前堆段不是第一个,可能需要检查前一个堆段的状态。

5. 计算可收缩的大小
new_size = (long) heap->size - (long) top_size;
new_size = ALIGN_DOWN (new_size, pagesz);
  • new_size = 堆段总大小 - Top Chunk 大小,再向下对齐到页边界。

  • 这是收缩后堆段的大小。

6. 执行收缩
if (new_size < HEAP_MIN_SIZE)
  new_size = HEAP_MIN_SIZE;

misalign = (char *) top_chunk - (char *) heap;
if (new_size < misalign)
  new_size = misalign;

if (new_size >= (long) heap->size)
  return -1;  // 没有可收缩的空间

if (shrink_heap (heap, new_size - (long) heap->size) != 0)
  return -1;
  • 确保 new_size 不小于 HEAP_MIN_SIZE

  • 确保 new_size 不小于 Top Chunk 的偏移量(misalign)。

  • 如果 new_size 不小于当前大小,说明没有可收缩的空间,返回 -1

  • 调用 shrink_heap 执行实际收缩。

7. 更新 Top Chunk 的大小
top_chunk = (mchunkptr) ((char *) heap + new_size - CHUNK_HDR_SZ);
set_head (top_chunk, (heap->size - new_size + top_size) | PREV_INUSE);
  • 收缩后,Top Chunk 的位置下移到新的堆段末尾。

  • 更新 Top Chunk 的 size 字段,使其覆盖收缩后剩余的空闲空间。

8. 更新 Arena 的 Top Chunk 指针
if (top_chunk != ar_ptr->top)
  ar_ptr->top = top_chunk;

如果 Top Chunk 位置变了,更新 Arena 的 top 指针。

完整流程

heap_trim(heap, pad):
    │
    ▼
1. 获取 Arena 和 Top Chunk
    │
    ▼
2. Top Chunk 在堆段末尾?
    ├── 否 → 返回 -1(无法收缩)
    └── 是 ↓
    │
    ▼
3. 计算 new_size = heap->size - top_size,向下对齐
    │
    ▼
4. 确保 new_size >= HEAP_MIN_SIZE 且 >= misalign
    │
    ▼
5. new_size >= heap->size?
    ├── 是 → 返回 -1(无空间可收缩)
    └── 否 ↓
    │
    ▼
6. 调用 shrink_heap(heap, new_size - heap->size)
    │
    ▼
7. 更新 Top Chunk 的位置和大小
    │
    ▼
8. 更新 Arena 的 top 指针
    │
    ▼
返回 0(成功)

与非主 Arena 堆段的关系

收缩前:
+------------------+------------------+------------------+
| heap_info        | 已分配 chunk     | Top Chunk        |
| + malloc_state   |                  | (大块空闲)       |
+------------------+------------------+------------------+
                                     ↑                  ↑
                                     top_chunk          堆段末尾

收缩后:
+------------------+------------------+------------------+
| heap_info        | 已分配 chunk     | Top Chunk        |
| + malloc_state   |                  | (缩小后的空闲)   |
+------------------+------------------+------------------+
                                     ↑                  ↑
                                     top_chunk          新堆段末尾
                                     (下移)

相关函数对比

函数作用系统调用
heap_trim收缩非主 Arena 堆段mprotect(通过 shrink_heap
systrim收缩主 Arena 的 brk 堆brk
shrink_heap收缩堆段的可写区域mprotect
grow_heap扩展堆段的可写区域mprotect

注意事项

  1. Top Chunk 必须在堆段末尾:这是收缩的前提条件。如果 Top Chunk 下方有活跃的 chunk,无法收缩。

  2. HEAP_MIN_SIZE 限制:堆段不能收缩到小于 HEAP_MIN_SIZE(通常 128KB)。

  3. pad 参数:调用者传入的填充量,确保收缩后仍保留足够的空闲空间,避免频繁扩展。

  4. 不解除 mmap 映射shrink_heap 只改变内存权限,虚拟地址空间仍然保留。

  5. 仅适用于非主 Arena:主 Arena 使用 systrim,不使用 heap_trim

总结

要点说明
功能收缩非主 Arena 堆段,将顶部空闲内存归还系统
前提条件Top Chunk 必须位于堆段末尾
核心操作调用 shrink_heap 收缩可写区域,更新 Top Chunk 位置和大小
限制不能小于 HEAP_MIN_SIZE,不能小于 Top Chunk 偏移量
返回值0 成功收缩,-1 无法收缩
与 systrim 区别heap_trim 用于非主 Arena,systrim 用于主 Arena

一句话理解

heap_trim 是 ptmalloc2 中“收缩非主 Arena 堆段”的函数。它检查 Top Chunk 是否位于堆段末尾,如果是,则计算可收缩的空间,调用 shrink_heap 将多余内存归还系统,并更新 Top Chunk 的位置和大小。它是非主 Arena 内存回收的核心机制,与主 Arena 的 systrim 对称。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值