1
limyel 2020-03-13 17:43:59 +08:00
我记得循环引用不是用标记清除来解决吗
|
2
lithbitren OP @limyel 是的,一直都是这么听说的,如果循环引用数量少的话(少于 44 对时),在__del__里发打印,会发现程序退出的时候才会打印。
|
3
lithbitren OP 顺便问问怎么在输入框里正常贴代码,直接贴好像缩进会被铲掉。
|
4
ipwx 2020-03-13 17:51:02 +08:00
Python 因为有 ref-counter,所以标记清扫很懒惰的吧。也是因为更推荐能用 weakref 就用 weakref
|
5
ppyybb 2020-03-13 19:39:53 +08:00 via iPhone
错误挺明显了,print 是个不可重入的函数,你在 del 里面写,会导致 print 可能被调用了一半后中断切到另外一个 del 里面又执行 print,导致报错。详情可以去知乎搜灵剑老大关于 gc 导致的不安全问题的文章。这个和线程安全不一样,是在一个线程里面切换的。如果 print 线程不安全,那么平常你 call print 也没有加锁啊……
|
6
lithbitren OP @ppyybb 谢谢大佬提示,学 c 的时候学过不过那时还不了解并行,把函数的重入性和线程安全搞混了,后来玩 python 几乎不会出线程相关错误,所以是第一次见这个错误,错误信息直接丢网上大多都在说线程安全问题,所以以为就是线程安全问题,现在搜了搜资料大概是了解。
灵剑大大的那篇文章也没具体说什么情况会报错,感觉这应该算是一个例子吧。 不过灵剑大大在评论区里提到了 pypy,pypy 很多扩展和函数都不能直接用,不过测了下好像也是会自动 GC 的,不过启动阈值很大, 而且并不是成对清理的,开 n = 1 000 000 才发现__del__函数的调用。psutil 不能用,任务管理器里进程内存的变化肉眼不可,感觉还是挺黑箱的。 pypy7.3 No_188294: (d_i: 188294, d_a: 188293, d_b: 40108) No_327273: (d_i: 138979, d_a: 138979, d_b: 148185) No_482999: (d_i: 155726, d_a: 155726, d_b: 138979) No_623060: (d_i: 140061, d_a: 140061, d_b: 155726) No_758523: (d_i: 135463, d_a: 135463, d_b: 140061) No_914568: (d_i: 156045, d_a: 156045, d_b: 135463) |
7
lxy42 2020-03-13 23:14:12 +08:00
可能是 print 执行过程中触发了 GC, 然后 GC 回收对象时又执行了__del__中的 print, 因为 print 无法重入, 导致报错.
|
8
ppyybb 2020-03-13 23:23:45 +08:00 via iPhone
@lithbitren 是比较黑箱,不知道这个知识点的就会被坑,所以最好不要在 del 里面做这些操作,很难控制
|
9
monsterxx03 2020-03-13 23:38:16 +08:00 via iPhone
在 3.4 之前,之前如果一个 class 内部有循环引用,并重载了 del,的确会内存泄漏,celery4.1 就有这个 bug https://github.com/celery/celery/pull/4839
|
10
felix021 2020-03-14 00:16:29 +08:00
CPython 1.x 仅仅使用引用计数来实现垃圾回收。虽然引用计数易于实现和理解,但是它不足以解决循环引用的问题。于是乎在 1999 年实现了一个循环垃圾回收器,并且附加在了 Python 2 以后的版本上。
|
11
lithbitren OP 垃圾回收好像看到说用通过链表来解决不可到达区,但为什么会有残留呢?
|