调试Python致命错误:GC对象已被跟踪

时间:2014-04-20 05:00:18

标签: python crash garbage-collection

我的python代码一直崩溃,错误' GC对象已经跟踪过' 。试图找出调试此崩溃的最佳方法。

操作系统:Linux。

  • 是否有正确的方法来调试此问题。

以下文章中有几点建议。 Python memory debugging with GDB

不确定哪种方法适用于作者。

  • 是否有办法在可以分析的场景中生成内存转储。就像在Windows世界中一样。

发现了一些关于此的文章。但不完全回答我的问题: http://pfigue.github.io/blog/2012/12/28/where-is-my-core-dump-archlinux/

3 个答案:

答案 0 :(得分:13)

在我的场景中找出了这个问题的原因(不一定是GC对象崩溃的唯一原因)。 我使用GDB和Core转储来调试此问题。

我有Python和C扩展代码(在共享对象中)。 Python代码使用C扩展代码注册一个Callback例程。 在某个工作流程中,来自C扩展代码的线程在Python代码中调用已注册的回调例程。

这通常可以正常工作,但是当多个线程同时执行相同的操作时,会导致崩溃并且“GC对象已被跟踪”。

同步对多线程的python对象的访问确实解决了这个问题。

感谢任何回应。

答案 1 :(得分:9)

当我们的C ++代码触发python回调时,我使用boost :: python遇到了这个问题。我偶尔会得到“GC对象已被跟踪”,程序将终止。

我能够在触发错误之前将GDB附加到进程。一个有趣的事情,在python代码中我们用回调包装回调 一个functools partial,它实际上掩盖了真正的错误发生的地方。用简单的可调用包装类替换partial后。 “GC对象已经跟踪错误”不再弹出,而是我现在只是遇到了一个段错误。

在我们的boost :: python包装器中,我们有lambda函数来处理C ++回调,lambda函数捕获了boost :: python :: object回调函数。事实证明,无论出于何种原因,在lambda的析构函数中,当销毁导致段错误的boost :: python :: object时,并不总是正确地获取GIL。

修复是不使用lambda函数,而是创建一个函数,确保在boost :: python :: object上调用PyDECREF()之前在析构函数中获取GIL。

class callback_wrapper
{
public:
    callback_wrapper(object cb): _cb(cb), _destroyed(false) {
    }

    callback_wrapper(const callback_wrapper& other) {
        _destroyed = other._destroyed;
        Py_INCREF(other._cb.ptr());
        _cb = other._cb;
    }

    ~callback_wrapper() {
        std::lock_guard<std::recursive_mutex> guard(_mutex);
        PyGILState_STATE state = PyGILState_Ensure();
        Py_DECREF(_cb.ptr());
        PyGILState_Release(state);
        _destroyed = true;
    }

    void operator ()(topic_ptr topic) {
        std::lock_guard<std::recursive_mutex> guard(_mutex);
        if(_destroyed) {
            return;
        }
        PyGILState_STATE state = PyGILState_Ensure();
        try {
            _cb(topic);
        }
        catch(error_already_set) { PyErr_Print(); }
        PyGILState_Release(state);
    }

    object _cb;
    std::recursive_mutex _mutex;
    bool _destroyed;
};

答案 2 :(得分:6)

问题是你试图将对象添加到Python的循环垃圾收集器跟踪两次。

结帐this bug,具体来说:

长话短说:如果您设置了Py_TPFLAGS_HAVE_GC并且您正在使用Python的内置内存分配(标准tp_alloc / tp_free),那么您就不会这样做必须手动拨打PyObject_GC_Track()PyObject_GC_UnTrack()。 Python背后处理它。

不幸的是,目前还没有很好地记录这一点。一旦您解决了问题,请随时查看有关此行为的更好文档的错误报告(上面链接)。