如何在 PyPy 中调试 GC?
How to debug GC in PyPy?
我最近一直在尝试从 CPython 切换到 PyPy,并且在尝试解决一个错误时,更准确地说是一个带有 SIGSEGV 信号的错误 139(所以是分段错误),我试图调查通过查看 gc.garbage
属性列表通过 GC 模块进行垃圾收集。
在 CPython 中,例如,我可以 运行 下面的一段代码(取自 there 并进行了修改)来检查 GC 垃圾列表中的延迟对象:
import gc
gc.set_debug(gc.DEBUG_SAVEALL)
print(gc.get_count())
lst = []
lst.append(lst)
list_id = id(lst)
del lst
gc.collect()
for item in gc.garbage:
print(item) if list_id == id(item) else "pass"
此代码在 CPython 中运行良好,但 returns 在 PyPy 中出现以下错误:
AttributeError: module 'gc' has no attribute 'set_debug'
的确,print(dir(gc))
,returns 不同的 GC class 属性和方法列表,没有列出 gc.set_debug()
PyPy :
# Under CPython
['DEBUG_COLLECTABLE', 'DEBUG_LEAK', 'DEBUG_SAVEALL', 'DEBUG_STATS', 'DEBUG_UNCOLLECTABLE', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'callbacks', 'collect', 'disable', 'enable', 'garbage', 'get_count', 'get_debug', 'get_objects', 'get_referents', 'get_referrers', 'get_stats', 'get_threshold', 'is_tracked', 'isenabled', 'set_debug', 'set_threshold']
# Under PyPy
['GcCollectStepStats', 'GcRef', '__doc__', '__loader__', '__name__', '__package__', '__spec__', '_dump_rpy_heap', '_get_stats', 'collect', 'collect_step', 'disable', 'disable_finalizers', 'dump_rpy_heap', 'enable', 'enable_finalizers', 'garbage', 'get_objects', 'get_referents', 'get_referrers', 'get_rpy_memory_usage', 'get_rpy_referents', 'get_rpy_roots', 'get_rpy_type_index', 'get_stats', 'get_typeids_list', 'get_typeids_z', 'hooks', 'isenabled']
如果我理解正确,设置 gc.set_debug(gc.DEBUG_SAVEALL)
会将无法访问的对象保留在 GC 的垃圾列表中,因此如果没有它,gc.collect()
将尝试释放对象的内存分配。但我之前想检查垃圾列表,因为我怀疑它触发了我试图跟踪的分段错误。
尽管查看了 PyPy 关于垃圾收集的文档(如 here,
here) and other places (like here or here),我一直无法找到一种方法来在 PyPy 中仔细观察垃圾收集过程,就像在 CPython 中可以做到的那样。那么,有人可以向我解释 PyPy 和 CPython 的 GC 之间的差异如何影响上述测试代码,更准确地说,如何在使用 PyPy 收集之前观察 gc.garbage
中的未决对象?
我 运行宁 Python 3.6.9 与 PyPy 7.3.2。 GCC 对于 CPython 是 8.4.0,对于 PyPy 是 7.3.1。
你想做的事情是不可能的。即使在 CPython 上,列表 gc.garbage
也不会包含所有被回收的对象,即使您启用调试模式,但只包含那些被发现处于循环中的对象。除了循环查找逻辑本身的作者之外,这不太可能与任何人相关。在 PyPy 上,“处于循环中”的概念甚至更不相关;正如您可能已经从您指向的各种链接中了解到的那样,PyPy 的 GC 是完全不同的。
不,无法检查所有 正在消亡的对象。事实上,PyPy 的 GC 针对早逝的对象进行了优化,对于所有这些(通常是程序中 all 对象的 80%-90%),GC 的结构是这样一来,甚至 都无法知道 垂死的物体是什么。这 80%-90% 的对象占据 space 是批量回收的,不是一个一个回收的。
您很有可能从错误的角度看待问题。如果您能详细描述一下您的问题是什么,我们可以尝试提出更好的解决方案。同时,请注意,当您遇到段错误时,您可以 运行 pypy -X faulthandler
至少获得某种回溯。
尝试 运行 python 和 faulthandler
as suggested by this answer to python tracing a segmentation fault
这应该适用于 CPython 和 PyPy
% python3 -q -X faulthandler -c "import ctypes; ctypes.string_at(0)"
Fatal Python error: Segmentation fault
Current thread 0x00007fe10d301740 (most recent call first):
File "/usr/lib/python3.8/ctypes/__init__.py", line 514 in string_at
File "<string>", line 1 in <module>
Segmentation fault (core dumped)
当您遇到段错误时,一些更高级的调试也可能会有所帮助。您可以跟随trace
module or the strace
program(仅限Linux)
当心,这些会产生巨大的输出量
python -m trace --trace myprogram.py
strace python myprogram.py
我最近一直在尝试从 CPython 切换到 PyPy,并且在尝试解决一个错误时,更准确地说是一个带有 SIGSEGV 信号的错误 139(所以是分段错误),我试图调查通过查看 gc.garbage
属性列表通过 GC 模块进行垃圾收集。
在 CPython 中,例如,我可以 运行 下面的一段代码(取自 there 并进行了修改)来检查 GC 垃圾列表中的延迟对象:
import gc
gc.set_debug(gc.DEBUG_SAVEALL)
print(gc.get_count())
lst = []
lst.append(lst)
list_id = id(lst)
del lst
gc.collect()
for item in gc.garbage:
print(item) if list_id == id(item) else "pass"
此代码在 CPython 中运行良好,但 returns 在 PyPy 中出现以下错误:
AttributeError: module 'gc' has no attribute 'set_debug'
的确,print(dir(gc))
,returns 不同的 GC class 属性和方法列表,没有列出 gc.set_debug()
PyPy :
# Under CPython
['DEBUG_COLLECTABLE', 'DEBUG_LEAK', 'DEBUG_SAVEALL', 'DEBUG_STATS', 'DEBUG_UNCOLLECTABLE', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'callbacks', 'collect', 'disable', 'enable', 'garbage', 'get_count', 'get_debug', 'get_objects', 'get_referents', 'get_referrers', 'get_stats', 'get_threshold', 'is_tracked', 'isenabled', 'set_debug', 'set_threshold']
# Under PyPy
['GcCollectStepStats', 'GcRef', '__doc__', '__loader__', '__name__', '__package__', '__spec__', '_dump_rpy_heap', '_get_stats', 'collect', 'collect_step', 'disable', 'disable_finalizers', 'dump_rpy_heap', 'enable', 'enable_finalizers', 'garbage', 'get_objects', 'get_referents', 'get_referrers', 'get_rpy_memory_usage', 'get_rpy_referents', 'get_rpy_roots', 'get_rpy_type_index', 'get_stats', 'get_typeids_list', 'get_typeids_z', 'hooks', 'isenabled']
如果我理解正确,设置 gc.set_debug(gc.DEBUG_SAVEALL)
会将无法访问的对象保留在 GC 的垃圾列表中,因此如果没有它,gc.collect()
将尝试释放对象的内存分配。但我之前想检查垃圾列表,因为我怀疑它触发了我试图跟踪的分段错误。
尽管查看了 PyPy 关于垃圾收集的文档(如 here,
here) and other places (like here or here),我一直无法找到一种方法来在 PyPy 中仔细观察垃圾收集过程,就像在 CPython 中可以做到的那样。那么,有人可以向我解释 PyPy 和 CPython 的 GC 之间的差异如何影响上述测试代码,更准确地说,如何在使用 PyPy 收集之前观察 gc.garbage
中的未决对象?
我 运行宁 Python 3.6.9 与 PyPy 7.3.2。 GCC 对于 CPython 是 8.4.0,对于 PyPy 是 7.3.1。
你想做的事情是不可能的。即使在 CPython 上,列表 gc.garbage
也不会包含所有被回收的对象,即使您启用调试模式,但只包含那些被发现处于循环中的对象。除了循环查找逻辑本身的作者之外,这不太可能与任何人相关。在 PyPy 上,“处于循环中”的概念甚至更不相关;正如您可能已经从您指向的各种链接中了解到的那样,PyPy 的 GC 是完全不同的。
不,无法检查所有 正在消亡的对象。事实上,PyPy 的 GC 针对早逝的对象进行了优化,对于所有这些(通常是程序中 all 对象的 80%-90%),GC 的结构是这样一来,甚至 都无法知道 垂死的物体是什么。这 80%-90% 的对象占据 space 是批量回收的,不是一个一个回收的。
您很有可能从错误的角度看待问题。如果您能详细描述一下您的问题是什么,我们可以尝试提出更好的解决方案。同时,请注意,当您遇到段错误时,您可以 运行 pypy -X faulthandler
至少获得某种回溯。
尝试 运行 python 和 faulthandler
as suggested by this answer to python tracing a segmentation fault
这应该适用于 CPython 和 PyPy
% python3 -q -X faulthandler -c "import ctypes; ctypes.string_at(0)"
Fatal Python error: Segmentation fault
Current thread 0x00007fe10d301740 (most recent call first):
File "/usr/lib/python3.8/ctypes/__init__.py", line 514 in string_at
File "<string>", line 1 in <module>
Segmentation fault (core dumped)
当您遇到段错误时,一些更高级的调试也可能会有所帮助。您可以跟随trace
module or the strace
program(仅限Linux)
当心,这些会产生巨大的输出量
python -m trace --trace myprogram.py
strace python myprogram.py