You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

调用sys.exit()后Python模块对象内存损坏问题排查

问题分析:sys.exit()后模块对象损坏的间歇性问题

你遇到的这个间歇性问题,核心原因不是Python的bug,而是Python退出时全局对象销毁顺序的不确定性在搞鬼!让我们一步步拆解清楚:

问题复现代码

主程序 bug_happens.py

#!/usr/bin/python3
import sys
import usbtmc

scope = usbtmc.Instrument()

print('in Main')
print(' id of usbtmc.usb: 0x{:x}'.format(id(usbtmc.usb)))
print(' id of usbtmc.usb.util: 0x{:x}'.format(id(usbtmc.usb.util)))
print(' id of usbtmc.usb.util.dispose_resources: 0x{:x}'.format(id(usbtmc.usb.util.dispose_resources)))
print()

print('calling Exit()')
sys.exit()

精简后的 usbtmc 模块

"""
****** Very stripped down partial Version for debugging a memory corruption issue ******
Python USBTMC driver
Copyright (c) 2012-2017 Alex Forencich
...
"""
import usb.util

class Instrument(object):
    def __init__(self, *args, **kwargs):
        self.connected = False
        print('usbtmc.Instrument created 0x{:x}'.format(id(self)))
        print(' id of usb: 0x{:x}'.format(id(usb)))
        print(' id of usb.util: 0x{:x}'.format(id(usb.util)))
        print(' id of usb.util.dispose_resources: 0x{:x}'.format(id(usb.util.dispose_resources)))
        print()

    def __del__(self):
        print('usbtmc.Instrument.__del__() called for 0x{:x}'.format(id(self)))
        print(' id of usb: 0x{:x}'.format(id(usb)))
        print(' id of usb.util: 0x{:x}'.format(id(usb.util)))
        print(' id of usb.util.dispose_resources: 0x{:x}'.format(id(usb.util.dispose_resources)))
        print(' end of usbtmc.Instrument.__del__()')
        print()

现象说明

正常运行输出

usbtmc.Instrument created 0x7fee5775d9b0
 id of usb: 0x7fee562800e8
 id of usb.util: 0x7fee562247c8
 id of usb.util.dispose_resources: 0x7fee561ad048

in Main
 id of usbtmc.usb: 0x7fee562800e8
 id of usbtmc.usb.util: 0x7fee562247c8
 id of usbtmc.usb.util.dispose_resources: 0x7fee561ad048

calling Exit()
usbtmc.Instrument.__del__() called for 0x7fee5775d9b0
 id of usb: 0x7fee562800e8
 id of usb.util: 0x7fee562247c8
 id of usb.util.dispose_resources: 0x7fee561ad048
 end of usbtmc.Instrument.__del__()

约50%概率出现的错误

usbtmc.Instrument created 0x7fec34b829b0
 id of usb: 0x7fec336c3278
 id of usb.util: 0x7fec335dbb88
 id of usb.util.dispose_resources: 0x7fec335e5048

in Main
 id of usbtmc.usb: 0x7fec336c3278
 id of usbtmc.usb.util: 0x7fec335dbb88
 id of usbtmc.usb.util.dispose_resources: 0x7fec335e5048

calling Exit()
usbtmc.Instrument.__del__() called for 0x7fec34b829b0
 id of usb: 0xa40060
Exception ignored in: <bound method Instrument.__del__ of <usbtmc.Instrument object at 0x7fec34b829b0>>
Traceback (most recent call last):
  File "/home/don/Electronics/Projects/HF_Meas/Scope_SA/Bug_usb_close/usbtmc.py", line 27, in __del__
AttributeError: 'NoneType' object has no attribute 'util'

偶尔出现的错误

usbtmc.Instrument created 0x7ff75968c9b0
 id of usb: 0x7ff7581af0e8
 id of usb.util: 0x7ff7581537c8
 id of usb.util.dispose_resources: 0x7ff7580dc048

in Main
 id of usbtmc.usb: 0x7ff7581af0e8
 id of usbtmc.usb.util: 0x7ff7581537c8
 id of usbtmc.usb.util.dispose_resources: 0x7ff7580dc048

calling Exit()
usbtmc.Instrument.__del__() called for 0x7ff75968c9b0
 id of usb: 0x7ff7581af0e8
 id of usb.util: 0xa40060
Exception ignored in: <bound method Instrument.__del__ of <usbtmc.Instrument object at 0x7ff75968c9b0>>
Traceback (most recent call last):
  File "/home/don/Electronics/Projects/HF_Meas/Scope_SA/Bug_usb_close/usbtmc.py", line 28, in __del__
AttributeError: 'NoneType' object has no attribute 'dispose_resources'

