您的位置:首页 > 编程语言 > Python开发

Vim 的 Python 接口的内存回收机制有问题 !!!!!

2013-05-19 12:25 197 查看
<本文的原始位置: http://bluegene8210.is-programmer.com/posts/26168.html>

---- 续写 FileSystemExplorer 这个插件,现在写好的:

1. 能刷新

2. 能设置属性:

a. 是否显示隐藏文件

b. 显示基本输出(只有名称)还是扩展输出(包括大小,修改时间与访问时间)

c. 设置根据名称 / 大小 / 修改时间 / 访问时间排序,设置正序或逆序。

3. 能递归式打开节点(但是结果恐怖,后述。)

---- 几个要点记一下:

1. 数据结构内部不要形成引用回路(reference cycle)。让文件节点同时保持对上级和下级节点的引用可以方便操作,但是对上级的引用要用 weakref 实现。如果形成引用回路就会产生没法析构的对象,以及内存泄漏。

2. 怎样定义一个类似 list 的对象: 直接继承 list 类型不是个好主意,应该继承 Abstract Base
Classes(ABC)里面的 MutableSequence,然后覆盖掉以下 “虚函数”: __init__(), __len__(),
__getitem__(), __del__(), __setitem__(), insert()。

3. 类 list 对象的读取操作与 list 形式一样,可以用 slicing,也可以用 comprehension。但是赋值不一样,不能直接

self= 另一个sequence

,需要注意。

4. 可以使用 del[:] 清除一个 类 list
对象,但是操作之前要先清除成员之间的引用关系。否则即使没有引用回路存在,Python
也不知道先清除哪个成员,结果又是一堆没法析构的对象。(对这一点还不是十分确定,有可能是太过谨慎了,回头写个程序验证一下。)

5. 在函数的默认参数里不要使用可变值类型(mutable type)。比如:

def my_function(arg=[]):

pass

这样是不对的,第二次调用时那个值就会变掉。应该这样:

def my_function(arg=None):

if arg is None: arg= []

---- 测试: 使用基本输出形式,显示隐藏文件,用 recursive
方式打开我的根目录(但是产生输出内容用的是线性处理方式),将近 49000 个节点(垃圾文件触目惊心),时间大概 8 秒。记得以前用
NerdTree 递归式打开 firefox 源文件的目录,也是几万个节点,花了两分钟以上。用扩展输出形式,多用 10 秒。我的电脑是 07
年的双核笔记本。所以 Python 接口的速度还是不错的,跟 VimScript 相比。

---- 最后一个大要点必须单独写:

Vim 的 Python 接口的内存回收机制有问题 !!!!!

如上。虽然已经通过定义 __del__() 等方式确认所建立的 Python 数据对象都能被正确析构,但是内存占用还是一路彪升。用
recursive 方式打开一次根目录会增加几十 MB 内存,但是这些对象析构的时候内存却不减少。试着来回打开关闭了十几次,内存就到了 400
MB 以上,通过资源管理器来看,gvim 成了最耗内存的程序。

后来把 Python 代码搬出来,改成一般的 Python 测试程序,通过 Shell 运行,没出现这种情况。递归式建立 49000
个节点会耗用 160MB 内存,但是后面无论怎样销毁再建立,内存都不再增加,可见内存回收在起作用。所以不是我代码写的有问题,有可能是 Vim 与
Python 的 garbage collector 通气不畅所致。

[补记]:

---- 又想了个办法,在原来的 Vim 与 Python 混合代码里定义了一个测试命令,模拟其它所有内部操作但只是不往 Vim
Buffer 做任何输出。通过此命令反复进行大量数据结构的建立与销毁操作,结果与上面的测试程序一样,内存占用是固定的,不会一直增加。所以问题出在
Python 接口上面。

---- 先不想这么多,内存问题绝对是我能力以外的事情。只要平时不会二到用 recursive 方式打开几万个节点的目录再关闭,再打开再关闭,再打开再关闭 ... 这个插件还是能用的。

---- 关于 Reference Cycle 的测试代码如下,如果两个对象互相硬指向对方的话,析构函数 __del__() 不会被调用,于是内存泄漏。

# -*- coding: utf-8 -*-

import weakref

class Child:

def __init__(self, parent):

# 这里切换使用软指向还是硬指向
#   self._parent= parent    # XXX: 硬指向上级对象
self._parent= weakref.ref(parent)   # XXX: 软指向上级对象

print('Child.__init__() -- called !')

def __del__(self):
print('Child.__del__() -- called !')

class Parent:

def __init__(self):
self._child= None
print('Parent.__init__() -- called !')

def add_child(self, child):
self._child= child          # XXX: 硬指向下级对象

def __del__(self):
print('Parent.__del__() -- called !')

parent= Parent()
child= Child(parent=parent)
parent.add_child(child=child)

del parent
del child


Reference Cycle
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: