当前位置: 首页 > news >正文

python资源释放问题

这里要讲的,本质上是在自问自答

尽管如此,深挖后发现仍然有些python深层的运行机制需要注意。一起来踏上学习之旅吧~

第一部分 资源释放

问题:python在shell中运行,shell窗口关闭时,python中的变量和资源都会释放吗?

简短回答:是的,几乎都会。 这个问题它触及了操作系统进程管理的核心概念。当Shell窗口关闭时,操作系统会终止在该Shell中运行的所有进程(包括Python解释器),进程所占用的所有资源都会被操作系统强制回收。

一、深入理解:为什么会释放?

1. 进程与资源的关系

当你运行 python3 script.py 时,Shell会创建一个新的进程来执行Python解释器。

这个进程拥有自己独立的内存空间、文件描述符、环境变量等资源

操作系统内核负责跟踪和管理所有这些资源。

2. Shell关闭时发生了什么?

当你关闭Shell终端(无论是点击“X”、输入 exit 还是断开SSH会话),操作系统会向该Shell进程发送一个 SIGHUP (Signal Hang Up) 信号。

Shell进程在收到 SIGHUP 后,在退出之前,它会向由它启动的所有子进程(包括你的Python程序)也发送 SIGHUP 信号。默认情况下,SIGHUP 信号的处理方式是终止进程。

3. 进程终止后的清理

当一个进程被终止(无论是自愿退出还是被信号杀死),操作系统内核会自动负责清理该进程所占用的所有资源

  • 内存:包括堆(Heap)和栈(Stack)中的所有数据,所有Python变量(整数、列表、字典、对象等)所占用的内存都会被彻底释放。

  • 文件描述符:所有打开的文件都会被关闭。

  • 网络连接:所有的Socket连接会被关闭。

  • 其他系统资源:如管道、共享内存段等都会被清理。

可以这样理解: 资源是向操作系统“借”的。进程是“借款人”。借款人消失了(进程终止),操作系统这个“银行”就会自动收回所有借出的资源。


二、例外情况与注意事项

虽然绝大多数资源都会被释放,但在某些极端情况下,可能会留下“垃圾”,不过这非常罕见。

1. 僵尸进程

如果Python进程创建了子进程,并且在Shell终止时,Python父进程先于其子进程被杀死,这些子进程可能会短暂地变成“僵尸进程”。

但是,当Shell退出后,这些僵尸进程的父进程ID (PPID) 会变为 1 (即 init 或 systemd 进程),init 进程会定期“收割”僵尸进程,最终它们也会被完全清理掉。这是一个自动的过程。

2. 外部持久化资源

Python进程管理的内部资源会被释放,但如果你的程序修改了外部资源,这些更改是持久的,不会被回滚:

  • 已写入文件的数据:如果程序已经执行了 file.write() 并且数据已写入磁盘,文件内容会被保留。

  • 已发送的网络请求:如果请求已经到达服务器,服务器已经处理,那么这个效果是永久的。

  • 修改了的数据库记录:如果已经执行了 COMMIT,数据库的更改是永久的。

示例:

# 假设这个程序运行到一半被强制终止
import jsondata = {"name": "test"} # 这个变量在内存中,会被释放
with open("data.json", "w") as f:json.dump(data, f)   # 如果执行到这里,数据已经写入磁盘# 如果在这之后进程被杀死,文件 "data.json" 仍然存在且包含数据f.flush() # 强制将缓冲区数据写入磁盘

三、如何模拟和验证?

你可以写一个简单的Python脚本来验证这个过程:

创建测试脚本 test.py

#!/usr/bin/env python3
import time
import signal
import atexit# 定义一个很大的列表,占用显著的内存
large_list = [i for i in range(10_000_000)] # 约 80 MB 内存
print(f"Created a large list with {len(large_list)} elements.")# 注册一个退出函数(如果正常退出会被执行)
@atexit.register
def cleanup():print("Cleanup function called!") # 如果这行没打印,说明进程是被强制杀死的# 忽略SIGHUP信号,看看会发生什么
# signal.signal(signal.SIGHUP, signal.SIG_IGN)print("Script is running. Now close the terminal window...")
try:while True:time.sleep(1)
except KeyboardInterrupt:print("Received interrupt.")

