完整教程:深入剖析 Chrome PartitionAlloc 内存池源码原理与性能调优实践

news/2025/9/22 13:54:23/文章来源:https://www.cnblogs.com/wzzkaifa/p/19105125

一、前言

在现代浏览器中,内存分配的性能和稳定性直接影响整体用户体验。Chromium/Chrome 作为世界上最复杂的 C++ 应用之一,为了高效、安全地管理内存,自研了一套高性能的内存池机制——PartitionAlloc

PartitionAlloc 在 Chrome 中几乎无处不在:DOM 节点的分配、V8 堆外内存、IPC 消息缓冲、UI 渲染对象,都可能通过它来完成。与传统 malloc / free 相比,它更强调:

  • 内存安全:结合 BackupRefPtr (BRP)、Pointer Scanning (PCScan) 等机制,降低 UAF 风险。

  • 性能优化:引入线程缓存(ThreadCache)、批量分配(FillBucket)、快速路径分配,减少锁争用和系统调用。

  • 碎片控制:通过 SlotSpan / Bucket / SuperPage 的层次化管理,降低内存浪费。

  • 跨平台一致性:统一抽象在 Windows、Linux、macOS 上的虚拟内存分配。

这篇文章将从源码视角深入解析 Chrome PartitionAlloc 内存池的机制,并结合实际工程调优经验,给出一份“从架构到实战”的完整指南。


二、PartitionAlloc 内存池架构解析

PartitionAlloc 的整体设计分为三层:

  1. SuperPage (2MB)

    • 内存分配的基本单位,每次至少申请 2MB。

    • SuperPage 的首尾部分保留作为保护页(guard page)。

    • 在中间区域,划分多个 SlotSpan

  2. SlotSpan (由多个系统页组成)

    • 每个 SlotSpan 管理一组等大小的对象(slot)。

    • SlotSpan 的元数据(metadata)记录 freelist、slot 数量等信息。

  3. Bucket (桶)

    • 每个 bucket 管理相同大小的 slot。

    • 典型分布:8B、16B、32B…到 256KB。

    • 大于 256KB 的分配采用 DirectMap,直接映射单独的大块内存。

关键常量定义

constexpr size_t PartitionPageSize = 16 * 1024; // 16KB constexpr size_t kSuperPageSize = 2 * 1024 * 1024; // 2MB constexpr size_t SystemPageSize = 4 * 1024; // 4KB
  • PartitionPageSize (16KB):内部元数据和 SlotSpan 的最小粒度。

  • kSuperPageSize (2MB):每次分配的大块内存。

  • SystemPageSize (4KB):操作系统页面大小。


三、源码关键类说明

PartitionAlloc 的核心代码位于 base/allocator/partition_allocator/,几个重要类:

1. PartitionRoot

  • 整个内存池的入口类。

  • 负责初始化 bucket、管理 SuperPage。

  • 接口:

    • AllocFromBucket():分配对象。

    • Free():释放对象。

2. PartitionBucket

  • 管理一组固定大小的对象 slot。

  • 内部维护活跃 SlotSpan 链表:

    • active_slot_spans_head

    • empty_slot_spans_head

    • decommitted_slot_spans_head

  • 快速路径:直接从 freelist 取内存。

  • 慢速路径:触发新建 SlotSpan 或 SuperPage。

3. SlotSpanMetadata

  • 管理 SlotSpan 的元信息。

  • 字段:

    • freelist_head:指向空闲 slot。

    • num_allocated_slots:当前分配数量。

    • num_unprovisioned_slots:剩余可分配 slot 数。

4. ThreadCache

  • 线程本地缓存,减少锁争用。

  • 接口:

    • GetFromCache():快速分配。

    • MaybePutInCache():回收时加入缓存。

    • RunPeriodicPurge():周期清理(默认 2s)。

5. PartitionDirectMap

  • 处理大于 256KB 的分配,直接 mmap/VirtualAlloc

  • 分配粒度对齐到系统页。


四、SuperPage / SlotSpan / FreeList 图示

