pin_drop当前位置:知识文库 ❯ 图文

Python sys.getsizeof()详解 - 内存占用测量与优化完整指南

概述

sys.getsizeof() 是 Python 标准库中用于测量对象内存占用大小的函数。它返回对象在内存中占用的字节数,包括对象头部信息和基本属性,但不包括对象引用的其他对象的大小。sys.getsizeof() 是性能分析和内存优化的基础工具,帮助开发者了解不同数据结构的内存开销,从而做出更合理的设计选择。


语法

代码示例

import sys

sys.getsizeof(object[, default])

参数说明

参数 类型 默认值 说明
object 任意类型 必填 要测量内存大小的对象
default 任意类型 当对象无法提供大小时返回的默认值

返回值

返回 int,表示对象占用的内存字节数。如果对象无法提供大小且未指定 default,则引发 TypeError 异常。


代码示例

示例1:基本数据类型的内存占用

代码示例

import sys

# 数值类型
print("数值类型:")
print(f"  int(0): {sys.getsizeof(0)} 字节")
print(f"  int(1): {sys.getsizeof(1)} 字节")
print(f"  int(10**6): {sys.getsizeof(10**6)} 字节")
print(f"  float(1.0): {sys.getsizeof(1.0)} 字节")
print(f"  bool(True): {sys.getsizeof(True)} 字节")
print(f"  None: {sys.getsizeof(None)} 字节")

# 字符串类型
print("\n字符串类型:")
print(f"  '': {sys.getsizeof('')} 字节")
print(f"  'a': {sys.getsizeof('a')} 字节")
print(f"  'abc': {sys.getsizeof('abc')} 字节")
print(f"  '你好': {sys.getsizeof('你好')} 字节")

# 容器类型(仅计算容器本身,不含元素内容)
print("\n容器类型(空容器):")
print(f"  []: {sys.getsizeof([])} 字节")
print(f"  (): {sys.getsizeof(())} 字节")
print(f"  {{}}: {sys.getsizeof({})} 字节")
print(f"  set(): {sys.getsizeof(set())} 字节")

输出:

代码示例

数值类型:
  int(0): 24 字节
  int(1): 28 字节
  int(10**6): 28 字节
  float(1.0): 24 字节
  bool(True): 28 字节
  None: 16 字节

字符串类型:
  '': 49 字节
  'a': 50 字节
  'abc': 52 字节
  '你好': 53 字节

容器类型(空容器):
  []: 56 字节
  (): 40 字节
  {}: 64 字节
  set(): 216 字节

示例2:容器增长时的内存变化

代码示例

import sys

