ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

022-Python对象的生命周期和周期方法

2022-02-20 12:03:13  阅读:188  来源: 互联网

标签:__ 生命周期 Python 回收 对象 del 引用 022 print


话不多说,上代码,看结果。



print('1、概念#############################################')
# 生命周期
# 指的是一个对象, 从诞生到消亡的过程
# 当一个对象被创建时, 会在内存中分配相应的内存空间进行存储
# 当这个对象不再使用, 为了节约内存, 就会把这个对象释放

# 涉及问题
# 如何监听一个对象的生命过程?
# Python是如何掌控一个对象的生命?

# 监听对象生命周期
# __new__方法 创建一个对象时, 用于给这个对象分配内存的方法
# 通过拦截这个方法, 可以修改对象的创建过程  比如:单例设计模式
# __init__方法
# 每个对象实例化的时候,都会自动执行这个方法 可以在这个方法里面,初始化一些实例属性
# __del__方法
# 当对象被释放的时候调用这个方法  可用于在这个方法中清理资源


class A:
    def __init__(self):
        print('init')
        self.aa = 12

    def __del__(self):
        print('del')


a = A()
print(a)
print(a.aa)
del a
print('class B#####')


class B:
    def __new__(cls, *args, **kwargs):
        print('拦截')

    def __init__(self):
        print('init')
        self.bb = 12

    def __del__(self):
        print('del')


b = B()
print(b)

# print(b.bb)  # AttributeError: 'NoneType' object has no attribute 'bb'

# 内存管理机制
# 存储方面
# 1. 在Python中万物皆对象
# 不存在基本数据类型  0,  1.2,  True, False, "abc"  这些全都是对象
# 2. 所有对象, 都会在内存中开辟一块空间进行存储
# 会根据不同的类型以及内容, 开辟不同的空间大小进行存储
# 返回该空间的地址给外界接收(称为"引用"), 用于后续对这个对象的操作 可通过id()函数获取内存地址(10进制) 通过hex()函数可以查看对应的16进制地址
# 3. 对于整数和短小的字符, Python会进行缓存; 不会创建多个相同对象  此时, 被多次赋值, 只会有多份引用
# 4. 容器对象, 存储的其他对象, 仅仅是其他对象的引用, 并不是其他对象本身
# 比如字典, 列表, 元组这些"容器对象"  全局变量是由一个大字典进行引用  global()查看

print('class C#####')


class C:
    pass


c1 = C()
c2 = C()
print(id(c1), id(c2))

c3 = 12
c4 = 12
print(id(c3), id(c4))
# 垃圾回收方面
# 引用计数器
# 概念
# 一个对象, 会记录着自身被引用的个数
# 每增加一个引用, 这个对象的引用计数会自动+1
# 每减少一个引用, 这个对象的引用计数会自动-1
# 举例
# 引用计数+1场景
# 对象被创建  p1 = Person()
# 对象被引用  p2 = p1
# 对象被作为参数,传入到一个函数中 log(p1)  这里注意会+2, 因为内部有两个属性引用着这个参数
# 对象作为一个元素,存储在容器中 l = [p1]
# 引用计数-1场景
# 对象的别名被显式销毁  del p1
# 对象的别名被赋予新的对象  p1 = 123
# 一个对象离开它的作用域  一个函数执行完毕时 内部的局部变量关联的对象, 它的引用计数就会-1
# 对象所在的容器被销毁,或从容器中删除对象
# 查看引用计数
# import sys   sys.getrefcount(对象)  注意会大一
print('class D########')
import sys


class D:
    pass


d1 = D()
d3 = D()
print(sys.getrefcount(d1))  # 1
d2 = d1
print(sys.getrefcount(d1))  # 2
del d2
print(sys.getrefcount(d1))  # 1

print('def E########')


def E(obj):
    print(sys.getrefcount(obj))  # 2


e1 = E(d1)
e2 = [d1]
print(sys.getrefcount(d1))  # 3


