Python内存管理机制

说到内存管理,就先说一下垃圾回收吧。垃圾回收是Python,Java等语言管理内存的一种方式,说的直白些,就是清除无用的垃圾对象。C语言及C++中,需要通过malloc来进行内存的申请,通过free而进行内存的释放。而Python和Java中有自动的内存管理机制,不需要动态的释放内存,这种机制就是垃圾回收。

Python中通过引用计数法来进行内存的管理的。对每一个对象,都维护这一个对指向该对对象的引用的计数。比如:

a = "ABC"
b = a

首先创建了一个字符串对象"ABC",然后将字符串对象的引用赋值为a。因而,字符串对象"ABC"的引用计数会加1。当执行b=a的时候,仅仅是创建了指向"ABC"这个字符串对象的引用的别名,而没有创建对象。这样,字符串对象"ABC"的引用计数会加1。如何验证呢?我们可以查看a和b的id是否一致即可:

a = "ABC"
b = a
id(a) 36422672
id(b) 36422672

引用计数的数目可以通过sys.getrefcount()函数来进行获取。为啥创建a对象后引用计数是2而不是1呢?这是因为一个是全局域里的,一个是调用函数的。当执行完b=a后,引用计数加1。

import sys
a = "ABC"
sys.getrefcount(a) 2
b = a
sys.getrefcount(a) 3

那什么时候引用计数增加,什么时候引用计数减少呢?小编总结了一下:

引用计数增加主要有以下场景:

1、对象被创建时:a = "ABC";

2、另外的别人被创建时:b = a;

3、被作为参数传递给函数时:foo(a);

4、作为容器对象(list,元组,字典等)的一个元素时:x = [1,a,'33']。

引用计数减少主要有以下场景:

1、一个本地引用离开了它的作用域时,比如上面的foo(a)函数结束时,a指向的对象引用减1;

2、对象的引用被显式的销毁时:del a 或者 del b;

3、对象的引用被赋值给其他对象时:a = 789;

4、对象从一个容器对象(例如list)中移除时:L.remove(a);

5、容器对象本身被销毁:del L,或者容器对象本身离开了作用域。

所谓垃圾回收,就是回收引用计数为0的对象,释放其占用的内存空间。垃圾回收机制还有一个循环垃圾回收器, 确保释放循环引用对象(a引用b, b引用a, 导致其引用计数永远不为0)。

Python的内存管理是层次结构的,各层负责不同的功能,如下图所示:-1,-2层主要有操作系统进行操作,属于内核态;第0层是C中的malloc,free等内存分配和释放函数进行操作;第1层和第2层是内存池,有Python的接口函数PyMem_Malloc函数实现,当对象小于256K时由该层直接分配内存;第3层是最上层,也就是我们对Python对象的直接操作。感兴趣的同学可以阅读以下《Python源码剖析》,相信你的收获肯定是大大滴。

在 C 中如果频繁的调用 malloc 与 free 时会产生性能问题的,再加上频繁的分配与释放小块的内存会产生内存碎片。Python 在这里主要干的工作有:如果请求分配的内存在1~256字节之间就使用自己的内存管理系统,否则直接使用 malloc。这里还是会调用 malloc 分配内存,但每次会分配一块大小为256k的大块内存。经由内存池登记的内存到最后还是会回收到内存池,,并不会调用 C 的 free 释放掉,以便下次使用。

Python中的变量在内存中的分配主要有两种,一种是简单拷贝,比如上一篇讲述的list,改变一个就会引起另一个的改变,这是因为它们的引用相同,指向了同一个对象:

L= [3,4,5]
LL = L
id(L)  34432584
id(LL) 34432584
L.append(6)
id(L)  34432584
id(LL) 34432584

另外一种是深度拷贝,如数值、字符串、元组(tuple不允许被更改)等,也就是说当将变量a赋值给变量b时,虽然a和b的内存空间仍然相同,但当a的值发生变化时,会重新给a分配空间,a和b的地址变得不再相同。

a = 'ABC'
b = a
id(a) 36042680
id(b) 36042680
a = 'XYZ'
id(a) 36043424
id(b) 36042680
原文地址:https://www.cnblogs.com/chif/p/9349787.html