标签:测开 面向对象 计数 对象 进阶篇 引用 print 内存地址 id
内存管理机制
内存管理机制
在python中创建对象的时候,首先会去申请内存地址,然后对这个对象进行初始化,所有的对象都会维护在一个叫做 refachain
的双向循环链表中,每个数据都保存如下信息:
- 链表中数据前后数据的指针
- 数据的类型
- 数据值
- 数据的引用计数
- 数据的长度(list,dict…)
引用计数机制
引用计数增加:
- 对象被创建
- 对象被别的变量应用(另外起了一个名字)
- 对象被作为元素,放在容器中(比如被当着元素放在列表或者字典中)
- 对象被当成参数传递进函数中
上面这么描述可能有写抽象,下面我们通过实例具体查看一下
>>> a = 1000
>>> print(id(a))
140728749462288
首先当我们创建一个变量a的时候,此时python解释器会给a这个变量分配一个内容地址,我们可以看到上方a的内存地址是 140728749462288
。
那么假设现在我们现在设置b 的a,然后使用 sys.getrefcount(a)
,查看这个a这个对应的引用计数,我们可以看到是3。
>>> a = 1000
>>> b = a
>>> sys.getrefcount(a)
3
为什么这里会变成3了,因为当我们a赋值一个变量时,此时引用计数是1次,然后我们使用b赋值等于a时,此时引用计数应该是2次,之所以是3次是因为 sys.getrefcount(a)
自身是一个函数,那么当我们 对象被当成参数传递进函数中
,因此引用计数为3。
下面我们在来看一个非常有意思的现象:
>>> a = 1000
>>> b = 1000
>>> print(id(a))
2334204996144
>>> print(id(b))
2334204997136
首先,我们分别定义 a、b两个变量,此时设置的变量值是一致的,都是1000,我们去查看python底层分配的内存地址,可以看到他们的内存地址是不一样的。
那么下面我们将a和b的变量值,都改成1,再来看看:
>>> a = 1
>>> b = 1
>>> print(id(a))
140728749459120
>>> print(id(b))
140728749459120
那么我们来思考一下,为什么创建了两个不同的对象,但是内存地址却是一样的呢?
是因为python解释器底层会判断当我们的变量值为 -5 到 255 之间的值,是属于常用的数据,那么这些数据python解释器会把共同值的变量,指向同一个内存地址。其中包括ASCII码中的内容,这么做的目的非常简单,假设我们1这个值,被非常多的变量所引用,如引用一万次,如果不这么处理,那么python会非常1w个内存空间,这样就会造成非常大的资源消耗。
那么此时假设我们的变量值包含数字、字母和符号,我们来查看一下他具体的内存空间分配
>>> a = 'acb23?'
>>> b = 'acb23?'
>>> print(id(a))
2334205039216
>>> print(id(a))
2334205039216
可以看到他们的内存地址也是一样的。这里我们需要了解一下 intern机制
。
intern机制
:它的优点是,在创建新的字符串对象时,会先在缓存池里面找是否有已经存在的相同值的对象(标识符、字母、下划线的字符串),如果有,则直接拿过来(引用),避免频繁的创建和销毁内存,提升效率。
引用计数的减少:
- 对象的别名被显式的销毁
- 对象的一个别名被赋值给其他对象(例如:原来的a=10,我们改成a=100,那么此时10的引用计数就减少了)
- 对象从容器中移除,或者容易被销毁(例:对象从列表中移除,或者列表被销毁)
- 一个引用离开了他的作用域(调用函数的时候传进去的参数,在函数运行结束后,该参数的引用被销毁)
标签:测开,面向对象,计数,对象,进阶篇,引用,print,内存地址,id 来源: https://blog.csdn.net/weixin_43865008/article/details/120761928
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。