下面给出一个结构示意图,展示 SuperPage → SlotSpan → Slot → FreeList 的关系:

+-----------------------------------------------------------+ | SuperPage (2MB) | | | | [Guard Page 16KB] | | +-------------------+ | | | Metadata Area |  freelist_head → slot_3 → slot_7 ... | | | +---------+ | | | | slot_2 | | | | +---------+ | | +-------------------+ | | | SlotSpan #2 | [slots of size 128B] | | +-------------------+ | | | | [Guard Page 16KB] | +-----------------------------------------------------------+
  • freelist_head 存储在 SlotSpanMetadata 内部。

  • 每个空闲 slot 的起始字节存储下一个空闲 slot 的地址(单链表)。

  • 内存布局高度紧凑,减少碎片。


五、内存分配流程

Chrome 的分配流程可以分为三步:

  1. 调整大小

    • 向上对齐到对应 bucket 的 slot 大小。

    • 预留空间存放 cookie、guard byte。

  2. 分配

    • 线程缓存ThreadCache::GetFromCache()

    • 分配器快速路径PartitionRoot::AllocFromBucket(),直接 freelist 弹出。

    • 慢速路径PartitionBucket::SlowPathAlloc()

  3. 初始化

    • 如有需要,零初始化。

    • 调试模式下写入 cookie。

源码片段:

void* PartitionRoot::Alloc(size_t size) { PartitionBucket* bucket = SizeToBucket(size); SlotSpanMetadata* slot_span = bucket->active_slot_spans_head; if (slot_span && slot_span->freelist_head) { return slot_span->freelist_head->Pop(); } return SlowPathAlloc(bucket, size); }

六、内存释放机制

释放同样分为 线程缓存 → 普通释放 → 慢速路径 → 系统回收

  1. 线程缓存

    • ThreadCache::MaybePutInCache()

    • 如果缓存已满,批量返还给 PartitionRoot。

  2. 普通释放

    • 插入 SlotSpan 的 freelist 头部。

    • 如果 Span 曾经标记为满,则重新挂回活跃列表。

  3. 慢速路径

    • 如果 Span 全部释放,迁入 empty_list。

    • 可能触发 Decommit,释放物理内存。

    • 对大块内存(DirectMap)直接 munmap / VirtualFree

  4. 定期释放

    • MemoryReclaimer::Reclaim(),默认 4s 周期运行。

    • 释放未使用的系统页,降低 RSS。


七、性能调优方向

在工程落地时,可以从以下几个方面优化:

  1. 线程缓存大小

    • 过大:浪费内存。

    • 过小:频繁 fallback,增加锁开销。

    • 调整策略:通过 ThreadCache::SetThreadCacheSizeLimits() 调优。

  2. 延迟回收策略

    • 默认 4s 的 MemoryReclaimer 可以缩短或延长。

    • 高频分配/释放场景适合更快周期。

  3. 大对象分配 DirectMap

    • 对于 >256KB 的 DirectMap,避免过度分配。

    • 可通过分配对齐策略减少内部碎片。

  4. BRP 与 PCScan 的选择

    • BRP (BackupRefPtr):运行时检测 UAF,增加内存开销。

    • PCScan (Pointer Scanning):周期扫描指针,适合大内存场景。

    • 可在 GN args 中按需启用:

      use_backup_ref_ptr = true enable_pointer_compression = true enable_pcscan = false
  5. 统计与监控

    • 启用 tracing,观察 MemoryInfra.PartitionAlloc

    • 调用 allocator hooks,收集实时分配情况。


八、实战步骤

1. 本地构建带 BRP 的 Chromium

gn gen out/brp --args="is_debug=false use_backup_ref_ptr=true" ninja -C out/brp chrome

2. 启用 PCScan

gn gen out/pcscan --args="enable_pcscan=true"

3. 收集运行时统计

  • 方式 1:Tracing
    打开 about://tracing,勾选 MemoryInfraPartitionAlloc

  • 方式 2:Histogram
    在代码中添加:

    UMA_HISTOGRAM_COUNTS_1000("PartitionAlloc.AllocSize", size);
  • 方式 3:Allocator hooks

    base::allocator::SetHooks(&my_hooks);

