Python的内存管理,引用计数、垃圾回收、内存池机制、深copy,浅copy

     

目录

1、引用计数

1.1  引用计数器原理

1.2  循环引用的情况

 1.3  优缺点

2、垃圾回收

2.1  回收原则

2.2  gc机制

2.3  效率问题

2.4  三种情况触发垃圾回收

 2.5  分代(generation)回收

2.6  标记清除

3、内存池机制

3.1  整数对象缓冲池

3.2  字符串驻留区

4、深copy,浅copy


        以引用计数为主,分代回收,标记清除为辅的垃圾回收方式进行内存回收,引入小整数缓冲池和常见简单字符串驻留区的内存缓存池机制。

1、引用计数

        1、Python动态类型:引用和对象分离,是动态类型的核心。

        2、Python每个对象都维护一个引用技术字段,记录该对象被引用的次数。

        3、减少引用计数:
                        del删除或重新引用时,引用计数会变化(del只是删除引用)

1.1  引用计数器原理

        每个对象维护一个 ob_ref 字段,用来记录该对象当前被引用的次数,每当新的引用指向该对象时,它的引用计数ob_ref加1,每当该对象的引用失效时计数ob_ref减1 一旦对象的引用计数为0,该对象可以被回收,对象占用的内存空间将被释放。
        它的缺点是需要额外的空间维护引用计数,这个问题是其次的,最主要的问题是它不能解决对象的“循环引用”。

>>> from sys import getrefcount
>>> a = 500
>>> getrefcount(a)  临时引用a,用完即减一
2
>>> b = a
>>> getrefcount(a)
3
>>> lst = [a]
>>> getrefcount(a)
4

1.2  循环引用的情况

        x = []
        y = []
        x.append(y)
        y.append(x)

对于上面相互引用的情况,如果不存在其他对象对他们的引用,这两个对象,所占用的内存也还是无法回收,从而导致内存泄漏。

 1.3  优缺点

        引用计数机制的优点:

  •                 简单
  •                 实时性

        引用计数机制的缺点: 

  • 维护引用计数消耗资源
  • 循环引用时,无法回收。

2、垃圾回收

2.1  回收原则

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

2.2  gc机制

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

        找到内存中无用的垃圾资源,清除这些垃圾并把内存让出来给其他对象使用。 GC彻底把程序员从资源管理的重担中解放出来,让他们有更多的时间放在业务逻辑上。

2.3  效率问题

        垃圾回收时,Python不能进行其它的任务。频繁的垃圾回收将大大降低Python的工作效率。 当Python运行时,会记录其中分配对象(object allocation)和取消分配对象(object deallocation)的 次数。当两者的差值高于某个阈值时,垃圾回收才会启动。

import gc
print(gc.get_threshold())

输出:
(700, 10, 10)

2.4  三种情况触发垃圾回收

  • 调用gc.collect()
  • GC达到阀值时
  • 程序退出时

 2.5  分代(generation)回收

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

        Python将所有的对象分为0,1,2三代,所有的新建对象都是0代对象,当某一代对象经历过垃圾回收,依然存活,那么它就被归入下一代对象。

        垃圾回收启动时,一定会扫描所有的0代对象。 如果0代经过一定次数垃圾回收,那么就启动对0代和1代的扫描清理。 当1代也经历了一定次数的垃圾回收后,那么会启动对0,1,2,即对所有对象进行扫描。

2.6  标记清除

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

        主要用于解决循环引用

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

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

3、内存池机制

3.1  整数对象缓冲池

        对于[-5,256] 这样的小整数,系统已经初始化好,可以直接拿来用,避免为整数频繁申请和销毁内存空间小整数池目的:节省内存,提高执行效率。

        而对于其他的大整数,系统则提 前申请了一块内存空间,等需要的时候在这上面创建大整数对象。终端是每次执行一次,所以每次的大整数都重新创建,而在pycharm中,每次运行是所有代码都加载都内存中。

3.2  字符串驻留区

        字符串的intern机制 python对于短小的,只含有字母数字的字符串自动触发缓存机制。其他情况不会缓存。

        符合条件则放到驻留区,输入字符串会先去驻留区寻找,可以用is判断。

        条件:
        多个字符包涵特殊字符,不会放入驻留区,单个字符包涵特殊字符可以放入驻留区。

>>> str1 ="acb"
>>> str2 ="acb"
>>> str2 is str1
True
>>> str2 ="acb "
>>> str1 ="acb "
>>> str2 is str1
False
>>> str2 = "a"*20
>>> str1 = "a"*20
>>> str2 is str1
True
>>> str1 = "a"*21
>>> str2 = "a"*21
>>> str2 is str1
False
>>> str4 = "a#"
>>> str6 = "a#"
>>> str4 is str6
False
>>> str6 = "#"
>>> str4 = "#"
>>> str4 is str6
True

4、深copy,浅copy

        d2 = d1.copy()  浅拷贝,拷贝的是地址,只拷贝第一层地址。

        (import copy)  copy.deepcopy() 深拷贝,拷贝每一层的值,拷贝之后的容器不会影响原来的容器,深拷贝只有这一种情况。

        容器内包涵可变数据类型容器的时候,才会有深浅拷贝的区别。

 参考文章:

浅拷贝与深拷贝的区别(详解)_Mark sheng的博客-CSDN博客_浅拷贝深拷贝

python可变数据类型和不可变数据类型_宇宙无敌天下第一帅的博客-CSDN博客_python 可变数据类型

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很抱歉,我之前的回答中有误。`Tkinter.PhotoImage` 对象没有提供 `copy()` 方法,因此我们需要使用其他方法来避免内存泄漏问题。一种常见的做法是将 `tk_image` 对象存储到一个列表中,然后在不需要它时将其从列表中移除,从而使其引用计数减少。 以下是修改后的代码示例: ```python def add_image(self, file_path): if file_path.endswith(".gif") or file_path.endswith(".jpg") or file_path.endswith(".png"): try: image = Image.open(file_path) # 缩放到指定尺寸 if not(file_path.endswith(".gif")): image.save(file_path[ : file_path.rfind('.')]+'.gif') image = Image.open(file_path[ : file_path.rfind('.')]+'.gif') new_img = image.resize((760, 430)) tk_image = ImageTk.PhotoImage(new_img) self.label.image_create(tk.INSERT, image=tk_image) self.label.pack() image_box = Label(root) image_box.image = tk_image image_box.pack() image_tag = f'<img src="{file_path}">\n' self.label.insert(tk.INSERT, image_tag) self.label.pack() # 将tk_image对象存储到列表中 self.images.append(tk_image) except Exception as e: print(f"{e}") tk.messagebox.showerror('Error', '无法打开此图片!') def delete_images(self): # 删除所有存储的tk_image对象 for image in self.images: del image self.images = [] ``` 在这个修改后的代码中,我们使用一个名为 `self.images` 的列表来存储所有的 `tk_image` 对象。当不需要这些对象时,我们可以通过 `del image` 来删除它们,并将 `self.images` 列表重置为空列表。这样,我们就能避免内存泄漏问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值