def track_list_growth():
    """跟踪列表增长时的内存变化"""
    lst = []
    sizes = []

    for i in range(20):
        size = sys.getsizeof(lst)
        sizes.append((len(lst), size))
        lst.append(i)

    print("列表长度 -> 内存大小 (字节)")
    print("-" * 35)
    for length, size in sizes:
        bar = '#' * (size // 10)
        print(f"  {length:2d} -> {size:4d}  {bar}")

track_list_growth()

输出:

代码示例

列表长度 -> 内存大小 (字节)
-----------------------------------
   0 ->   56
   1 ->   88  ########
   2 ->   88  ########
   3 ->   88  ########
   4 ->   88  ########
   5 ->  120  ###########
   6 ->  120  ###########
   7 ->  120  ###########
   8 ->  120  ###########
   9 ->  184  ##################
  10 ->  184  ##################
  ...

示例3:递归计算对象总内存

代码示例

import sys
from collections.abc import Mapping, Iterable

def get_total_size(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, (str, bytes, bytearray)):
        return size

    # 字典类型
    if isinstance(obj, Mapping):
        size += sum(get_total_size(k, seen) + get_total_size(v, seen)
                    for k, v in obj.items())
    # 可迭代类型(排除字符串)
    elif isinstance(obj, Iterable) and not isinstance(obj, (str, bytes)):
        size += sum(get_total_size(item, seen) for item in obj)

    return size

# 对比浅层和深层内存占用
data = {
    "name": "张三",
    "scores": [85, 92, 78, 95, 88],
    "info": {"age": 25, "city": "北京"}
}

shallow = sys.getsizeof(data)
deep = get_total_size(data)

print(f"浅层内存占用: {shallow} 字节")
print(f"深层内存占用: {deep} 字节")
print(f"差异: {deep - shallow} 字节 (被引用对象)")

输出:

代码示例

浅层内存占用: 184 字节
深层内存占用: 856 字节
差异: 672 字节 (被引用对象)

实际应用场景

  • 内存优化:比较不同数据结构(如列表 vs 元组、字典 vs 命名元组)的内存开销,选择更节省内存的方案

  • 性能分析:定位内存占用过大的对象,优化数据存储方式

  • 缓存策略:评估缓存对象的内存成本,合理设置缓存上限


注意事项

注意1sys.getsizeof() 只计算对象本身的内存,不包括其引用的其他对象。例如列表的 size 不包含列表中元素的大小。要计算总内存需要递归遍历。

注意2:不同 Python 版本和实现(CPython、PyPy 等)中,相同对象的内存大小可能不同。以上示例基于 CPython 3.11。

注意3:Python 中小整数(-5 到 256)和小字符串会被缓存(intern),多次引用同一对象不会额外占用内存。

注意4:容器类型(如列表、字典)在添加元素时会预分配额外空间,因此内存增长不是线性的,而是阶梯式的。

提示:对于更深入的内存分析,推荐使用 tracemalloc 模块或第三方工具 memory_profilerpympler,它们能提供更详细的内存分配追踪。


相关方法对比

特性 sys.getsizeof() tracemalloc pympler.asizeof() memory_profiler
测量方式 单对象浅层 内存分配追踪 递归深层 行级内存分析
递归计算 不支持 不适用 支持 不适用
运行时开销 极低 中等 中等 较高
实时监控
安装要求 标准库 标准库 需安装 需安装
适用场景 快速估算 内存泄漏排查 精确测量 性能调优

小结

  • sys.getsizeof() 是测量单个对象内存占用的快捷方法,返回浅层字节数

  • 不同数据类型的内存开销差异显著,选择合适的数据结构能有效节省内存

  • 容器类型存在预分配机制,内存增长呈阶梯式而非线性

  • 需要计算对象总内存时,需自行实现递归遍历或使用 pympler 等第三方库


练习题

练习1

编写一个函数,比较空列表、空元组、空字典、空集合的内存占用,并计算存储 1000 个整数时各容器的总内存差异。

练习2

编写一个递归函数 deep_getsizeof(),能够计算嵌套数据结构(如嵌套字典、嵌套列表)的总内存占用,并处理循环引用问题。

练习3

编写一个内存分析工具,接受一个对象,输出其浅层内存、深层内存、以及各子对象的内存占比排名。

常见问题

sys.getsizeof() 计算的是对象的总内存吗?

不是。sys.getsizeof() 只计算对象本身的内存(浅层大小),不包括其引用的其他对象。例如列表的大小不包含列表中元素的大小。要计算总内存需要递归遍历或使用 pympler.asizeof()

为什么列表的内存增长是阶梯式的?

Python 列表在添加元素时会预分配额外空间,以减少频繁内存分配的开销。当预分配空间用完后,会一次性分配更大的空间,因此内存增长呈阶梯式而非线性。

如何计算嵌套对象的总内存?

需要自行实现递归遍历函数,遍历对象及其引用的所有子对象,累加每个对象的 sys.getsizeof() 值。注意要处理循环引用问题,使用 seen 集合记录已访问的对象 ID。

列表和元组的内存占用有什么区别?

元组的内存占用通常比列表小,因为元组是不可变的,不需要预分配额外空间。列表为了支持动态增长,会预分配额外容量。对于固定大小的数据集合,使用元组更节省内存。

标签: 内存分析 getsizeof 内存优化 性能分析 数据结构

本文涉及AI创作

内容由AI创作,请仔细甄别

list快速访问

上一篇: Python sys.platform详解 - 跨平台开发与系统判断入门指南 下一篇: Python sys.setrecursionlimit()详解 - 递归深度限制与栈溢出处理入门指南

poll相关推荐