九、工程落地建议

如果你希望将 PartitionAlloc 引入到非浏览器项目(如嵌入式/服务端组件):

  1. 构建系统

    • GN: 保留 partition_alloc/partition_alloc.gni

    • CMake: 需要手动导入 .cc/.h 文件,并定义 PA_BUILDFLAG(...)

  2. 启用/禁用建议

    • 禁用 allocator_shim:避免和系统 malloc 冲突。

    • 禁用 sanitizers:ASan 下 PartitionAlloc 与系统 malloc 共存会冲突。

    • PGO/LTO:需要在 GN args 中设置 use_partition_alloc=true

  3. 兼容性

    • Windows: 使用 VirtualAlloc

    • Linux: 使用 mmap + madvise

    • macOS: 使用 mmap + VM tag。


十、常见问题 FAQ

Q1: 为什么分配了 1KB 内存,实际 RSS 增加了 16KB?

  • 因为最小粒度是 PartitionPage (16KB),小分配被批量管理。

Q2: 为什么大于 256KB 的对象不走内存池?

  • 设计选择,避免巨型对象污染小对象池。

Q3: 如何判断某个对象是 DirectMap 分配?

  • 通过 PartitionAllocGetDirectMapMetadata(ptr)

Q4: ThreadCache 会导致内存泄漏吗?

  • 不会。ThreadCache 有周期清理机制,且在线程退出时自动回收。

Q5: 如何在生产环境观察内存池状态?

  • 开启 --enable-memory-infra,结合 Chrome 内置 DevTools → Performance → Memory。


十一、总结

通过 PartitionAlloc,Chromium 实现了一套跨平台、高性能、安全的内存池机制。

  • 架构层面:SuperPage → SlotSpan → Bucket → FreeList,层次分明,碎片可控。

  • 源码实现PartitionRoot 负责分配,ThreadCache 提升性能,MemoryReclaimer 负责回收。

  • 调优方向:可通过缓存大小、回收周期、DirectMap 策略优化性能。

  • 工程落地:不仅适合浏览器,也可以用于服务端、嵌入式系统,提供稳定高效的内存分配。

未来,随着 Chrome 在 隐私沙盒、性能优化、内存安全 方面的持续投入,PartitionAlloc 也会不断演进,例如更智能的垃圾回收、更轻量的指针保护。

对于开发者而言,理解 PartitionAlloc 的原理和源码实现,不仅能帮助我们优化浏览器,还能将其经验迁移到其他高性能项目中。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/909370.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何构建embeding 的就是pytorch 中

如何构建embeding 的就是pytorch 中pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco…

萤石设备视频接入平台EasyCVR国标GB28181视频平台整合铁路抑尘喷洒智能视频监控方案

萤石设备视频接入平台EasyCVR国标GB28181视频平台整合铁路抑尘喷洒智能视频监控方案一、建设背景与需求分析 铁路运输量的增长带来了粉尘污染的挑战。为了保障铁路运输的安全与环保,铁道部出台了《铁路煤炭运输抑尘技…

【低代码平台之应用构建展示】数智化贸易订单管理平台

贸易行业的企业,作为连接生产企业与客户的桥梁,业务管理往往面对诸多挑战:订单数量庞大,供货品种繁杂;供应商数量众多,分布范围广泛,管理难度大;结算方式多样,对账工作繁重,容易出错等等。这些问题会随着业务…

从零到Offer:Java Socket面试通关秘籍-Socket面试为何总让人“心跳加速”? - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

C# 第 17天 028 029接口,依赖反转,单元测试

