Python 垃圾回收机制略解、交互语句

104 阅读6分钟

一、垃圾回收机制略解

垃圾回收机制(简称GC)是Python解释器自带的一种机制,专门用来回收不可用(引用次数为0)的变量值所占用的内存空间。

Python的GC模块主要运用了“引用计数”来跟踪和回收垃圾。在引用计数的基础上,还可以通过“标记-清除”解决容器对象可能产生的循环引用的问题,并且通过“分代回收”以空间换取时间的方式来进一步提高垃圾回收的效率。

  1. 引用计数

    #举例讲解概念
    
    x = 10  # 直接引用
    # y = x  # 直接引用 变量名直接关联变量值
    # z = x  # 直接引用 变量名直接关联变量值
    # x/y/z都分别绑定10的内存地址
    print(id(x))  # 140708674410432
    
    l = ['a', 'b', x]  # 间接引用
    print(id(l[2]))  # 140708674410432
    
    d = {'mmm': x}  # 间接引用
    print(id(d['mmm']))  # 140708674410432
    
    #举例讲解原理
    
    x=10
    l=['a','b',x]
    # 列表存储的实质是 l=['a'的内存地址,'b'的内存地址,10的内存地址]
    x=123
    print(l[2])  # 10
    
    #举例讲解原理
    
    x = 10
    print(id(x))  # 140708995274688
    print(id(10))  # 140708995274688
    
    x = 12
    print(id(x))  # 140708995274752
    print(id(12))  # 140708995274752
    
  2. 标记清除

    用来解决循环引用带来的内存泄露问题。

    # 循环引用=>导致内存泄露
    l1 = [111, ]
    l2 = [222, ]
    
    l1.append(l2)  # l1=[值111的内存地址,l2列表的内存地址]
    l2.append(l1)  # l2=[值222的内存地址,l1列表的内存地址]
    
    print(l1)  # [111, [222, [...]]]
    print(l2)  # [222, [111, [...]]]
    
    print(l1[1])  # [222, [111, [...]]]
    print(l2[1])  # [111, [222, [...]]]
    
    print(id(l1))  # 2619110589312
    print(id(l2[1]))  # 2619110589312
    
    print(id(l2))  # 2619109535616
    print(id(l1[1]))  # 2619109535616
    

    根据上述代码,l1与l2对列表直接引用,但两列表间又相互间接引用,此时若执行如下代码:

    del l1
    del l2
    

    此时,l1与l2对列表的直接引用被解除,列表也不能被访问,但两列表的引用计数却不为0,这种状况下会被标记清除机制识别为垃圾而被清除。

    #若只解除一个对应关系
    del l1
    

    此时,仍能根据l2访问到原l1对应列表里的数据,故两列表均不会被垃圾回收机制清除。

    判断是否能被访问到,是从栈区的变量名开始,顺着绑定关系线去堆区查找这条关系线对应的数据,若数据有间接引用线,也顺着去找被间接引用的数据,若数据可以被访问到则不会被认为成垃圾而清除。

直接引用与间接引用

​ 直接引用指的是从栈区出发直接引用到的内存地址。

​ 间接引用指的是从栈区出发引用到堆区后,再通过进一步引用才能到达的内存地址。

​ 如:

l2 = [20, 30]  # 列表本身被变量名l2直接引用,包含的元素被列表间接引用
x = 10  # 值10被变量名x直接引用
l1 = [x, l2]  # 列表本身被变量名l1直接引用,包含的元素被列表间接引用

图解如下: img 参考文章

  1. 分代回收

    用来降低引用计数的扫描频率,提升垃圾回收的效率。

    分代回收的核心思想是:在历经多次扫描的情况下,都没有被回收的变量,GC机制就会认为,该变量是常用变量,GC对其扫描的频率会降低。

二、交互语句

  1. 接收用户输入

    python 3:input——且会将用户输入的所有内容都存成字符串类型

    name = input("请输入您的用户名:")
    print(name, type(name))
    
    age = input("请输入的你的年龄: ")
    # 输入18
    print(age, type(age))  # 18,str
    age=int(age)  # int只能将纯数字(整数)的字符串转成整型
    print(age > 16)  #True
    

    python 2.7(了解):

    ​ raw_input——用法与python3的input一模一样 ​ input——要求用户必须输入一个明确的数据类型,输入的是什么类型,就存成什么类型。

  2. 字符串格式化输出

    %:值按照位置与%s一一对应,少一个不行,多一个也不行。
    res = "my name is %s my age is %s" %('Python',"30")
    print(res)
    # my name is Python my age is 30
    
    res = "my name is %s my age is %s" %("30",'Python')
    print(res)
    # my name is 30 my age is Python
    
    res = "my name is %s" %"Python"
    print(res)
    # my name is Python
    

    以字典的形式传值,打破位置对应的限制。

    res = "我的名字是 %(name)s 我的年龄是 %(age)s" %{"age":"30","name":'Python'}
    print(res)
    # 我的名字是 Python 我的年龄是 30
    

    %s可以接收任意类型

    print('my age is %s' %18)
    # my age is 18
    print('my age is %s' % "Hello")
    # my age is Hello
    print('my age is %s' %[1,23])
    # my age is [1, 23]
    print('my age is %s' %{'a':333})
    # my age is {'a': 333}
    

    %d只能接收int

    print('my age is %d' %18)  # %d只能接收int
    # my age is 18
    print('my age is %d' %"18")  #报错
    
    str.format:兼容性好。

    按位置传值:

    res = '我的名字是 {} 我的年龄是 {}'.format('JL',19)
    print(res)
    # 我的名字是 JL 我的年龄是 19
    
    res='我的名字是 {0}{0}{0} 我的年龄是 {1}{1}'.format('JL',19)
    print(res)
    # 我的名字是 JLJLJL 我的年龄是 1919
    

    打破位置的限制,按照key=value传值:

    res="我的名字是 {name} 我的年龄是 {age}".format(age=19,name='JL')
    print(res)
    # 我的名字是 JL 我的年龄是 19
    
    f' ':3.5版本推出,低版本不支持。
    x = input('your name: ')
    y = input('your age: ')
    res = f'我的名字是{x} 我的年龄是{y}'
    print(res)
    
    #了解f的新用法:{}内的字符串可以被当做表达式运行
    res=f'{10+3}'
    print(res)  # 13
    f'{print("aaaa")}'  # aaaa