11

在 Python 的早期版本(我不记得是哪个版本)中,调用gc.get_referrers任意内部字符串可用于获取对interneddict 的引用,然后可以查询其长度。

但这在 Python 2.7.5 中不再有效:gc.get_referrers(...)不再interned在它返回的列表中包含 dict。

在 Python 2.7.5 中还有其他方法可以确定实习字符串的数量吗?如果是这样,怎么做?

4

2 回答 2

3

您可以这样做,但是所有选项都很混乱,并且充满了几乎无用的警告,所以首先,让我们考虑一下您是否真的想要这样做。

实习字符串不会延长其生命周期。您不必担心实习生的 dict 会永远增长,其中充满了您不需要的字符串。因此,字符串实习不太可能是一个实际的内存问题,并且了解有多少字符串已被实习可能毫无用处。

如果你仍然想这样做,让我们来看看你的选择。


正确的方法可能是使用您自己的实习实现...除了 Python 乏善可陈的弱引用支持不允许您创建对字符串的弱引用。这意味着如果你尝试这种方法,你要么被困在传递你自己的弱引用字符串包装器,要么让被留存的字符串永远保持活动状态。这两种选择都很糟糕。


实际上有一个功能可以打印您要询问的信息……但它也取消了所有内容。它的存在是一个实现细节,它只能通过 C API 访问,所以我们需要使用ctypes.pythonapi它来了解它。

import ctypes

_Py_ReleaseInternedStrings = ctypes.pythonapi._Py_ReleaseInternedStrings

_Py_ReleaseInternedStrings.argtypes = ()
_Py_ReleaseInternedStrings.restype = None

_Py_ReleaseInternedStrings()

输出:

releasing 3461 interned strings
total size of all interned strings: 33685/0 mortal/immortal

列出的总大小是字符串长度的总和,因此它们不包括对象标头或空终止符。


您可能不满意每次您想检查有多少时都必须释放所有的实习字符串。不幸的是,Python 没有公开实习字典,即使是通过 C API 或通过 GC 挂钩。你还能尝试什么?好吧,继续更疯狂的选择,有调试器。

ecatmur 发布了一个疯狂的 hack,以无人值守模式启动 GDB 进程,并使用条件断点来获取,与您要访问errnomap的 dict 非常相似的 dict 。interned这可以改为访问interned字典。这将是高度不可移植且极难维护的。


启动调试器也是一个糟糕的选择。你还能尝试什么?好吧,您总是可以构建自己的 Python 自定义构建。从python.org下载源代码,添加

PyObject *
AwfulHackToGetTheInternedDict(void)
{
    if (interned == NULL) {
        // No interned dict yet.
        Py_RETURN_NONE;
    }
    Py_INCREF(interned);
    return interned;
}

Objects/stringobject.c构建和安装。您可能希望使用 virtualenv 将其与普通的 Python 解释器分开。有了这个可怕的黑客,你可以做

import ctypes

AwfulHackToGetTheInternedDict = ctypes.pythonapi.AwfulHackToGetTheInternedDict

AwfulHackToGetTheInternedDict.argtypes = ()
AwfulHackToGetTheInternedDict.restype = ctypes.py_object

interned = AwfulHackToGetTheInternedDict()

获取所有实习字符串的字典。


所以,这些是你的选择,或者至少是我想到的选择。我还尝试强制 GC 跟踪一个字符串,然后将其实习以使实习的 dict 通过 GC 可见,但调用PyObject_GC_Track一个字符串会导致致命错误,所以这不起作用。

于 2016-11-14T19:30:30.643 回答
0

出于您的目的,我认为真正的答案是使用更强大的内存分析解决方案。

有几个选项可以做到这一点,例如 pypi 上的 free memory_profiler选项。

于 2016-11-10T21:47:52.277 回答