问题根源

当你调用sys.exit()时,Python会触发解释器的退出流程:

  1. 首先,Python会开始清理全局命名空间中的对象,包括已导入的模块。
  2. 你的Instrument实例scope是全局对象,它的__del__方法会在退出时被调用,但Python完全不保证全局对象的销毁顺序。
  3. 运气不好的时候,__del__执行时,usb模块(或者usb.util子模块)已经被Python的退出清理机制销毁了——此时模块对象被置为None,内存被标记为无效,就出现了你看到的0xa40060这类垃圾值。

解决方案

你需要彻底避免在__del__方法中依赖全局模块对象,因为它们的生命周期完全不受你控制。推荐两种可靠的修复方式:

方案1:显式清理,替代__del__

添加一个显式的close()方法,在退出前手动调用,同时在实例中保存模块对象的引用,避免被提前销毁:

class Instrument(object):
    def __init__(self, *args, **kwargs):
        self.connected = False
        # 保存模块引用,绑定到实例生命周期
        self.usb = usb
        self.usb_util = usb.util
        print('usbtmc.Instrument created 0x{:x}'.format(id(self)))
        print(' id of usb: 0x{:x}'.format(id(self.usb)))
        print(' id of usb.util: 0x{:x}'.format(id(self.usb_util)))
        print(' id of usb.util.dispose_resources: 0x{:x}'.format(id(self.usb_util.dispose_resources)))
        print()

    def close(self):
        print('usbtmc.Instrument.close() called for 0x{:x}'.format(id(self)))
        print(' id of usb: 0x{:x}'.format(id(self.usb)))
        print(' id of usb.util: 0x{:x}'.format(id(self.usb_util)))
        print(' id of usb.util.dispose_resources: 0x{:x}'.format(id(self.usb_util.dispose_resources)))
        print(' end of usbtmc.Instrument.close()')
        print()
        # 这里添加实际的资源释放逻辑
        self.connected = False

    # 可选:保留__del__作为兜底,但要做防御性检查
    def __del__(self):
        print('usbtmc.Instrument.__del__() called for 0x{:x}'.format(id(self)))
        # 先检查对象是否还存在
        if hasattr(self, 'usb'):
            print(' id of usb: 0x{:x}'.format(id(self.usb)))
        else:
            print(' usb module already destroyed')
        if hasattr(self, 'usb_util'):
            print(' id of usb.util: 0x{:x}'.format(id(self.usb_util)))
            if hasattr(self.usb_util, 'dispose_resources'):
                print(' id of usb.util.dispose_resources: 0x{:x}'.format(id(self.usb_util.dispose_resources)))
            else:
                print(' dispose_resources already destroyed')
        else:
            print(' usb.util module already destroyed')
        print(' end of usbtmc.Instrument.__del__()')
        print()

主程序中手动调用close():

#!/usr/bin/python3
import sys
import usbtmc

scope = usbtmc.Instrument()

print('in Main')
print(' id of usbtmc.usb: 0x{:x}'.format(id(usbtmc.usb)))
print(' id of usbtmc.usb.util: 0x{:x}'.format(id(usbtmc.usb.util)))
print(' id of usbtmc.usb.util.dispose_resources: 0x{:x}'.format(id(usbtmc.usb.util.dispose_resources)))
print()

print('calling close()')
scope.close()
print('calling Exit()')
sys.exit()

方案2:使用上下文管理器(推荐)

让Instrument实现上下文管理器协议,用with语句自动管理资源,这是Python中最优雅的资源管理方式:

class Instrument(object):
    def __init__(self, *args, **kwargs):
        self.connected = False
        self.usb = usb
        self.usb_util = usb.util
        print('usbtmc.Instrument created 0x{:x}'.format(id(self)))
        # ... 其他初始化逻辑

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        # 这里执行资源清理逻辑
        print('usbtmc.Instrument.__exit__() called for 0x{:x}'.format(id(self)))
        # ... 释放USB资源的代码
        self.connected = False

主程序使用方式:

#!/usr/bin/python3
import sys
import usbtmc

with usbtmc.Instrument() as scope:
    print('in Main')
    # ... 你的业务逻辑代码

print('calling Exit()')
sys.exit()

关键总结

  • Python退出时的全局对象销毁顺序是完全不确定的,永远不要在__del__中依赖全局模块或其他全局对象。
  • 显式的资源清理(close()方法)或上下文管理器是更可靠的资源管理方式,能彻底避免这类间歇性问题。
  • 你看到的“垃圾值”不是内存损坏,而是模块对象被销毁后内存被标记为无效的表现,本质是生命周期管理问题。

内容的提问来源于stack exchange,提问作者Don M R

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:52:06