# 内存管理机制 = 引用计数器机制 + 垃圾回收机制
# 当一个对象, 如果被引用 + 1, 删除一个引用 : -1 0: 被自动释放
# 循环引用 容易造成内存泄漏
# objgraph
# objgraph.count() 可以查看, 垃圾回收器, 跟踪的对象个数
print('class F########')
import objgraph


class F:
    pass


class G:
    pass


f = F()
g = G()
print(objgraph.count('F'), objgraph.count('G'))
print()
f.f1 = g
g.g1 = f

del f
del g
print(objgraph.count('F'), objgraph.count('G'))


# 垃圾回收
# 主要作用  从经历过"引用计数器机制"仍未被释放的对象中, 找到"循环引用", 干掉相关对象
# 底层机制(了解&难)

# 怎样找到"循环引用"?
# 1. 收集所有的"容器对象", 通过一个双向链表进行引用
# 容器对象  可以引用其他对象的对象 列表、元组、字典、自定义类对象...
# 非容器对象  不能引用其他对象的对象  数值、字符串、布尔...  注意: 针对于这些对象的内存, 有其他的管理机制
# 2. 针对于每一个"容器对象", 通过一个变量gc_refs来记录当前对应的引用计数
# 3. 对于每个"容器对象",找到它引用的"容器对象", 并将这个"容器对象"的引用计数 -1
# 4. 经过步骤3之后, 如果一个"容器对象"的引用计数为0, 就代表这玩意可以被回收了, 肯定是"循环引用"导致它活到现在的

# 如何提升查找"循环引用"的性能?
# 如果程序当中创建了很多个对象, 而针对于每一个对象都要参与"检测"过程; 则会非常的耗费性能
# 所以, 基于这个问题, 产生了一种假设:  越命大的对象, 越长寿 假设一个对象10次检测都没给它干掉, 那认定这个对象一定很长寿, 就减少这货的"检测频率"

# 基于这种假设, 设计了一套机制
# 分代回收
# 机制
# 1. 默认一个对象被创建出来后, 属于 0 代
# 2. 如果经历过这一代"垃圾回收"后, 依然存活, 则划分到下一代
# 3. "垃圾回收"的周期顺序为  0代"垃圾回收"一定次数, 会触发 0代和1代回收
# 1代"垃圾回收"一定次数, 会触发0代, 1代和2代回收
# 查看和设置相关参数  import gc  print(gc.get_threshold())  gc.set_threshold(700, 10, 5)
# 垃圾回收器当中, 新增的对象个数-消亡的对象个数 , 达到一定的阈值时, 才会触发, 垃圾检测

# 垃圾回收时机(掌握&简单)
# 1. 自动回收
# 触发条件
# 开启垃圾回收机制
# gc.enable()  开启垃圾回收机制(默认开启)
# gc.disable()  关闭垃圾回收机制
# gc.isenabled()   判定是否开启

# 达到了垃圾回收的阈值
# 垃圾回收器中, 新增的对象个数和释放的对象个数之差到达某个阈值
# 涉及方法
# gc.get_threshold()  获取自动回收阈值
# gc.set_threshold()  设置自动回收阈值
# 2. 手动回收
# 触发条件
# gc.collect()  执行一次垃圾回收(开关状态无效)

# 特殊场景
# 场景条件  Python2.x版本下, 循环引用, 并且有一个对象都实现了__del__方法
# 概念  两个对象互相引用对象, 谁的引用计数都是1
# 导致结果  无法释放, 进行内存回收
# 底层原因  无法判别先释放哪个对象, 调用哪一个del方法
# 解决思路
# 手动解除循环引用
# 方式1
# 预防  尽可能避免循环引用产生
# 实现  一方使用弱引用代替
# 方式2
# 治疗  在循环引用的产生的前提下
# 实现  当删除一个引用, 确定以后不再使用时, 手动清空对其他容器对象的引用

# 测量对象的引用个数
# 辅助工具  objgraph

结果如下图。
在这里插入图片描述
就先这样,遇到别的再补充。

标签:__,生命周期,Python,回收,对象,del,引用,022,print
来源: https://blog.csdn.net/weixin_48013634/article/details/123028948

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有