胖接口:多要显示接口实现(C#语言独有的功能)

详细介绍:Linux驱动开发笔记(七)——并发与竞争(下)——自旋锁信号量互斥体

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025年项目管理软件革命:AI与空间计算如何重塑企业协作范式

2025年项目管理软件革命:AI与空间计算如何重塑企业协作范式 全球项目管理软件市场正在经历一场由多重技术叠加驱动的深度变革。根据Gartner最新预测,到2025年,75%的企业将采用至少一种具备AI决策能力的项目管理工具…

Threading 串行VS并发

Threading 串行VS并发 IO 密集型 结果 >>> 串行开始... 下载https://www.baidu.com/成功,状态码为200 下载https://www.sina.com.cn/成功,状态码为200 下载https://www.bilibili.com/成功,状态码为412 耗时…

parallel index

select * from tablename tb为提高查询效率,可使用一下方法:(1)并行查询:/*+ parallel(tb,32) * / select /*+ parallel(tb,32) */ count(*) from leo1 tb; (2)强制走索引:/*+ index(tb, indexname) */ …

C语言 第三讲:分支和循环(上) - 教程

C语言 第三讲:分支和循环(上) - 教程pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mo…

群晖安装套件,套件版本与群晖版本不兼容;

群晖安装套件,套件版本与群晖版本不兼容;以SurveillanceStation9.1.2为例 若遇到最新版群晖系统提示“与您的Synology NAS不兼容,请上传9.2.1-11320或以上版本”导致无法安装,请执行以下代码,通过SSH或者计划任务…

中间件专题:Redis

1. Redis 数据结构 # String set `key` `value` setnx `key` `value` # 不存在才set setex `key` `value` `ttl` incrby `key` `increment` # 自增# Hash 哈希表 hset `key` `field` `value` `field` `value` hget `ke…

Vue3 新趋势:弃用 ECharts!最强图表库诞生!

Vue3 新趋势:弃用 ECharts!最强图表库诞生!原文链接:https://mp.weixin.qq.com/s/hE7XmPjSOGpD6EYQCKSpkw在前端开发领域,数据可视化已然成为不可或缺的一环。 目前市面上主流的图表库诸如 ECharts、AntV 等,虽然…

群晖安装套件跳过版本检查

群晖安装套件跳过版本检查若遇到最新版群晖系统提示“10854与您的Synology NAS不兼容,请上传9.2.1-11320或以上版本”导致无法安装,请执行以下代码,通过SSH或者计划任务执行均可: cp /etc.defaults/synopackagesli…

负载排查和分析四

负载排查和分析四明白,我给你提供完整整合后的最终文档版本,把软/硬中断触发机制、背景、流程、表格全部补充进去,保持原有网络优化、Perf 分析、脚本和流程图不变。Linux 网络优化与性能分析完全指南(最终完整版)…

微信个人号开发API/文档/教程

微信个人号开发API/文档/教程 大家一般需求点无非是以下几个需求: 1.开发个人微信营销系统 2.开发自定义的微信机器人, 3.开发微信智能聊天客服系统 4.定制行业内的群数据分析 功能需求很简单,业务代码贼好撸,但是如…

微指令控制器基本原理

微指令控制器的基本原理 微命令和微操作一一对应。一个微命令对于一根输出控制线 相容性微命令:可以并行完成的微命令 互斥型微命令:不可以并行完成的微命令 微命令格式 水平型微命令 一条微命令可定义多个可并行的微…

一个拒绝过度设计的 .NET 快速开发框架:开箱即用,专注干活

前言 .NET 生态快速发展的背景下,越来越多开发希望找到一个既能快速上手,又不过度设计的后端框架。尤其是在中小型项目中,复杂的架构、层层封装的服务逻辑往往让开发效率大打折扣。 今天推荐一个轻量级、高效实用的…

lookup-mehtod和replace-method标签的作用

lookup-mehtod和replace-method标签的作用下面通过一个基于Spring框架的简单示例来解释 lookup - method和 replace - method的作用。 1. 项目准备 首先创建一个Maven项目,添加Spring相关的依赖: <dependencies&g…

个人微信号二次开发API调用、微信API接口

个人微信号二次开发API调用、微信API接口微信API接口、微信二次开发API调用微信协议接口调用-加微信好友及通过好友请求发送小程序 请求URL: http://域名地址/sendApplets 请求方式: POST 请求头Headers: Content-T…