当前位置:Gxlcms > Python > 为什么Python要使用有明显缺陷的引用计数而不是像JavaScript一样的标记清除?

为什么Python要使用有明显缺陷的引用计数而不是像JavaScript一样的标记清除?

时间:2021-07-01 10:21:17 帮助过:33人阅读

引用计数有循环计数这个明显缺陷,那为什么Python还要使用引用计数而不是标记清除呢?

回复内容:

引用计数最大的好处是回收及时:一个对象的引用计数归零的那一刻即是它成为垃圾的那一刻,同时也是它被回收的那一刻。而这正式 mark-sweep 等 tracing GC 算法的劣势:一个对象成为垃圾之后,直到被下一轮 GC 清理掉之前,还要在内存中留存一段时间(floating garbage)。

Python 的 GC 设计是,对于内部不包含指向其他对象的引用的对象(如字符串、数值类型等),采用引用计数,因为这些对象根本不可能产生循环引用。对于 List、Map 等可能产生循环引用的对象,则采用 mark-sweep。所以我的理解是,Python 的 GC 设计一定程度上综合了两类 GC 算法的优点——即保证回收的完整性,又力求回收的及时性。

Update

上文描述有偏颇,把评论里 R 大的补充贴上来:
对List啊Map啊Set之类的引用计数也在起作用的。mark-sweep只是备份。

整套引用计数机制嵌在ceval.c里了,所有对象都要被它折腾到。
换句话说不是List不被引用计数,而是List的引用计数如果自然降到零的话就自然按照引用计数机制释放;否则当cycle GC启动的时候就会对它处理。
不过通过引用计数来提高回收及时性这点仍然还是成立的。 这是一种设计取舍。用CPython的大家高兴就好呗。
其它Python实现有许多不用引用计数的,不高兴可以用它们(逃

另外CPython的引用计数是有mark-sweep备份的,不怕循环引用。
官网解释了这个选择(但其实也没说什么…):docs.python.org/2/faq/d
The details of Python memory management depend on the implementation. The standard C implementation of Python uses reference counting to detect inaccessible objects, and another mechanism to collect reference cycles, periodically executing a cycle detection algorithm which looks for inaccessible cycles and deletes the objects involved. The gc module provides functions to perform a garbage collection, obtain debugging statistics, and tune the collector’s parameters.

In the absence of circularities and tracebacks, Python programs do not need to manage memory explicitly.

Why doesn’t Python use a more traditional garbage collection scheme? For one thing, this is not a C standard feature and hence it’s not portable. (Yes, we know about the Boehm GC library. It has bits of assembler code for most common platforms, not for all of them, and although it is mostly transparent, it isn’t completely transparent; patches are required to get Python to work with it.)
...
Traditional GC also becomes a problem when Python is embedded into other applications. While in a standalone Python it’s fine to replace the standard malloc() and free() with versions provided by the GC library, an application embedding Python may want to have its own substitute for malloc() and free(), and may not want Python’s. Right now, Python works with anything that implements malloc() and free() properly.

然后请看这篇文章介绍较新的Python的“mark-swep GC”其实还是“分代式”的:patshaughnessy.net/2013

然后就是JavaScript也不一定是用mark-sweep的…语言规范没这么规定,实际实现也不全是用mark-sweep。 Python现在还用引用计数是因为很久很久以前Python很老土的在引用计数上打了个补丁,就这么一直打补丁打过来的

Garbage Collection for Python 先简单正面回答下题主,为啥python要用呢,因为作者愿意

如果题主是想问,改用“更好”的标记清除是不是一个更好的选择,那就可以多说一些了

首先,这个前提并不成立,学术一点的观点可以看《垃圾回收》这书,里面用“没有银弹”来形容算法的选择,即没有谁比谁好,只有在具体的场景下谁比谁适合的问题
引用计数最大好处是实时性,其次是在没有循环引用的情况下避免过多的over-allocation,内存不会浪费
这个实时性的重要性有多大呢,我猜在某些场景下,比大多数人认为的都重要很多,具体可以看我的blog文章,这篇也正好是在说其它会stop world的gc算法的缺点时候写的:
雪崩效应 - xtlisk的专栏
(文章是一个系列,对语言理论有兴趣的可以多交流:)
再者,循环引用真的是一个很大的缺陷吗,这个其实一直有争论,有人用统计来证明绝大多数代码和对象并不产生循环引用,用代码来规避即可,但也有人说在特定场景下会非常多(比如游戏中角色、装备、buff之间的关系),一般认为还是一个很大缺陷的,但并非不能解决

其次,问为什么之前先看是不是,py标准版的确主要用引用计数,但是其他实现并非这样,如Jython就直接依赖所在jvm的了。而且标准版并没有无视循环引用这个缺陷,在2.5就引入了gc模块,针对循环引用用局部标记清扫,注意是局部标记清扫而非普通的标记清扫,这个思想虽然一样但还是有特殊点,具体可以看《垃圾回收》这本书

事实上py用ref count是有一个缺陷的,一定程度甚至是很大程度上导致GIL这种机制,吐槽GIL的人非常多,但很少注意到跟GC的选择有关 Python刚刚被写出来的时候标记清除并没有今天这么大行其道, Java也是从引用计数走过来的。 ObjectiveC还在使用引用计数。

大部分策略总归有两面性, 比如你也要看看引用计数的好处啊:
  • 如果程序员能够做到不循环引用,那效率还是很高,不会带来VM停机啊
  • 再一个来说为什么用引用计数?简单啊, 你听完马上自己就可以去写一个。 易懂啊,几分钟就能说明白了。 可以对比标记清除
等到Python长到足够大开始像Java一样担忧引用计数的时候,悲剧发生了,好像怎么都改不过来了。 相信到今天还是有不少人在致力于去掉Python里的引用计数

巴拉巴拉的絮絮叨叨
Python memory management compared with other language runtime 你调试Java程序时在暂挂态触发一个JVM GC看看,不感觉卡一下吗? 为什么JavaScript要使用有明显缺陷的标记清除而不是像Python一样的引用计数? 为什么JavaScript要使用有明显缺陷的标记清除而不是像Python一样的引用计数?
标记清除有性能低下这个明显缺陷,那为什么JavaScript还要使用标记清除而不是引用计数呢? 引用计数比较方便让第三方拓展模块参与管理cpython的内存。第三方的拓展模块可以容易的保存cpython的指针而不必担心这个指针所指向的内存被释放了。

人气教程排行