验证步骤:

  1. 在终端运行 python3 test.py

  2. 直接关闭终端窗口。

  3. 重新打开一个终端,运行 htop 或 top 命令。

  4. 观察进程列表,你会发现之前的Python进程已经完全消失了,它占用的内存也被回收了。同时,Cleanup function called! 这行字也不会出现,因为 atexit 函数只在程序正常退出时调用,而强制关闭属于非正常退出。


四、如何避免资源被释放?

如果你希望关闭Shell窗口后Python程序继续运行,你需要让进程与Shell脱离关系,使其不受 SIGHUP 信号影响。有几种方法:

1. nohupnohup 会忽略 SIGHUP 信号,& 让进程在后台运行。

2. disowndisown 命令可以将作业从Shell的作业表中移除,使其不再接收Shell发出的信号。

总结

场景结果
直接关闭Shell窗口进程被强制终止,所有内存变量和系统资源被操作系统100%回收。
程序已修改外部资源修改(文件、数据库)会被保留,因为更改已经发生在进程之外。
使用 nohuptmux 等方法Python进程与Shell分离,继续运行,资源不会被释放。

因此,可以放心地关闭Shell窗口,操作系统会为你做好清理工作,不会因为Python变量没有手动释放而导致内存泄漏。

如释重负~~

第二部分 python中类的__del__函数

问题:python调用了一个类,类中写了__del__函数进行了一系列处理。在关闭shell时,__del__函数会执行吗?

简短回答:不会,或者更准确地说,不保证会执行。

当Shell窗口关闭时,操作系统会强制终止Python进程,这是一种“粗暴”的清理方式,不会给Python解释器任何机会去优雅地执行清理代码,包括 __del__ 方法。


一、__del__何时不会执行?

优雅终止:当Python程序正常结束时(例如,代码执行完毕、遇到 sys.exit() 或未捕获的异常),解释器会开始垃圾回收过程。在这个过程中,如果对象的引用计数降为0,它的 __del__ 方法就会被调用。

强制终止:当Shell窗口关闭时,操作系统发送 SIGHUP 或 SIGKILL 信号。SIGKILL(信号9)是尤其“无情”的,它会立即从内核层面彻底移除进程,不会给进程任何机会去执行自己的清理代码(包括 __del__atexittry/finally 等)。

__del__ 方法是一个析构函数 ,它的调用依赖于Python解释器的垃圾回收机制。而操作系统的强制杀死行为,发生在解释器层面之下,完全绕过了Python的垃圾回收流程。想象一下,你的房子(进程)着火了。

优雅终止:你有时间(__del__)去抢救重要物品、关闭煤气闸(finally)、然后从大门离开(sys.exit)。

强制终止:房子被一颗导弹直接击中(SIGKILL),瞬间化为灰烬。你根本没有时间做任何事。

二、__del__ 的正确用途

用途:既然 __del__ 不可靠,那它有什么用?

  • 作为一种最后的保障,在对象被Python解释器正常垃圾回收时,释放一些非外部资源

  • 例如:提醒开发者这个对象应该被更显式地清理了。(在实际开发中,这很少见)。

陷阱:

  1. 执行顺序不确定:Python不保证垃圾回收的顺序。如果 __del__ 试图访问另一个已经被销毁的对象的属性,会导致难以调试的错误。

  2. 循环引用问题:如果两个对象有 __del__ 方法且相互引用,垃圾回收器可能永远无法回收它们,导致内存泄漏。

  3. 不保证执行:正如你所经历的,在强制终止、解释器崩溃等情况下,它不会运行。

既然 __del__ 不可靠,我们应该使用其他方法来确保资源被正确释放。

三、可靠的资源清理方案

1. 上下文管理器 (with 语句) 

这是Python中最推荐的方式。它保证了无论代码块如何退出(即使是异常),__exit__ 方法都会被调用。

class ResourceHandler:def __init__(self, name):self.name = namedef __enter__(self):print(f"Acquiring {self.name}")return self # 返回的资源对象def __exit__(self, exc_type, exc_val, exc_tb):# 这里的清理代码无论如何都会执行print(f"Releasing {self.name} (guaranteed!)")with open("release_log.txt", "a") as f:f.write(f"{self.name} was released\n")# 使用方式
with ResourceHandler("DatabaseConnection") as resource:# 使用资源print("Using the resource...")# 即使这里出现异常,__exit__也会被调用
# 离开with代码块后,__exit__自动调用

2. 显式清理方法

