Protocol Buffers 提供了高效的内存管理机制,可以在保持简单 API 的同时优化性能和资源使用。本页面将介绍 Protocol Buffers 如何管理内存,重点关注 Arena 分配系统、字段内存布局以及库中使用的优化策略。
Arena 分配系统是 Protocol Buffers 内存管理的核心,它通过从预分配的内存块中分配对象并允许一次性释放所有对象,从而带来显著的性能优势。
Arena 系统主要包含三个组件:
Arena:提供分配方法的公共 API 类。ThreadSafeArena:提供线程安全性的内部实现。SerialArena:管理内存块的内部每个线程的分配器。创建 Arena 对象时,它会在内部创建一个 ThreadSafeArena,该 ThreadSafeArena 管理一个或多个 SerialArena 实例(每个访问 Arena 的线程一个)。每个 SerialArena 以递增大小的块分配内存,从一个小的初始块开始,并增长到可配置的最大块大小。
在 Arena 上创建对象
Arena::Create<T>():在 Arena 上创建类型为 T 的对象。Arena::CreateMessage<T>():在 Arena 上创建类型为 T 的消息。Arena::Own():将现有对象的拥有权转移给 Arena。来源:src/google/protobuf/arena.h156-773 src/google/protobuf/arena.h38-105 src/google/protobuf/generated_message_reflection.cc95-102
下图展示了 Protocol Buffers 代码实体如何映射到内存管理概念。
此映射显示了公共 API 与管理内存的内部实现类之间的关系。
来源:src/google/protobuf/arena.h166-195 src/google/protobuf/serial_arena.h src/google/protobuf/thread_safe_arena.h
Protocol Buffers 使用专门的容器来处理重复字段,并针对内存使用和性能进行了优化。
处理重复字段的两个主要类:
RepeatedField<T>:用于基本类型(int32、float 等)。RepeatedPtrField<T>:用于消息类型和字符串。对于小的重复字段,Protocol Buffers 使用小型对象优化来避免堆分配。
SOO 优化:
RepeatedField 对象中。当重复字段需要增长时:
增长策略旨在平衡:
来源:src/google/protobuf/repeated_field.h237-866 src/google/protobuf/repeated_ptr_field.h137-902
Protocol Buffers 提供了多种字符串存储策略,以优化不同场景。
| 类 | 最佳用途 | 内存特性 | 权衡 |
|---|---|---|---|
| ArenaStringPtr | 通用用法 | 字符串指针 | 简单,灵活 |
| InlinedStringField | 小型字符串 | 字符串内容内联到消息中 | 为小型字符串避免间接引用 |
| MicroString | 极小型字符串 | 高度优化的字符串 | 对于小于 15 字节的字符串速度最快 |
| absl::Cord | 大型字符串 | 树状结构 | 对大型字符串高效,对小型字符串开销较大 |
ArenaStringPtr 是标准的字符串表示。
InlinedStringField 将小型字符串直接存储在消息中。
来源:src/google/protobuf/arenastring.h src/google/protobuf/inlined_string_field.h src/google/protobuf/micro_string.h
Protocol Buffers 消息具有基于字段类型和选项的优化内存布局。
Protocol Buffers 使用位字段(_has_bits_)来高效跟踪哪些可选字段已被设置。
_has_bits_ 数组中占有一位。| 字段类型 | 存储 | 内存模式 |
|---|---|---|
| 标量(int32、bool) | 直接 | 内联存储在消息对象中。 |
| 字符串 | 间接 | 指向字符串内容(带优化)。 |
| 消息 | 间接 | 指向子消息对象。 |
| 重复 | 间接 | 指向 RepeatedField 或 RepeatedPtrField。 |
| 枚举(Enum) | 直接 | 存储为整数值。 |
| Map | 间接 | 指向专门的哈希映射实现。 |
对于可能不会被访问的大型嵌套消息,Protocol Buffers 支持延迟解析。
来源:src/google/protobuf/generated_message_reflection.cc392-547 src/google/protobuf/message.h241-411 src/google/protobuf/message_lite.h44-958
Extensions 允许在不修改消息定义的情况下向消息添加字段,这需要特殊的内存管理考虑。
Extensions 存储在 ExtensionSet 结构中。
主要特性
Extension 内存通过以下方式优化:
来源:src/google/protobuf/extension_set.h177-210 src/google/protobuf/extension_set.cc101-146
Protocol Buffers 消息设计为与 Arena 分配系统高效协同工作。
Protocol Buffers 使用特性来确定如何分配和释放对象。
这些特性支持:
生成的消息通过以下方式与 Arena 集成:
来源: src/google/protobuf/arena.h196-226 src/google/protobuf/message_lite.h236-280
除了 Arena 之外,Protocol Buffers 还采用了多种内存优化策略。
每个消息都维护一个缓存的大小,以避免重复计算序列化大小。
这项优化
内存高效的解析技术包括:
通过以下方式优化扩展:
来源: src/google/protobuf/message_lite.cc175-243 src/google/protobuf/parse_context.h66-102
为了优化 Protocol Buffers 的内存使用:
使用 Arena 分配:处理大量消息时,使用 Arena 分配可以降低分配开销。
考虑字符串存储选项:对于包含许多小字符串的消息,请考虑字符串内联或 MicroString 选项。
批量处理:使用单个 Arena 批量处理消息,以提高局部性并减少碎片。
使用移动语义:在适当的情况下,使用移动语义以避免复制。
监控内存使用:使用 SpaceUsed() 方法来跟踪内存消耗。
来源: src/google/protobuf/arena.h282-290 src/google/protobuf/message.h314-327
刷新此 Wiki
最后索引时间2025 年 4 月 18 日(2d6278)