语言的内存管理是语言设计的一个重要方面。它是决定语言性能的重要因素。无论是c语言的手工管理,还是java的垃圾回收,都成为语言最重要的特征。这里以python语言为例子,说明一门动态类型的、面向对象的语言的内存管理方式。
对象的内存使用
赋值语句是语言最常见的功能了。但即使是最简单的赋值语句,也可以很有内涵。python的赋值语句就很值得研究。
a = 1
整数1为一个对象。而a是一个引用。利用赋值语句,引用a指向对象1。python是动态类型的语言(参考 动态类型 ),对象与引用分离。python像使用“筷子”那样,通过引用来接触和翻动真正的食物——对象。
引用和对象
为了探索对象在内存的存储,我们可以求助于python的内置函数 id() 。它用于返回对象的身份(identity)。其实,这里所谓的身份,就是该对象的 内存地址 。
a = 1
print(id(a))
print(hex(id(a)))
在我的计算机上,它们返回的是:
11246696
'0xab9c68'
分别为内存地址的十进制和十六进制表示。
在python中,整数和短小的字符,python都会缓存这些对象,以便重复使用。当我们创建多个等于1的引用时,实际上是让所有这些引用指向同一个对象。
a = 1
b = 1
print(id(a))
print(id(b))
上面程序返回
11246696
11246696
可见a和b实际上是指向同一个对象的两个引用。
为了检验两个引用指向同一个对象,我们可以用 is 关键字。is用于判断两个引用所指的对象是否相同。
# truea = 1 b = 1 print(a is b) # true a = good b = good print(a is b) # false a = very good morning b = very good morning print(a is b) # false a = [] b = [] print(a is b)
上面的注释为相应的运行结果。可以看到,由于python缓存了整数和短字符串,因此每个对象只存有一份。比如,所有整数1的引用都指向同一对象。即使使用赋值语句,也只是创造了新的引用,而不是对象本身。长的字符串和其它对象可以有多个相同的对象,可以使用赋值语句创建出新的对象。
在python中,每个对象都有存有指向该对象的引用总数,即 引用计数 (reference count)。
我们可以使用 sys 包中的 getrefcount() ,来查看某个对象的引用计数。需要注意的是,当使用某个引用作为参数,传递给getrefcount()时,参数实际上创建了一个临时的引用。因此,getrefcount()所得到的结果,会比期望的多1。
from sys import getrefcount a = [1, 2, 3] print(getrefcount(a)) b = a print(getrefcount(b))
由于上述原因,两个getrefcount将返回2和3,而不是期望的1和2。
对象引用对象
python的一个容器对象(container),比如表、词典等,可以包含多个对象。实际上,容器对象中包含的并不是元素对象本身,是指向各个元素对象的引用。
我们也可以自定义一个对象,并引用其它对象:
class from_obj(object): def __init__(self, to_obj): self.to_obj = to_obj b = [1,2,3] a = from_obj(b) print(id(a.to_obj)) print(id(b))
可以看到,a引用了对象b。
对象引用对象,是python最基本的构成方式。即使是a = 1这一赋值方式,实际上是让词典的一个键值a的元素引用整数对象1。该词典对象用于记录所有的全局引用。该词典引用了整数对象1。我们可以通过内置函数 globals() 来查看该词典。
当一个对象a被另一个对象b引用时,a的引用计数将增加1。
from sys import getrefcount a = [1, 2, 3] print(getrefcount(a)) b = [a, a] print(getrefcount(a))
由于对象b引用了两次a,a的引用计数增加了2。
容器对象的引用可能构成很复杂的拓扑结构。我们可以用objgraph包来绘制其引用关系,比如
x = [1, 2, 3] y = [x, dict(key1=x)] z = [y, (x, y)] import objgraph objgraph.show_refs([z], filename='ref_topo.png')
objgraph是python的一个第三方包。安装之前需要安装xdot。
sudo apt-get install xdot sudo pip install objgraph
objgraph官网
两个对象可能相互引用,从而构成所谓的 引用环 (reference cycle)。
a = []
b = [a]
a.append(b)
即使是一个对象,只需要自己引用自己,也能构成引用环。
a = []
a.append(a)
print(getrefcount(a))
引用环会给垃圾回收机制带来很大的麻烦,我将在后面详细叙述这一点。
纳智捷U5上手体验评测
关于波束斜视的量与角度θ和频率变化呈函数关系
关于Cortex-A7处理器性能分析
浅谈电动机工作模式分类及原理
ModbusTCP协议及通信报文解读
以Python语言为例探讨语音内存管理方式
【夏日福音】戴森新款空气净化风扇:降温净化两位一体
探讨中兴和华为在5G时代的发展
PI公司推出针对低功率LED灯泡的高度集成LED驱动器IC
埃夫特冲刺科创板 吃透大研发和大营销的红利才最要紧
明纬电源|85~100W超薄阶梯型DIN导轨型电源HDR-100系列
温度测量的基本概念
影响集成运放输入误差信号的主要参数分析
protel内电层分割的注意事项解析
深度学习基本概念
土壤碳通量测量系统产品介绍
iPhone12用什么充电器好?
人工智能工作是什么
Python入门之什么是函数
参考设计加快产品设计时间和上市时间