跳到主要内容

内存模型

问题

Python 对象在内存中是如何布局的?小整数池和字符串驻留是怎么回事?Python 的内存分配器是怎样的?

答案

对象内存布局

每个 Python 对象至少包含:

┌────────────────────┐
│ ob_refcnt (8B) │ 引用计数
├────────────────────┤
│ ob_type (8B) │ 类型指针 → type 对象
├────────────────────┤
│ 对象数据... │ 具体类型的数据
└────────────────────┘
import sys

# 空对象的基本大小
print(sys.getsizeof(object())) # 16 bytes(refcnt + type)
print(sys.getsizeof(1)) # 28 bytes
print(sys.getsizeof("")) # 49 bytes
print(sys.getsizeof([])) # 56 bytes
print(sys.getsizeof({})) # 64 bytes

内存分配器层次

  • Block:固定大小的内存块(8 的倍数,8~512 bytes)
  • Pool:4KB,包含相同大小的 Block
  • Arena:256KB,包含多个 Pool

小对象(≤ 512 bytes)由 pymalloc 管理,大对象直接用系统 malloc。

小整数池与字符串驻留

# 小整数池:CPython 缓存 [-5, 256]
a = 256
b = 256
print(a is b) # True

a = 257
b = 257
print(a is b) # False(交互模式下;脚本中可能被编译器优化为 True)

# 字符串驻留:标识符类字符串自动驻留
a = "hello"
b = "hello"
print(a is b) # True

a = "hello world" # 含空格
b = "hello world"
print(a is b) # False(不自动驻留)

# 手动驻留
import sys
a = sys.intern("hello world")
b = sys.intern("hello world")
print(a is b) # True

常见面试问题

Q1: 为什么 Python 的 int 比 C 的 int 大这么多?

答案

Python 的 int 是对象,包含引用计数(8B)、类型指针(8B)、值长度(8B)和实际数值(至少 4B),所以最小 28 bytes。而 C 的 int 只有 4 bytes。这是动态类型语言的代价。

Q2: 如何测量 Python 对象的内存占用?

答案

import sys

# 浅层大小(不含引用的对象)
sys.getsizeof([1, 2, 3]) # 88 bytes(列表本身)

# 深层大小需要递归计算
def deep_getsizeof(obj, seen=None):
if seen is None:
seen = set()
obj_id = id(obj)
if obj_id in seen:
return 0
seen.add(obj_id)
size = sys.getsizeof(obj)
if isinstance(obj, dict):
size += sum(deep_getsizeof(k, seen) + deep_getsizeof(v, seen) for k, v in obj.items())
elif isinstance(obj, (list, tuple, set)):
size += sum(deep_getsizeof(i, seen) for i in obj)
return size

相关链接