概念
- 生命周期
- 一个对象,从诞生到消亡的过程
- 一个对象被创建时,会在内存中分配相应的内存空间进行存储
- 当对象不再使用,为了节约内存,就会把这个对象释放掉
- 涉及问题
- 如何监听一个对象的生命过程?
- python如何掌握一个对象的生命?
- 监听对象的生命周期
- __new__方法
- 当我们创建一个对象时,用于给这个对象分配内存的方法
- 通过拦截这个方法,可以修改对象的创建过程。
- 比如:单例设计模式
- __init__方法
- __del__方法
- __new__方法
__new__方法
class Person:
def __new__(cls, *args, **kwargs):
print("新建了一个对象,被拦截了")
# def __init__(self):
# print("初始化方法")
# self.name = "sz"
#
# def __del__(self):
# print("对象被释放了")
#
pass
p = Person()
# del p
print(p)
# print(p.name)
覆盖了__new__方法
未覆盖__new__方法
__init__和__del__方法
class Person:
# def __new__(cls, *args, **kwargs):
# print("新建了一个对象,被拦截了")
def __init__(self):
print("初始化方法")
self.name = "sz"
def __del__(self):
print("对象被释放了")
pass
p = Person()
# 调用del会释放对象
# del p
print(p)
print(p.name)
小案例:统计对象的个数
# Person,打印一下,当前这个时刻,由Person类,产生的实例,有多少个
# 创建了一个实例,计数+1,删除一个实例,计数-1
# 方案1:使用全局变量personCount
# 方案2:使用类属性personCount
# 方案3:使用类隐藏属性__personCount
# personCount = 0
class Person:
__personCount = 0
def __init__(self):
# global personCount
print("计数:+1")
Person.__personCount += 1
def __del__(self):
# global personCount
print("计数:-1")
self.__class__.__personCount -= 1
# @staticmethod
@classmethod
def log(cls):
print("当前人的个数: ", cls.__personCount)
Person.personCount = 100
# personCount = 100
p = Person()
p1 = Person()
Person.log()
del p
Person.log()
- 内存管理机制
- 存储方面
- 1.在python中万物皆对象
- 不存在基本数据类型,全部是对象
- 2.所有对象都会在内存中开辟一块空间进行存储
- 会根据不同的类型和内容,开辟不同大小的内存储存空间
- 返回该空间的地址给外界接收("引用"),用于后续对这个对象的操作
- 通过id()函数查看内存地址(十进制)
- 通过hex()函数十六进制
- 3.对于整数和短小的字符,python会进行缓存;不会创建多个相同的对象
- 4.容器对象,存储的其他对象,仅仅是其他对象的引用,并不是其他对象本身
- 1.在python中万物皆对象
- 垃圾回收方面
- 引用计数器
- 概念
- 一个对象,会记录自身被引用的个数
- 每增加一个引用,这个对象的引用计数会自动+1
- 每减少一个引用,这个对象的引用计数会自动-1
- 举例
- 引用计数+1的场景
- 对象被创建
- 对象被引用
- 对象被作为参数传入到函数中
- 对象作为一个元素存储在容器中
- 引用计数-1的场景
- 对象的别名被显示销毁
- 对象的别名被赋予新的对象
- 一个对象离开它的作用域
- 一个函数执行完毕时
- 内部的局部变量关联的对象
- 对象所在的容器被销毁,或从容器中删除对象
- 引用计数+1的场景
- 查看引用计数
- 概念
- 垃圾回收
- 主要作用
- 从经历过“引用计数器机制”仍未被释放的对象中,找到“循环引用”,干掉相关对象
- 底层机制(了解&难)
- 怎样找到“循环引用”?
- 1.收集所有的“容器对象”,通过一个双向链表进行引用
- 容器对象:可以引用其他对象的对象(比如:字典,元组,列表,自定义对象等等)
- 非容器对象:
- 2.针对于每一个“容器对象”,通过一个变量gc_refs来记录当前对应的引用计数
- 3.对于每个“容器对象”,找到它引用的“容器对象”,并将这个“容器对象”的引用计数-1
- 4.经过步骤3之后,如果一个“容器对象”的引用计数为0,就代表这玩意可以被回收了,肯定是“循环引用”导致它活到现在的
- 1.收集所有的“容器对象”,通过一个双向链表进行引用
- 如何提升查找“循环引用”的性能?
- 如果程序中创建了很多个对象,而针对于每一个对象都要参与检测过程,会非常的耗费性能
- 基于这个问题,产生了一种假设
- 越命大的对象,越长寿
- 假设一个对象经过10次检测都没有被干掉,那么认定这个对象很长寿,就减少这个对象的检测频率
- 基于这种假设,设计了一套机制
- 分代回收
- 机制:
- 1.默认一个对象被创建出来后,属于0代
- 2.如果经历过这一代“垃圾回收”后,依然存活,则划分到下一代
- 3.“垃圾回收”周期顺序
- 0代“垃圾回收”一定次数,会触发0代和1代回收
- 1代“垃圾回收”一定次数,会触发0代、1代和2代回收
- 查看和设置相关参数
- import gc
- print(gc.get_threshold())
- gc.set_threshold(700,10,5)
- 机制:
- 垃圾回收器中,新增对象个数-消亡对象个数,达到一定的阈值,才会触发垃圾检测
- 分代回收
- 怎样找到“循环引用”?
- 垃圾回收时机(掌握&简单)
- 自动回收
- 触发条件
- 开启垃圾回收机制(默认开启)并且 达到垃圾回收的阈值
- 触发条件
- 手工回收
- 自动回收
- 主要作用
- 特殊场景
- 引用计数器
- 测量对象的引用个数
- 存储方面
对象在内存中的储存
class Person:
pass
p = Person()
print(p)
print(id(p))
print(hex(id(p)))
p2 = Person()
print(id(p), id(p2))
num1 = 2
num2 = 2
# 对于整数和短小的字符,python会进行缓存,不会创建多个相同的对象
print(id(num1), id(num2))
str1 = 'A'
str2 = 'A'
print(id(str1), id(str2))
引用计数器
# 引用技术器+1 的情况
import sys
class Person:
pass
# 1.对象被创建
p1 = Person()
# 2.对像作为函数参数
print(sys.getrefcount(p1))
# 对像作为函数参数
# def log(obj):
# print(sys.getrefcount(obj))
#
#
# log(p1)
#
# # dir()可以打印对象所有属性
# # getattr() 获取所有的属性的值
# for attr in dir(log):
# print(attr, getattr(log, attr))
# 3.对象被引用
p2 = p1
print(sys.getrefcount(p2))
# del p2
# print(sys.getrefcount(p1))
# del p1
# print(sys.getrefcount(p1))
# 4.对象作为一个元素,存储在容器中
l = [p1]
print(sys.getrefcount(p1))
引用计数器-特殊场景-循环引用问题
# 内存管理机制:引用计数器机制,垃圾回收机制
# 当一个对象,如果被引用+1,删除一个引用:-1,0:被自动释放
# 循环引用
# objgraph.count() 可以查看,垃圾回收器,跟踪的对象个数
import objgraph
class Person:
pass
class Dog:
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.默认一个对象被创建出来后,属于0代
# 2.如果经历过这一代“垃圾回收”后,依然存活,则划分到下一代
# 3.“垃圾回收”周期顺序为:
# ---- 0代“垃圾回收”一定次数,会触发0代和1代回收
# ---- 1代“垃圾回收”一定次数,会触发0代,1代和2代回收
# 垃圾回收当中,新增对象个数-消亡的对象个数,达到一定的阈值,才会触发,垃圾检测
# 自动回收-1.开启垃圾回收机制(默认开启);2.达到垃圾回收阈值(新增对象个数-消亡的对象个数,达到一定的阈值)
import gc
# 第一个是阈值(新增对象个数-消亡的对象个数),第二个次数(0代“垃圾回收”一定次数),第三个次数(1代“垃圾回收”一定次数)
print(gc.get_threshold())
# 可以设置垃圾回收的阈值
# gc.set_threshold(500, 5, 5)
# 判断垃圾回收机制是否开启状态
print(gc.isenabled())
# 关闭垃圾回收机制
gc.disable()
print(gc.isenabled())
# 开启垃圾回收机制
gc.enable()
print(gc.isenabled())
垃圾回收机制-分代回收(手动回收)
import objgraph
import gc
# 弱引用
import weakref
class Person:
# 在python2.x中会导致无法回收“循环引用”的对象
def __del__(self):
print("Person对象,被释放了")
pass
class Dog:
def __del__(self):
print("Dog对象,被释放了")
pass
p = Person()
d = Dog()
# 循环引用,计数器机制无法释放;需要垃圾回收机制进行释放
# 3.多个循环引用的破除,弱字典
# p.pets = weakref.WeakKeyDictionary({"dog": d1, "cat": c1})
p.pet = d
# # 1.弱引用,导致无法形成“循环引用”
# # d.master = weakref.ref(p)
d.master = p
# 2.破除“循环引用”
# p.pet = None
del p
del d
# 垃圾回收,释放“循环引用”导致的内存
gc.collect()
print(objgraph.count("Person"))
print(objgraph.count("Dog"))
学习链接: 【Python】零基础入门 面向对象编程(强烈推荐)
标签:__,生命周期,Person,python,回收,面向对象,对象,引用,print From: https://www.cnblogs.com/amanzhj/p/17809249.html