提供一個显式的 .close() 或 .cleanup() 方法,并要求调用者在使用完毕后调用它。通常与 try...finally 结合使用。

class FileHandler:def __init__(self, filename):self.file = open(filename, 'w')def write_data(self, data):self.file.write(data)def close(self): # 显式清理方法if self.file:self.file.close()self.file = Noneprint("File closed explicitly.")# 使用方式(保证会清理)
handler = FileHandler("test.txt")
try:handler.write_data("Hello")
finally:handler.close() # 确保无论发生什么都会执行关闭

总结

方法可靠性说明
__del__ 方法不保证执行,尤其怕强制终止。应避免用于关键清理。
with 语句最强推荐。能处理异常,保证清理代码执行。
try...finally与显式清理方法配合,非常可靠。
atexit只在Python正常退出时工作,怕 SIGKILL

结论:不要依赖 __del__ 来做任何重要的清理工作。 对于文件、网络连接、数据库连接等资源的释放,永远优先使用上下文管理器 (with 语句) 或 try...finally 块


文章转载自:

http://FV4D7cdP.Lwzgn.cn
http://IfDJRPsM.Lwzgn.cn
http://3KcckiLU.Lwzgn.cn
http://Z2QB7VVz.Lwzgn.cn
http://iMSHJdTj.Lwzgn.cn
http://vxifmEHh.Lwzgn.cn
http://z0RPBm16.Lwzgn.cn
http://86q05Vbe.Lwzgn.cn
http://DAvfleWI.Lwzgn.cn
http://HG3QEzYd.Lwzgn.cn
http://VILA89He.Lwzgn.cn
http://NOZhhFfN.Lwzgn.cn
http://UxkNrgFH.Lwzgn.cn
http://p8EF5vlP.Lwzgn.cn
http://tU8l54er.Lwzgn.cn
http://NOIUAtpi.Lwzgn.cn
http://NLwkGc6u.Lwzgn.cn
http://p1doK1eA.Lwzgn.cn
http://C14NMrau.Lwzgn.cn
http://e4DQ4kfW.Lwzgn.cn
http://xJlVimc7.Lwzgn.cn
http://EpGarS1c.Lwzgn.cn
http://8GzoZ9L2.Lwzgn.cn
http://kSmXP8Cf.Lwzgn.cn
http://Bax5AhNE.Lwzgn.cn
http://NpzdHTrA.Lwzgn.cn
http://4JKw4Z0D.Lwzgn.cn
http://4VwaMsis.Lwzgn.cn
http://bhqTfbtx.Lwzgn.cn
http://RuVoYWNL.Lwzgn.cn
http://www.dtcms.com/a/386082.html

相关文章:

  • ATR网格---ATR计算原理研究运用
  • 用Postman实现自动化接口测试
  • Hyper Rust HTTP 库入门教程
  • 软考系统架构设计师之软件架构评估法-ATAM
  • 贪心算法应用:图着色问题(顶点着色)
  • 基于51单片机的电子琴弹奏及播放系统
  • 守护每一滴水的清澈与安全
  • Python入门教程之成员运算符
  • 简易BIOS设置模拟界面设计
  • Git教程:常用命令 和 核心原理
  • Tomcat Session 管理与分布式方案
  • 声纹识别技术深度剖析:从原理到实践的全面探索
  • 第6章串数组:特殊矩阵的压缩存储
  • 多账号矩阵管理再也不复杂
  • 电商接口之电子面单API接口对接以及调用:以快递鸟为例
  • Ubuntu22.04部署-LNMP
  • Day05_苍穹外卖——Redis店铺营业状态设置
  • C++(list)
  • Toshiba东芝TB67S109AFNAG炒菜机器人的应用体验
  • Parasoft 斩获 AutoSec 2025 优秀汽车 AI 测试创新方案奖,引领行业安全测试革新
  • MoonBit 正式加入 WebAssembly Component Model 官方文档 !
  • 【线性代数:代数余子式】
  • 基于一种域差异引导的对比特征学习的小样本故障诊断方法
  • k8s pod优雅滚动更新实践
  • Day43 嵌入式 中断、定时器与串行通信
  • Flink框架中的窗口类别:时间窗口、计数窗口
  • PayPal将加密货币整合到点对点支付中,打通Web2与Web3?
  • 正则表达式学习
  • IP 打造:如何长期保持表达动力与热情?
  • 网站使用独立ip有什么好处