Python 面向对象 内存管理机制

本文深入解析Python中的内存管理机制,包括对象存储、引用计数、垃圾回收原理及循环引用的解决策略,帮助读者理解Python如何高效管理内存资源。
部署运行你感兴趣的模型镜像

存储方面

  • 在 python 中万物皆对象,不存在基本数据类型
  • 所有数据类型,都会在内存中开辟一块空间进行存储。会根据不同的类型以及内容,开辟不同的空间大小进行存储。
    返回该空间的地址给外界接收(称为‘引用’),用于后续对这个对象的操作,
    可以通过id()函数获取内存地址(10进制)
    通过hex()函数可以查看对应的16进制地址
class Person(object):
    pass

p = Person()
print(p)
print(id(p))
print(hex(id(p)))

<main.Person object at 0x03DECE20>
64933408
0x3dece20

  • 对于整数和短小的字符,python会进行缓存,不会创建多个相同对象
num1 = 2
num2 = 2
print(id(num1), id(num2))

1430255552 1430255552

  • 容器对象,存储的其他对象,仅仅是其他对象的引用,并不是其他对象本身

垃圾回收方面

引用计数器

概念

一个对象,会记录着自身被引用的个数,每增加一个引用,这个对象的引用计数会自动+1,每减少一个引用,这个对象的引用计数会自动-1

查看引用计数:

import sys
sys.getrefcount(对象)#注意会大一
import sys

class Person(object):
    pass

p1 = Person() #1
print(sys.getrefcount(p1))

p2 = p1 #2
print(sys.getrefcount(p1))

del p2 #1
print(sys.getrefcount(p1))

del p1

2
3
2

引用计数器加减场景

  • 引用计数+1:
    • 对象被创建 P1 = Person()
    • 对象被引用 p2 = p1
    • 对象被作为参数,传入到一个函数中,+2
    • 对象作为一个元素,存储在容器中 l = [p1]
import sys

class Person(object):
    pass

p = Person() # 1
print(sys.getrefcount(p))

def log(obj):
    print(sys.getrefcount(obj))

log(p)

2
4

  • 引用计数-1
    • 对象的 别名被显示销毁 del p1
    • 对象的别名被赋予新的对象 p1 = 123
    • 一个对象离开它的作用域,一个函数执行完毕时
    • 对象所在的容器被销毁,或从容器中删除对象

循环引用

import sys
import objgraph

# 内存管理机制 = 引用计数机制 + 垃圾回收机制
# objgraph.count() 可以查看垃圾回收器,跟踪的对象个数

class Person(object):
    pass

class Dog(object):
    pass

p = Person()
d = Dog()

print(objgraph.count('Person'))
print(objgraph.count('Dog'))

p.pet = d
d.master = p

del p
del d

print(objgraph.count('Person'))
print(objgraph.count('Dog'))

1
1
1
1

垃圾回收

主要作用:从经历过引用计数器机制任未被释放的对象中,找到循环引用,干掉相关对象

怎么找到循环引用?

  1. 收集所以的容器对象,通过一个双向链表进行引用。容器对象,可以引用其他对象的对象(列表,元组,字典)
  2. 针对于每一个容器对象,通过一个变量gc_refs来记录当前对应的引用计数
  3. 对于每一个容器对象,找到它引用的容器对象,并将这个容器对象的引用计数-1
  4. 经过步骤3之后,如果一个容器对象的引用计数为0,就代表可以被回收,肯定是循环引用导致它活到现在

分带回收

如果程序当中创建了很多个对象,而针对于每一个对象都要参与检测过程,则会非常的耗费性能,所以基于这个问题,产生了一种假设,越命大的对象,越长寿。假设一个对象10次检测都没干掉,那认定这个对象长寿,就减少检测频率
基于这种假设,设计了一套机制,分带回收。

  1. 默认一个对象被创建出来后,属于0代
  2. 如果经历这一代垃圾回收后,依然存活,则划分到下一代
  3. 垃圾回收周期顺序,0代垃圾回收一定次数,会触发0代和1代回收,1代垃圾回收一定次数,会触发2代回收

查看和设置相关参数:

import gc

print(gc.get_threshold())
gc.set_threshold(700, 10, 5)

垃圾回收时机

