讲师中心 微信公众号

工具推荐

视频效率加速
剧云
剧云

中文短剧剧本制作平台

updream

一键生成高质量推广成片

立刻MV
立刻MV

低成本歌曲 MV、视频生成

Seko
Seko

AI短剧一键生成成片

解决Python对象自引用导致的内存泄漏:使用弱引用方法

胖墨酱_2771

胖墨酱_2771

发布时间:2025-10-15 10:19:15

|

689人浏览过

|

来源于php中文网

原创

解决Python对象自引用导致的内存泄漏:使用弱引用方法

当python对象内部列表持有其自身绑定方法的强引用时,会形成循环引用,导致垃圾回收器无法自动销毁旧对象,从而引发内存泄漏。本文将详细介绍如何利用`weakref.weakmethod`创建弱引用来打破这种循环,确保对象在不再被引用时能够被python的自动垃圾回收机制正确清理,避免手动调用`gc.collect()`。

理解Python的垃圾回收与循环引用

Python的垃圾回收机制主要依赖引用计数。当一个对象的引用计数归零时,它就会被销毁。然而,引用计数无法解决循环引用的问题。例如,当对象A引用对象B,同时对象B又引用对象A时,即使外部不再有对A或B的引用,它们的引用计数也不会降到零,从而导致它们无法被回收。Python的垃圾回收器包含一个循环检测器来处理这种情况,但手动触发(如gc.collect())或等待其自动运行可能不总是最佳实践,尤其是在需要及时释放资源的场景中。

在给定的代码示例中,Foo类的一个实例将其自身的绑定方法print_func添加到其functions列表中。绑定方法本质上是一个包含了对实例(self)的强引用的对象。因此,foo对象通过其functions列表强引用了自身,形成了一个循环引用:foo -> functions列表 -> 绑定方法 -> foo。

import gc

class Foo():
    def __init__(self):
        self.functions = []
        print('CREATE', self)

    def some_func(self):
        # 此处将绑定方法(包含对self的强引用)添加到列表中
        for i in range(3):
            self.functions.append(self.print_func)
        print(self.functions)

    def print_func(self):
        print('I\'m a test')

    def __del__(self):
        print('DELETE', self)

# 第一次创建Foo对象
foo = Foo()
foo.some_func()

# 第二次创建Foo对象,期望第一个对象被销毁
foo = Foo()

# 如果不调用gc.collect(),第一个Foo对象不会被销毁
# gc.collect()

input() # 保持程序运行,观察输出

运行上述代码,你会发现第一个Foo对象的__del__方法并没有被调用,表明它仍然存活,占用了内存。只有手动调用gc.collect()后,旧对象才会被销毁。

解决方案:使用weakref.WeakMethod

为了打破这种循环引用,我们可以使用Python标准库weakref模块中的WeakMethod。弱引用(weak reference)是一种特殊的引用,它不会增加对象的引用计数。当一个对象只剩下弱引用时,它仍然会被垃圾回收器销毁。

立即学习“Python免费学习笔记(深入)”;

Python Hardener
Python Hardener

通过完整的安全审查、代码整洁重构、健壮的错误处理、正确的日志记录以及完整的文档字符串覆盖,强化 Python 脚本。

下载

weakref.WeakMethod专门用于创建对绑定方法的弱引用。它允许你存储一个方法,而不会阻止该方法所属的对象被垃圾回收。

实现细节

修改Foo类中的some_func方法,使用WeakMethod来存储绑定方法:

from weakref import WeakMethod

class Foo():
    def __init__(self):
        self.functions = []
        print('CREATE', self)

    def some_func(self):
        for i in range(3):
            # 使用WeakMethod创建弱引用
            self.functions.append(WeakMethod(self.print_func))
        print(self.functions)

    def print_func(self):
        print('I\'m a test')

    def __del__(self):
        print('DELETE', self)

# 第一次创建Foo对象
foo = Foo()
foo.some_func()

# 调用弱引用方法:需要先解引用,再调用
# 注意:如果对象已被回收,则解引用会返回None
if foo.functions[0]():
    foo.functions[0]()() # 第一次调用弱引用对象,获取绑定方法;第二次调用实际方法

# 第二次创建Foo对象,旧对象将被自动销毁
foo = Foo()
input()

输出分析

运行修改后的代码,你将观察到如下输出(地址可能不同):

CREATE <__main__.Foo object at 0x0000018F0B397150>
[<weakref at 0x0000018F0B18E0A0; to 'Foo' at 0x0000018F0B397150>, <weakref at 0x0000018F0B18E1F0; to 'Foo' at 0x0000018F0B397150>, <weakref at 0x0000018F0B18E490; to 'Foo' at 0x0000018F0B397150>]
I'm a test
CREATE <__main__.Foo object at 0x0000018F0B397190>
DELETE <__main__.Foo object at 0x0000018F0B397150>

从输出中可以看到,当第二个Foo对象被创建时,第一个Foo对象的__del__方法被自动调用,证明它已被成功垃圾回收。这表明WeakMethod有效地打破了循环引用,使得Python的自动垃圾回收机制能够正常工作。

