您现在的位置: 首页 > 网站导航收录 > 百科知识百科知识
python如何管理内存?
对象,内存,分配器python如何管理内存?
发布时间:2019-02-08加入收藏来源:互联网点击:
python如何管理内存?
回答于 2019-09-11 08:43:50
回答于 2019-09-11 08:43:50
谢邀。对于Python来说,内存管理涉及所有包含Python对象和堆。 Python内存管理器在内部确保对堆的管理和分配。 Python内存管理器具有不同的组件,可处理各种动态存储管理方面,如共享,分段,预分配或缓存。
在最低级别,原始内存分配器确保堆中有足够的空间通过与操作系统的内存管理器交互来存储所有与Python相关的数据。在原始内存分配器之上,几个特定于对象的分配器在同一堆上运行,并实现适合于每种对象类型的特性的不同内存管理策略。
例如,整数对象在堆内的管理方式与字符串,元组或字典不同,因为整数意味着不同的存储要求和速度/空间权衡。因此,Python内存管理器将一些工作委托给特定于对象的分配器,但确保后者在堆的边界内运行。
重要的是要理解Python堆的管理是由解释器本身执行的,并且用户无法控制它,即使它们经常操作对象指针到该堆内的内存块。 Python内存管理器通过本文档中列出的Python / C API函数按需执行Python对象和其他内部缓冲区的堆空间分配。
为了避免内存损坏,扩展编写器不应该尝试使用C库导出的函数对Python对象进行操作:malloc(),calloc(),realloc()和free()。这将导致C分配器和Python内存管理器之间的混合调用带来致命的后果,因为它们实现了不同的算法并在不同的堆上运行。
在大多数情况下,我们建议从Python堆中分配内存,因为后者受Python内存管理器的控制。 例如,当使用C编写的新对象类型扩展解释器时,这是必需的。使用Python堆的另一个原因是希望通知Python内存管理器有关扩展模块的内存需求。 可将所有内存请求委托给Python内存管理器也会使解释器整体上有更准确的内存占用空间。 所以在某些情况下,Python内存管理器可能会或可能不会触发适当的操作,如垃圾收集,内存压缩等。
回答于 2019-09-11 08:43:50
在Python 中的对象越来越多,占用的内存越来越大,垃圾回收机制就是将没用的对象清除,释放内存。Python垃圾回收采用引用计数机制为主,标记-清除和分代回收机制为辅的策略,其中标记清除机制用来解决技术引用带来的循环引用而无法释放内存的问题,分代回收机制是为提升垃圾回收的效率。
引用计数
在Python中,每个对象都有指向该对象的引用总数,即引用计数(reference count)。一个对象会记录着引用自己的对象的个数,每增加1个引用,个数加1,每减少1个引用,个数减1。在垃圾回收过程中,利用引用计数器方法,在检测到对象引用个数为 0 时,对普通的对象进行释放内存的机制。
我们可以使用 sys.getrefcount 方法,来查看每个对象的引用计数。需要注意的是,当使用某个引用作为参数,传递给 getrefcount方法时,参数实际上创建了一个临时的引用。因此,getrefcount 方法所得到的结果比期望的多1。
由上可见,l 中的 [t,27] 两个元素,都指向了同一个对象,实际上,容器对象(如,列表、字典等)中包含的并不是元素对象本身,是指向各个元素对象的引用。同时,l 的引用计数随着 ll 的创建和删除,引用计数也随着增加1和减少1。
导致引用计数增加的场景如下:
对象被创建:t = 27其它的别名被创建:ll = l作为参数传递给函数:getrefcount(l)作为容器对象的一个元素:l = [t, 27]导致引用计数减少的场景如下:
对象的别名被显式的销毁:del ll对象的一个别名被赋值给其他对象:l = 789对象所在的容器被销毁或从容器中删除对象 如,del ll 或 l.remove(t)。一个本地引用离开了它的作用域,比如上面的 getrefcount(x) 函数结束时,x指向的对象引用减1。引用计数中的循环引用
循环引用即对象之间进行相互引用,出现循环引用后,利用上述引用计数机制无法对循环引用中的对象进行释放空间,从而导致内存泄漏,这就是循环引用问题,如下:
对象 test 中的元素引用 ops,而对象 ops 中元素同时来引用 test ,从而造成仅仅删除 test和 ops对象,无法释放其内存空间,因为他们依然在被引用(引用个数不为0)。进一步解释就是循环引用后,test 和 ops 被引用个数为2,删除 test 和 ops 对象后,两者被引用的个数变为1,并不是0,而Python只有在检查到一个对象的被引用个数为0时,才会自动释放其内存,所以这里无法释放 test 和 ops 的内存空间,因此这也是导致内存泄漏的原因之一。
垃圾回收
当Python的对象的引用计数降为 0时,说明没有任何引用指向该对象,该对象就成为要被回收的垃圾。比如新建一个对象,被赋值给某个变量,则该对象的引用计数变为1。如果变量被删除,对象的引用计数为0,那么该对象就会被垃圾回收。如上,执行 del t 后,已经没有任何引用指向之前建立的对象 9527,该对象引用计数变为0,用户不可能通过任何方式使用这个对象,当垃圾回收启动时,Python扫描到这个引用计数为0的对象,就将它所占用的内存进行回收。
标记-清除机制——解决循环引用问题
标记-清除机制顾名思义,首先标记对象(垃圾检测),然后清除垃圾(垃圾回收),标记清除用来解决引用计数机制产生的循环引用,进而导致内存泄漏的问题。循环引用只有在容器对象才会产生,比如字典,元组,列表等。首先为了追踪对象,需要每个容器对象维护两个额外的指针,用来将容器对象组成一个链表,指针分别指向前后两个容器对象,这样可以将对象的循环引用摘除,就可以得出两个对象的有效计数,我们通过如下示例,进一步了解一下。
在 标记-清除机制中,存在root链表、unreachable链表,这里简单介绍一下。
如上,在未执行 del 语句时,test、ops的引用计数都为 2。但是在 del 执行完以后,test、ops 引用次数互相减 1。test、ops陷入循环引用中,此时标记清除机制来打破这种循环引用,找到其中一端 test 开始拆test、ops的引用环。即从 test 出发,因为它有一个对 ops的引用,则将 ops的引用计数减1,然后顺着引用达到 ops,因为 ops有一个对 test的引用,同样将 test的引用减1,如此就完成了循环引用对象间环摘除。
引用环去掉以后发现,test、ops循环引用变为了0,所以test、ops就被添加到 unreachable链表 中直接被回收掉。
上一篇:如何让积极努力更有意义?
下一篇:返回列表
相关链接 |
||
网友回复(共有 0 条回复) |