自动回收
触发条件:开启垃圾回收机制,达到了垃圾回收的阈值
gc.enable() 开启垃圾回收机制(默认开启)
gc.disable() 关闭垃圾回收机制
gc.isenabled() 判定是否开启

垃圾回收中,新增的对象的个数和释放的对象个数之差达到某个阈值
gc.get_threshold() 获取自动回收阈值
gc.set_threshold() 设置自动回收阈值

手动回收

import objgraph
import gc

class Person(object):
    pass

class Dog(object):
    pass

p = Person()
d = Dog()

p.pet = d
d.master = p

del p
del d

gc.collect()

print(objgraph.count('Person'))
print(objgraph.count('Dog'))

0
0

循环引用解决细节

弱引用

import objgraph
import gc
import weakref

class Person(object):
    def __del__(self):
        print('Person 对象被释放了')
    pass

class Dog(object):
    def __del__(self):
        print('Dog 对象被释放了')
    pass

p = Person()
d = Dog()

p.pet = d
d.master = weakref.ref(p)

del p
del d

# gc.collect()

print(objgraph.count('Person'))
print(objgraph.count('Dog'))

Person 对象被释放了
Dog 对象被释放了
0
0

删除关联

import objgraph
import gc
import weakref

class Person(object):
    def __del__(self):
        print('Person 对象被释放了')
    pass

class Dog(object):
    def __del__(self):
        print('Dog 对象被释放了')
    pass

p = Person()
d = Dog()

p.pet = d
d.master = p

p.pet = None
del p
del d

# gc.collect()

print(objgraph.count('Person'))
print(objgraph.count('Dog'))

Dog 对象被释放了
Person 对象被释放了
0
0

您可能感兴趣的与本文相关的镜像

Python3.9

Python3.9

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

### Python 面向对象编程中的内存管理Python面向对象编程中,内存管理主要依赖于垃圾回收机制以及引用计数技术。当创建一个`Animal`对象实例时,如: ```python a1 = Animal(2, 0, 10.0) ``` 此过程不仅分配了必要的内存空间给新对象,还初始化了该对象的数据成员[^1]。 #### 引用计数 每当有一个新的引用指向某个对象时,其内部的引用计数值就会增加;相反地,如果某处不再持有对该对象的引用,则相应的引用计数会减少。一旦任何对象的引用计数降为零——意味着没有任何活动的部分正在使用它,那么这个对象就成为了可被清理的目标[^2]。 对于上述例子中的`a1`对象而言,在正常情况下它的引用计数至少为1,因为存在变量`a1`直接引用着它。而像下面这样的局部作用域内的临时引用不会影响到实际对象生命周期之外的时间点上的引用计数变化情况: ```python def some_function(): temp_animal = a1 # 这里temp_animal是一个临时引用 # 它只在这个函数的作用范围内有效 ``` #### 垃圾收集器(GC) 除了简单的引用计数外,Python还有一个周期性的垃圾收集器用来处理循环引用的问题。所谓循环引用是指两个或更多对象之间相互保持对方作为自己的属性或其他形式的一部分,从而使得它们各自的引用计数永远不会降到零的情况发生。例如: ```python class Node: def __init__(self): self.next_node = None node_a = Node() node_b = Node() node_a.next_node = node_b node_b.next_node = node_a # 形成环形结构 ``` 在这种情形下,即使外部已经不存在其他地方再访问这两个节点了,但由于彼此间的交叉引用关系仍然维持着非零状态下的引用数目,所以单纯依靠引用计数无法自动释放这些资源。这时就需要借助Python自带的垃圾收集模块gc来进行额外的扫描和清除工作[^3]。 #### 私有属性与内存保护 值得注意的是,虽然可以通过特殊方式访问私有属性(如`_ClassName__private_attr_name`),但这并不推荐也不应该成为常规做法。这样做可能会破坏封装原则并引发潜在的安全隐患或者难以预料的行为改变。因此应当遵循良好的编码实践,仅限于公开接口去操作对象的状态信息[^4]。 ```python print('a1体重:{0:0.2f}'.format(a1._Animal__weight)) ``` 尽管这段代码能够成功读取到原本设定为私有的权重字段值,但从长远来看不利于维护清晰合理的程序逻辑边界划分。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值