python - 内存管理

目录

1.0 python引用机制

1.1 python动态类型

2.0 python引用计数

2.1 引用计数器原理

2.2 获取引用计数(getrefcount)

2.3 增加引用计数

2.4 减少引用计数

2.5 循环引用的情况

2.5.1 内存泄露:

2.5.2 内存溢出:(out of memory)

3.0 python垃圾回收

3.1 gc机制

3.2 三种情况触发垃圾回收

3.3 分代(generation)回收

3.4 标记清除

4.0 python 缓冲池

4.1   整数对象缓冲池

4.2  字符串缓存

4.3  python内存管理总结

5.0 深拷贝与浅拷贝

1.浅拷贝只会拷贝第一层地址

2.深拷贝会拷贝每一层的值


1.0 python引用机制

1.1 python动态类型

对象是储存在内存中的实体。

• 我们在程序中写的对象名,只是指向这一对象的引用(reference)

• 引用和对象分离,是动态类型的核心

• 引用可以随时指向一个新的对象(内存地址会不一样)

 #########################################

2.0 python引用计数

在Python中,每个对象都有存有指向该对象的引用总数,即引用计数(reference count)。

 #########################################

2.1 引用计数器原理

每个对象维护一个 ob_ref 字段,用来记录该对象当前被引用的次数

每当新的引用指向该对象时,它的引用计数ob_ref加1

每当该对象的引用失效时计数ob_ref减1

一旦对象的引用计数为0,该对象可以被回收,对象占用的内存空间将被释放。

它的缺点是需要额外的空间维护引用计数,这个问题是其次的

最主要的问题是它不能解决对象的“循环引用

 #########################################

2.2 获取引用计数(getrefcount)

  #########################################

2.3 增加引用计数

当一个对象A被另一个对象B引用时,A的引用计数将增加1

  #########################################

2.4 减少引用计数

del删除或者重新引用时候,引用计数会变化(del只是删除引用)

 ######################################### 

2.5 循环引用的情况

对于上面相互引用的情况,如果不存在其他对象对他们的引用,这两个对象

所占用的内存也还是无法回收,从而导致内存泄漏.

 ######################################### 

2.5.1 内存泄露:

有一部分内存无法被回收释放,进程又无法访问

 ######################################### 

2.5.2 内存溢出:(out of memory)

内存不够用,程序需要的内存大于系统空闲的内存 

引用计数可以解决大部分内存释放问题,但是无法解决循环引用问题

 ######################################### 

3.0 python垃圾回收

回收原则: 

当某个python对象的引用计数为0时候,可以被垃圾回收

 ######################################### 

3.1 gc机制

GC作为现代编程语言的自动内存管理机制,专注于两件事

• 找到内存中无用的垃圾资源

• 清除这些垃圾并把内存让出来给其他对象使用。

GC彻底把程序员从资源管理的重担中解放出来,让他们有更多的时间放 在业务逻辑上。但这并不意味

着码农就可以不去了解GC,毕竟多了解GC知识还是有利于我们写出更健 壮的代码。

 ######################################### 

3.2 三种情况触发垃圾回收

1.调用gc.collect()

2.gc达到阈值

3.程序退出时候

 ######################################### 

3.3 分代(generation)回收

这一策略的基本假设是:存活时间越久的对象,越不可能在后面的程序中变成垃圾。

• Python将所有的对象分为0,1,2三代。

• 所有的新建对象都是0代对象。

• 当某一代对象经历过垃圾回收,依然存活,那么它就被归入下一代对象。

• 垃圾回收启动时,一定会扫描所有的0代对象。

• 如果0代经过一定次数垃圾回收,那么就启动对0代和1代的扫描清理。

• 当1代也经历了一定次数的垃圾回收后,那么会启动对0,1,2,即对所有对象进行扫描

 ######################################### 

3.4 标记清除

标记-清除机制,顾名思义,首先标记对象(垃圾检测),然后清除垃圾(垃圾回收)。

主要用于解决循环引用。

1. 标记:活动(有被引用), 非活动(可被删除)

2. 清除:清除所有非活动的对象

 ######################################### 

4.0 python 缓冲池

4.1   整数对象缓冲池

对于[-5,256] 这样的小整数,系统已经初始化好,可以直接拿来用。而对于其他的大整数,系统则提 前申请了一块内存空间,等需要的时候在这上面创建大整数对象。

a = 200
b = 200
getrefcount(a)
5
getrefcount(b)
5
id(a)
140116833235072
id(b)
140116833235072
>>> a = 1
>>> b = 1
>>> getrefcount(a)
873
>>> getrefcount(b)
873
>>> id(a)
140596260116384
>>> id(b)
140596260116384
>>> a = 257
>>> b = 257
>>> getrefcount(a)
2
>>> getrefcount(b)
2
>>> id(a)
140596125739920
>>> id(b)
140596125740048

 #########################################

4.2  字符串缓存

为了检验两个引用指向同一个对象,我们可以用is关键字。is用于判断两个引用所指的对象是否相同,当触发缓存机制时,只是创造了新的引用,而不是对象本身。

 注意:

这对于经常使用的数字和字符串来说也是一种优化的方案

多个字符,含有特殊字符的字符串,不会放在驻留区




str1 = abc
str2 = abc
id(str1)
140116834714936
id(str2)
140116834714936
str1 is str2
True
str1 = abc 
str2 = abc 
str1 == str2
True
str1 is str2
False

单个字符是特殊字符可以放在驻留区


str1  = #
str2  = #
str1 is str2
True

单个字符重复20次可以在驻留区,重复21次就不在


str1 = a*20
str2 = a*20
str1 is str2
True
str1 = a*21
str2 = a*21
str1 is str2
False

 ######################################### 

4.3  python内存管理总结

引用计数为主,分代回收和标记清除为辅的垃圾回收方式进行内存回收管理

还对小整型缓冲池以及常用字符串驻留区的方式进行内存分配管理

 ######################################### 

5.0 深拷贝与浅拷贝

深浅拷贝主要是针对可变数据类型里面包含可变容器类型的情况

容器里面包容器,才有深浅拷贝的区别

 ######################################### 

1.浅拷贝只会拷贝第一层地址

d1
{'a': [1, 2], 'b': 2}
d2 = d1.copy()
d2
{'a': [1, 2], 'b': 2}
d1 is d2
False
d1['a']
[1, 2]
d1['a'].append(3)
d1
{'a': [1, 2, 3], 'b': 2}
d2
{'a': [1, 2, 3], 'b': 2}
id(di['a'])
Traceback (most recent call last):
File <stdin>, line 1, in <module>
NameError: name 'di' is not defined
id(d1['a'])
140116834121224
id(d2['a'])
140116834121224

 ######################################### 

2.深拷贝会拷贝每一层的值

只有这一种情况才是深拷贝

import copy

copy.deepcopy()



import copy

d5 = d3.copy()
d5
{'a': [1, 2, 3, 4], 'b': 2}
d6 = copy.deepcopy(d5)
d6
{'a': [1, 2, 3, 4], 'b': 2}
d5 is d6
False
d5['a'].append(5)
d5
{'a': [1, 2, 3, 4, 5], 'b': 2}
d6
{'a': [1, 2, 3, 4], 'b': 2}

 #########################################

  • 5
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值