Python类对象的生命周期与内存管理机制

2020-03-27  本文已影响0人  摸着石头过河_崖边树

一、类对象的生命周期

什么是类对象的生命周期?
就是从对象创建 ----> 对象使用 ----> 对象销毁
废话少说,我们直接上代码来看

class Person(object):
   # 1.可以拦截对象的创建
   def __new__(cls, *args, **kwargs):
        print('__new__方法调用')
       return super(Person, cls).__new__(cls, *args, **kwargs)

   # 2.创建对象完成后会自动调用这个方法,并把实例传递给init方法
    def __init__(self):
       print('__init__初始化方法')
       self.name = 'zb'

   # 3.对象释放的时候自动调用
   def __del__(self):
       print('__del__对象释放')
 
  p = Person()  # 创建对象
  del p      # 删除对象

结果输出

__new__方法调用
__init__初始化方法
 __del__对象释放

由此可见
创建对象时候先后调用new --> init
删除对象的时候 调用del

二、内存管理机制:

介绍内存管理之前我们先熟悉几个函数,之后我们会用到

 print(id(p))   # 打印内存地址 10进制
 print(hex(id(p)))  # 打印内存地址 16进制
 print(sys.getrefcount(p))  #查看对象的引用计数器的值

python 是万物皆对象,所有基本数据类型都是对象,但是常用数据类型的对象地址相同

 num1 = 2
 num2 = 2
 print(hex(id(num1)), hex(id(num2)))
 结果:0x1097fd070     0x1097fd070

内存管理包括2个机制并存引用计数器机制(性能高) + 垃圾回收机制(性能低,但是能解决循环引用问题)

2.1 引用计数器:计算对象被引用的次数是+1 取消引用-1

 import sys
class Person(object):
      pass
 p1 = Person()  # 引用计数器 = 1
 print(sys.getrefcount(p1))

 p2 = p1      # 引用计数器 = 2
 print(sys.getrefcount(p1))

 del p1    # 引用计数器 = 1

 del p2    # 引用计数器 = 1
 
//  结果:2
         3

注意:sys.getrefcount(p1)函数会自动将p1的引用计数器+ 1, 所以计算的时候要-1 引用计数 0 表示对象会被销毁

2.1.1 引用计数器+1 4个场景
A. 创建对象的时候 + 1; p = Person();
B . 对象赋值的时候 + 1; p2 = p
C. 对象作为函数的参数 + 2; func(p) 函数里面有2个引用
D. 对象作为某个对象的容器对象 + 1 ; a = [p]

2.1.2 引用计数器-1 4个场景
A. 对象被删除 -1; del p
B. 对象被重新赋值 -1; p = 123
C. 函数执行完毕,离开作用域-1;
D. 针对对象的容器对象的销毁 -1; del a

2.2 垃圾回收机制
引用计数机制虽然可以管理内存,但是不能解决循环引用问题,于是引用垃圾回收机制

  objgraph.count('Person')   # 查看类对象引用个数

垃圾回收机制底层原理

  # 1、收集所有的"容器对象"(列表、字典、元祖、自定义对象),通过双向链表(集合)进行引用
  # 2、针对每一个"容器对象",通过一个变量gc_refs来记录当前的引用计数器
  # 3、对象每个'容器对象',找到他引用的'容器对象',并将这个'容器对象'的引用计数器 -1
  # 4、经过步骤3之后,如果一个'容器对象'的引用计数器未0 就代表这个东西可以被回收啦,肯定是循环引用导致的

垃圾回收机制底层优化:分代回收 (优化垃圾回收性能)

垃圾检测触发机制:垃圾回收器 新增的对象个数 - 消亡的对象 达到一定的阈值才会触发垃圾回收

   # 阈值设置
  import gc
  print(gc.get_threshold())
   # (700, 10, 10)  默认当阈值大于700 检测一次   大于10 1代检测   大于10 2代加测
  # 设置垃圾检测
  gc.set_threshold(1000, 5, 5)

2.2.1 垃圾回收触发时机
A. 自动触发 : 先开启机制 后设置阈值

 import gc
# 判断是否开启垃圾回收
isenable = gc.isenabled()
if isenable == False:
      # 1. 开启回收
      gc.enable() 
#设置阈值
gc.set_threshold(500, 10, 10)

B.手动触发 : 解决循环引用

import objgraph   # 引用计数器count
import gc         # 垃圾回收机制
import weakref    # 弱引用
class Person(object):
    def __del__(self):   // 实现了del 不能自动回收
          print('Person对象被释放啦')

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

p = Person()
d = Dog()
# 循环引用
p.pet = d
d.master = p
 # d.master = weakref.ref(p)  #解决循环引用方式一 弱引用的应用
# p.pet = None     # 解决循环引用方式二 指向None
del p
del d
# 解决循环引用方式三 垃圾回收机制手动回收
# gc.collect(1)
print(objgraph.count('Person'))
print(objgraph.count('Dog'))

总结

解决循环引用方案 :

1、weakref 弱引用 - 一个对象的弱引用 一对多的引用用需要弱引用字典
2、指向None 置空对象
3、 gc.collect(1) 垃圾回收机制回收

最后赠言

学无止境,学习Python的伙伴可以多多交流。

上一篇 下一篇

猜你喜欢

热点阅读