如何确定 Python 2.7.5 中的 interned 字符串数?

How to determine the number of interned strings in Python 2.7.5?

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

但这在 Python 2.7.5 中不再有效:gc.get_referrers(...) 列表中不再包含 interned 字典它 returns.

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

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

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

你可以这样做,但是所有的选项都是混乱的并且充满了警告 near-uselessness,所以首先,让我们考虑一下你是否真的想要。

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

如果您仍想执行此操作,请查看您的选项。


正确的方法可能是使用您自己的实习实现...除了 Python 乏善可陈的弱引用支持不允许您创建对字符串的弱引用。这意味着如果您尝试这种方法,您要么绕过自己的 weak-referenceable 字符串包装器,要么永远保持 interned 字符串。两种选择都很糟糕。


实际上有一个函数可以打印您询问的信息...但它也 de-interns 所有内容。它的存在是一个实现细节,它只能通过 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

列出的总大小是字符串长度的总和,因此不包括 object headers 或空终止符。


您可能对每次要检查有多少字符串时都必须释放所有驻留字符串感到不高兴。不幸的是,即使通过 C API 或通过 GC 挂钩,Python 也不会公开 interned dict。你还能尝试什么?好吧,继续更疯狂的选择,那就是调试器。

ecatmur 发布了 crazy hack 在无人值守模式下启动 GDB 进程并使用条件断点到达 errnomap,这是一个与您想要的 interned 非常相似的指令访问。这可以改为访问 interned 字典。它将非常 non-portable 并且极难维护。


启动调试器也是一个糟糕的选择。你还能尝试什么?好吧,您始终可以构建自己的 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 会导致致命错误,因此这不起作用。