(1).引用计数
在python中每创建一个对象,对应的会有一个引用计数,当发生赋值操作如a=b,对应的b的引用计数会自动加1,当引用的对象被清除或者函数结束时,引用计数会自动减1.
在python中使用引用计数,标记清楚,分代回收三种方式进行垃圾回收.
ython语言虽然提供了对内存的垃圾收集机制,但实际上它将不用的内存放到内存池而不是返回给操作系统,所以就有了以下:
①. Pymalloc机制;这个主要是为了加速Python的执行效率,Python引入了一个内存池机制,用于管理,为了对小块内存的申请和释放.
由于python中万物皆对象,所以python的存储问题是对象的存储问题.实际上,对于每个对象,python会分配一块内存空间去存储它.
那么python是如何进行内存分配,如何进行内存管理,又是如何释放内存的呢?
总结起来有一下几个方面:引用计数,垃圾回收,内存池机制
python内部使用引用计数,来保持追踪内存中的对象,Python内部记录了对象有多少个引用,即引用计数
①.、对象被创建 a= 'abc'
①.、变量被删除 del a 或者 del b
垃圾回收机制: ① 引用计数 , ②标记清除 , ③分带回收
引用计数也是一种垃圾收集机制, 而且也是一种最直观, 最简单的垃圾收集技术.当python某个对象的引用计数降为 0 时, 说明没有任何引用指向该对象, 该对象就成为要被回收的垃圾了.(如果出现循环引用的话, 引用计数机制就不再起作用了)
优点:简单实时性,缺点:维护引用计数消耗资源,且无法解决循环引用.
如果两个对象的引用计数都为 1 , 但是仅仅存在他们之间的循环引用,那么这两个对象都是需要被回收的, 也就是说 它们的引用计数虽然表现为非 0 , 但实际上有效的引用计数为 0 ,.所以先将循环引用摘掉, 就会得出这两个对象的有效计数.
标记清除算法也有明显的缺点:清除非活动的对象前它必须顺序扫描整个堆内存,哪怕只剩下小部分活动对象也要扫描所有对象.
为了提高效率,有很多对象,清理了很多次他依然存在,可以认为,这样的对象不需要经常回收,可以把它分到不同的集合,每个集合回收的时间间隔不同.简单的说这就是python的分代回收.
在Python中,许多时候申请的内存都是小块的内存,这些小块内存在申请后,很快又会被释放,当创建大量消耗小内存的对象时,频繁调用new/malloc会导致大量的内存碎片,致使效率降低.
内存池的概念就是预先在内存中申请一定数量的,大小相等的内存块留作备用,当有新的内存需求时,就先从内存池中分配内存给这个需求,不够了之后再申请新的内存.这样做最显著的优势就是能够减少内存碎片,提升效率.
在运行py程序的时候,解释器会专门分配一块空白的内存,用来存放纯单词字符组成的字符串(数字,字母,下划线)
字符串赋值时,会先去查找要赋值的字符串是否已存在于内存区域,已存在,则指向已存在的内存,不存在,则会在大整数池中分配一块内存存放此字符串
Dict
在小型程序中,特别是在脚本中,使用Python自带的dict来表示结构信息非常简单方便:
x = ob['x']
ob['y'] = y
print(sys.getsizeof(ob))
如上所示,dict占用了大量内存,尤其是如果突然虚需要创建大量实例时:
实例数
对象大小
①. 000 000
①.0 000 000
①.00 000 000
类实例
有些人希望将所有东西都封装到类中,他们更喜欢将结构定义为可以通过属性名访问的类:
class Point:
#
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
x = ob.x
ob.y = y
类实例的结构很有趣:
字段
大小(比特)
PyGC_Head
PyObject_HEAD
__weakref__
__dict__
合计:
print(sys.getsizeof(ob), sys.getsizeof(ob.__dict__))
所以呢,大量类实例在内存中占用的空间少于常规字典(dict):
大小
不难看出,由于实例的字典很大,所以实例依然占用了大量内存.
带有__slots__的类实例
为了大幅降低内存中类实例的大小,我们可以考虑干掉__dict__和__weakref__.为此,我们可以借助 __slots__:
__slots__ = 'x', 'y', 'z'
如此一来,内存中的对象就明显变小了:
x
y
z
总计:
在类的定义中使用了__slots__以后,大量实例占据的内存就明显减少了:
目前,这是降低类实例占用内存的主要方式.
这种方式减少内存的原理为:在内存中,对象的标题后面存储的是对象的引用(即属性值),访问这些属性值可以使用类字典中的特殊描述符:
pprint(Point.__dict__)
mappingproxy(
....................................
'x': ,
'y': ,
'z': })
为了自动化使用__slots__创建类的过程,你可以使用库namedlist().namedlist.namedlist函数可以创建带有__slots__的类:
Point = namedlist('Point', ('x', 'y', 'z'))
还有一个包attrs(),无论使用或不使用__slots__都可以利用这个包自动创建类.
元组
Python还有一个自带的元组(tuple)类型,代表不可修改的数据结构.元组是固定的结构或记录,但它不包含字段名称.你可以利用字段索引访问元组的字段.在创建元组实例时,元组的字段会一次性关联到值对象:
x = ob[0]
ob[1] = y # ERROR
元组实例非常紧凑:
大小(字节)
ob_size
[0]
[1]
命名元组
由于元组的使用非常广泛,所以终有一天你需要通过名称访问元组.为了满足这种需求,你可以使用模块collections.namedtuple.
namedtuple函数可以自动生成这种类:
Point = namedtuple('Point', ('x', 'y', 'z'))
如上代码创建了元组的子类,其中还定义了通过名称访问字段的描述符.对于上述示例,访问方式如下:
class Point(tuple):
@property
def _get_x(self):
return self[0]
def _get_y(self):
return self[1]
def _get_z(self):
def __new__(cls, x, y, z):
return tuple.__new__(cls, (x, y, z))
这种类所有的实例所占用的内存与元组完全相同.但大量的实例占用的内存也会稍稍多一些:
记录类:不带循环GC的可变更命名元组
由于元组及其相应的命名元组类能够生成不可修改的对象,所以呢类似于ob.x的对象值不能再被赋予其他值,所以有时还需要可修改的命名元组.由于Python没有相当于元组且支持赋值的内置类型,所以呢人们想了许多办法.今天这一节我们讨论一下记录类(recordclass,),它在StackoverFlow上广受好评().
此外,它还可以将对象占用的内存量减少到与元组对象差不多的水平.
recordclass包引入了类型recordclass.mutabletuple,它几乎等价于元组,但它支持赋值.它会创建几乎与namedtuple完全一致的子类,但支持给属性赋新值(而不需要创建新的实例).recordclass函数与namedtuple函数类似,可以自动创建这些类:
Point = recordclass('Point', ('x', 'y', 'z'))
类实例的结构也类似于tuple,但没有PyGC_Head:
在默认情况下,recordclass函数会创建一个类,该类不参与垃圾回收机制.一般来说,namedtuple和recordclass都可以生成表示记录或简单数据结构(即非递归结构)的类.在Python中正确使用这二者不会造成循环引用.所以呢,recordclass生成的类实例默认情况下不包含PyGC_Head片段(这个片段是支持循环垃圾回收机制的必需字段,或者更准确地说,在创建类的PyTypeObject结构中,flags字段默认情况下不会设置Py_TPFLAGS_HAVE_GC标志).
大量实例占用的内存量要小于带有__slots__的类实例:
dataobject
recordclass库提出的另一个解决方案的基本想法为:内存结构采用与带__slots__的类实例同样的结构,但不参与循环垃圾回收机制.这种类可以通过recordclass.make_dataclass函数生成:
Point = make_dataclass('Point', ('x', 'y', 'z'))
这种方式创建的类默认会生成可修改的实例.
另一种方法是从recordclass.dataobject继承:
class Point(dataobject):
x:int
y:int
z:int
这种方法创建的类实例不会参与循环垃圾回收机制.内存中实例的结构与带有__slots__的类相同,但没有PyGC_Head:
如果想访问字段,则需要使用特殊的描述符来表示从对象开头算起的偏移量,其位置位于类字典内:
mappingproxy({'__new__': ,
.......................................
大量实例占用的内存量在CPython实现中是最小的:
Cython
还有一个基于Cython()的方案.该方案的优点是字段可以使用C语言的原子类型.访问字段的描述符可以通过纯Python创建.例如:
cdef class Python:
cdef public int x, y, z
本例中实例占用的内存更小:
内存结构如下:
我觉得可能是因为你的py文件在第一次启动后,已经编译成pyc文件了,再次启动的时候都是加载pyc,省去了编译的阶段,所以速度很快.
你可以试着把程序目录下的所有pyc或者你的代码文件对应的pyc文件删除,看看是不是可以和第一次加载速度相同
一般情况下不会占内存,而且Python的内存需要的很小.如果用模拟器的话需要退出一下,输入close就可以了.请采纳,谢谢.
python 怎么在循环中释放内存
#include"stdio.h"
main()
{
printf("input string:\n");
gets(st);
puts(st);
}
可以看出当输入的字符串中含有空格时,输出仍为全部字符串.说明gets函数并不以空格作为字符串输入结束的标志,而只以回车作为输入结束.这是与scanf函数不同的.
以上就是土嘎嘎小编为大家整理的关于python释放函数内存的信息相关主题介绍,如果您觉得小编更新的文章只要能对粉丝们有用,就是我们最大的鼓励和动力,不要忘记讲本站分享给您身边的朋友哦!!