Python 我如何理解是否使用了内存地址?

Python 我如何理解是否使用了内存地址?,python,memory-management,free,Python,Memory Management,Free,我正在用Python垃圾收集器做一些实验,我想检查是否使用了内存地址。在下面的示例中,我已在ls[2]处取消引用字符串(当然)。如果我运行垃圾收集器,我仍然可以在原始地址看到。我想确定地址现在是可写的。有没有办法在Python中检查它 from ctypes import string_at from sys import getsizeof import gc ls = ['This','will be','surely','deleted'] idsurely= id(ls[2]) siz

我正在用Python垃圾收集器做一些实验,我想检查是否使用了内存地址。在下面的示例中,我已在
ls[2]
处取消引用字符串(
当然
)。如果我运行垃圾收集器,我仍然可以在原始地址看到
。我想确定地址现在是可写的。有没有办法在Python中检查它

from ctypes import string_at
from sys import getsizeof
import gc
ls = ['This','will be','surely','deleted']
idsurely= id(ls[2]) 
sizesurely = getsizeof(ls[2])
ls[2] = 'probably'
print(ls)
print(string_at(idsurely,sizesurely))
gc.collect()
# I check there is nothing in the garbage
print(gc.garbage)
print(string_at(idsurely,sizesurely))

我主要从理论的角度对此感兴趣,所以我不是说这是有实际用途的东西。我的目标是在教程中展示内存是如何工作的。我想表明数据仍然存在,并且现在可以写入地址处的字节。因此,脚本的输出到目前为止与预期一致。我只想证明最后一段

关于gc的文档中:

。。。收集器补充了Python中已经使用的引用计数

gc.is\u tracked()

如果垃圾回收器当前跟踪对象,则返回True,否则返回False。一般来说,不跟踪原子类型的实例,而跟踪非原子类型的实例(容器、用户定义的对象…)

垃圾回收器不跟踪字符串:

In [1]: import gc

In [2]: test = 'surely'
Out[2]: 'surely'

In [3]: gc.is_tracked(test)
Out[3]: False
查看文档,似乎没有从语言内部访问引用计数的方法

请注意,至少对我来说,在
处使用
string\u在交互式解释器中不起作用。它确实在脚本中工作。

不可能

Python中没有已使用或未使用内存地址的中央注册表。甚至没有一个所有对象的中央注册表(循环GC不知道所有对象),即使您有一个所有对象的注册表,也不足以确定正在使用的内存位置。此外,您不能只读取任意内存地址,也不能写入任意释放的地址。这将很快导致故障或更糟的情况


最后,我强烈建议不要在教程中使用这种东西,即使你确实找到了使它工作的方法。当你把一些东西放在教程中时,大部分阅读教程的人会认为这是他们应该学习的东西。编程新手不应该被误导,以为检查可能释放的内存位置是他们应该做的事情。

你的实验太离谱了
id
(仅作为CPython实现细节)确实获取了相关对象的内存地址,但我们讨论的是Python对象本身,而不是它包含的数据
sys.getsizeof
返回一个数字,该数字大致对应于对象占用的内存量,但不能保证内存是连续的

巧合的是,这几乎适用于
str
(尽管如果所讨论的字符串缓存了其UTF-8或
wchar\u t
表单的副本,那么它将执行缓冲区超读,因此您有可能使程序崩溃),但即使如此,您的测试还是有缺陷的;CPython使用看起来像合法变量名的字符串文字,因此,如果所讨论的字符串在程序中的任何其他地方显示为文字(包括作为导入的某个模块中的某个类或函数的名称),那么在替换它时,它实际上不会消失。如果文本字符串出现在任何函数中的任何位置,都可能发生类似的隐式缓存(它最终不仅被拘留,而且存储在该函数的常量中)

更新:在测试时,在实际脚本中,当您持有
副本时,
的参考计数为
3
,当您将其替换为
“可能”
时,参考计数降至
2
。事实证明,即使在全局范围内,常量也被缓存。交互式解释器没有表现出这种行为的唯一原因是它有效地将每一行分开,因此当
eval
完成时,常量缓存被丢弃

即使这一切都不是问题,但大多数(几乎所有)内存管理器(CPython的专用小对象堆和它所构建的通用堆)在释放内存时实际上不会将内存归零,因此,如果您在它真正释放后不久查看同一地址,其中可能包含非常相似的数据

最后,您的
gc.collect()
调用不会更改任何内容,除非是巧合(在
gc
过程中发生的任何事情都可能会产生副作用)
str
不是垃圾收集类型,因为它不能包含对其他Python对象的引用,所以它不可能是引用循环中的链接,而CPython垃圾收集器只负责收集循环垃圾;CPython是引用计数的,因此,当最后一个引用消失时,任何不属于引用周期的内容都会被自动清除

这一切导致的简短回答是:在CPython内部,没有办法非试探性地确定某个特定内存地址是否已释放到空闲存储并可供重用。CPython的内存管理方案纯粹是实现细节,当人们依赖API时,以这种细节级别公开API会产生兼容性问题

最接近的方法是使用类似于执行基本快照和计算快照差异的方法。这不会给你一个窗口,让你知道一个特定的地址是否仍然在使用,尽管AFAICT;充其量,它可以告诉你一个地址,肯定在使用分配

您可以使用的另一种方法(特定于CPython)是在替换对象之前只检查引用计数
sys.getrefcount
对于给定的名称/属性报告
2
,然后