注意事项

  1. 方法调用方式:使用WeakMethod存储的方法,在调用时需要先通过调用弱引用对象本身来获取实际的绑定方法,然后再调用该绑定方法。例如,如果weak_method_ref是一个WeakMethod实例,你需要使用weak_method_ref()()来调用它。
  2. 对象生命周期:如果弱引用指向的对象已经被垃圾回收,那么调用weak_method_ref()将返回None。因此,在调用从弱引用中获取的方法之前,最好进行None检查,以避免TypeError。
# 示例:安全地调用弱引用方法
weak_func = foo.functions[0]
actual_method = weak_func() # 获取实际的绑定方法
if actual_method:
    actual_method() # 调用实际方法
else:
    print("对象已被回收,无法调用方法。")

总结

在Python中处理包含其自身绑定方法列表的对象时,为了避免因循环引用导致的内存泄漏,推荐使用weakref.WeakMethod来存储这些方法。这种方法能够确保对象在不再被外部强引用时,能够被Python的垃圾回收机制自动、及时地清理,从而维护程序的内存效率和稳定性。理解并正确运用弱引用是编写健壮Python代码的关键实践之一,尤其是在开发需要长期运行或内存敏感的应用程序时。

热门AI工具

更多
二狗PPT
二狗PPT Hot

一款AI演示文稿工具,主要用于专为中式职场打造的AI PPT生成工具,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

花生AI
花生AI Hot

一款AI视频创作工具,主要用于B站推出的AI视频创作工具,适合需要提升相关任务效率的用户。

咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

Flowith
Flowith Hot

Flowith是一款基于画布和智能体工作流的 AI 协作工作空间。

Lovart
Lovart Hot

一款面向视觉设计创作的AI设计平台,可通过智能体和画布工作流辅助制作海报、Logo、网页、PPT及其他视觉内容。

超级简历WonderCV

一款AI办公效率工具,主要用于免费求职简历模版下载制作,应届生职场人必备简历制作神器,适合需要提升相关任务效率的用户。

相关专题

更多
Go-Zero API开发使用教程
Go-Zero API开发使用教程

本专题聚焦Go-Zero的API开发,拆解.api描述文件的语法结构、路由与分组定义、参数绑定与校验规则,说明统一响应体、JWT鉴权、跨域与接口文档生成的落地方式,并给出网关聚合与接口测试的实践建议。

20

2026.11.12

Go-Zero安装与goctl环境配置教程
Go-Zero安装与goctl环境配置教程

本专题围绕Go-Zero的安装与工具链配置展开,讲清Go版本要求、goctl安装方式、代理镜像、protoc依赖与环境变量设置,说明多版本共存、升级回退与CI流水线安装的做法,并整理安装阶段的常见报错与验证步骤。

20

2026.11.12

Go-Zero零基础入门教程
Go-Zero零基础入门教程

本专题整理Go-Zero的入门知识,从框架定位、环境准备到跑通第一个API服务,讲清目录分层、配置加载、日志与中间件的基本用法,并对比Gin、Kratos的差异,帮助Go开发者建立整体认知,判断它是否适合当前项目。

20

2026.11.12

Postman身份认证怎么配置
Postman身份认证怎么配置

本专题从实际API接口调试场景出发,讲解Postman身份认证的完整配置流程,包括常见认证协议介绍、授权参数设置、访问令牌填写、OAuth 2.0授权及认证失败排查,帮助用户正确完成接口授权与访问验证。

120

2026.11.06

Postman请求头Headers怎么设置
Postman请求头Headers怎么设置

围绕Postman HTTP请求头配置展开,讲解Headers选项的具体使用方式,包括Key和Value参数填写、请求头信息添加、常见Header字段设置及请求头修改方法,帮助用户解决接口调试过程中遇到的请求头配置问题。

100

2026.11.06

Postman接口请求发送教程
Postman接口请求发送教程

详细介绍Postman接口请求的创建与发送方法,涵盖GET和POST请求、URL配置、请求参数、Headers请求头、Body请求体设置以及响应结果查看,帮助用户掌握Postman接口请求的完整操作流程。

100

2026.11.06

DBeaver怎么导入数据
DBeaver怎么导入数据

DBeaver数据导入专题,整理Excel、CSV、SQL、TXT等常见文件的数据导入方法,涵盖目标数据库选择、数据表创建、字段映射、数据类型设置及导入参数配置等操作,帮助用户将本地文件中的数据写入数据库,并了解批量导入、重复数据处理和导入失败等常见问题的解决方法。

80

2026.11.05

DBeaver安全防护教程大全
DBeaver安全防护教程大全

DBeaver安全防护专题,围绕数据库连接安全、账号密码保护和访问权限管理展开,整理SSL/TLS加密连接、SSH隧道、数据库凭据保存及证书验证等配置方法,同时介绍敏感信息保护、远程连接风险和高权限账号使用等注意事项,帮助用户提高数据库日常管理的安全性。

80

2026.11.05

DBeaver连接创建与配置教程
DBeaver连接创建与配置教程

本专题聚焦DBeaver数据库连接配置与管理,覆盖从新建连接、选择数据库类型到填写连接参数和验证连接的完整流程。针对MySQL、PostgreSQL、SQL Server等常见数据库,整理相应的连接设置方法,并提供驱动缺失、认证失败和连接异常等问题的排查方向。

80

2026.11.04

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn