欢迎来到DIVCSS5查找CSS资料与学习DIV CSS布局技术!
  垃圾回收是Python自带的机制,用于自动释放不会再用到的内存空间;
 
  什么是内存泄漏呢?
 
  内存泄漏,并不是说你的内存出现了信息安全问题,被恶意程序利用了,而是指程序本身没有设计好,导致程序未能释放已不再使用的内存。
 
  内存泄漏也不是指你的内存在物理上消失了,而是意味着代码在分配了某段内存后,因为设计错误,失去了对这段内存的控制,从而造成了内存的浪费。
 
  计数引用
 
  Python中一切皆对象。当这个对象的引用计数(指针数)为0的时候,说明这个对象永不可达,自然它也就成为了垃圾,需要被回收。
 
  例:
 
  #显示当前python程序占用的内存大小
 
  defshow_memory_info(hint):
 
  pid=os.getpid()
 
  p=psutil.Process(pid)
 
  info=p.memory_full_info()
 
  memory=info.uss/1024./1024
 
  print('{}memoryused:{}MB'.format(hint,memory))
 
  deffunc():
 
  show_memory_info('initial')
 
  a=[iforiinrange(10000000)]
 
  show_memory_info('afteracreated')
 
  func()
 
  show_memory_info('finished')
 
  ##########输出##########
 
  #initialmemoryused:6.62890625MB
 
  #afteracreatedmemoryused:199.33203125MB
 
  #finishedmemoryused:7.6640625MB
 
  程序初始化时占的内存为6MB,接着创建了一个列表a,由于a还没被回收,因此占的内存升到了200MB,当函数返回后,a的引用计数为0,a被回收,内存又恢复到了7MB。
 
  如果把a变成全局变量,函数返回后,引用计数依然大于0,于是对象就不会被垃圾回收,依然占着大量的内存
 
  deffunc():
 
  show_memory_info('initial')
 
  globala
 
  a=[iforiinrange(10000000)]
 
  show_memory_info('afteracreated')
 
  func()
 
  show_memory_info('finished')
 
  ##########输出##########
 
  #initialmemoryused:6.67578125MB
 
  #afteracreatedmemoryused:199.30859375MB
 
  #finishedmemoryused:199.30859375MB
 
  或者把列表返回,在主程序中接收,引用依然存在,垃圾回收就不会被触发,大量内存仍然被占用着
 
  deffunc():
 
  show_memory_info('initial')
 
  a=[iforiinrange(10000000)]
 
  show_memory_info('afteracreated')
 
  returna
 
  a=func()
 
  show_memory_info('finished')
 
  ##########输出##########
 
  #initialmemoryused:6.6484375MB
 
  #afteracreatedmemoryused:199.2890625MB
 
  #finishedmemoryused:199.2890625MB
 
  看一下Python内部的引用计数机制
 
  importsys
 
  a=[]
 
  #两次引用,一次来自a,一次来自getrefcount
 
  print(sys.getrefcount(a))
 
  deffunc(a):
 
  #四次引用,a,python的函数调用栈,函数参数,和getrefcount
 
  print(sys.getrefcount(a))
 
  func(a)
 
  #两次引用,一次来自a,一次来自getrefcount,函数func调用已经不存在
 
  print(sys.getrefcount(a))
 
  ##########输出##########
 
  2
 
  4
 
  2
 
  sys.getrefcount()这个函数,可以查看一个变量的引用次数。这段代码本身应该很好理解,不过别忘了,getrefcount本身也会引入一次计数。另一个要注意的是,在函数调用发生的时候,会产生额外的两次引用,一次来自函数栈,另一个是函数参数。
 
  又如:
 
  importsys
 
  a=[]
 
  print(sys.getrefcount(a))#两次
 
  b=a
 
  print(sys.getrefcount(a))#三次
 
  c=b
 
  d=b
 
  e=c
 
  f=e
 
  g=d
 
  print(sys.getrefcount(a))#八次
 
  ##########输出##########
 
  2
 
  3
 
  8
 
  a、b、c、d、e、f、g这些变量全部指代的是同一个对象,而sys.getrefcount()函数并不是统计一个指针,而是要统计一个对象被引用的次数,所以最后一共会有八次引用。
 
  手动释放内存,应该怎么做呢?方法同样很简单。只需要先调用dela来删除一个对象;然后强制调用gc.collect(),即可手动启动垃圾回收。
 
  importgc
 
  importos
 
  importpsutil
 
  #显示当前python程序占用的内存大小
 
  defshow_memory_info(hint):
 
  pid=os.getpid()
 
  p=psutil.Process(pid)
 
  info=p.memory_full_info()
 
  memory=info.uss/1024./1024
 
  print('{}memoryused:{}MB'.format(hint,memory))
 
  show_memory_info('initial')
 
  a=[iforiinrange(10000000)]
 
  show_memory_info('afteracreated')
 
  dela
 
  gc.collect()
 
  show_memory_info('finish')
 
  print(a)
 
  initialmemoryused:6.54296875MB
 
  afteracreatedmemoryused:199.17578125MB
 
  finishmemoryused:7.26171875MB
 
  Traceback(mostrecentcalllast):
 
  File"Coroutine.py",line24,in<module>
 
  print(a)
 
  NameError:name'a'isnotdefined
 
  循环引用
 
  观察代码:
 
  deffunc():
 
  show_memory_info('initial')
 
  a=[iforiinrange(10000000)]
 
  b=[iforiinrange(10000000)]
 
  show_memory_info('aftera,bcreated')
 
  a.append(b)
 
  b.append(a)
 
  func()
 
  show_memory_info('finished')
 
  ##########输出##########
 
  initialmemoryused:6.625MB
 
  aftera,bcreatedmemoryused:392.08984375MB
 
  finishedmemoryused:392.08984375MB
 
  这里,a和b互相引用,并且,作为局部变量,在函数func调用结束后,a和b这两个指针从程序意义上已经不存在了。但是,很明显,依然有内存占用!为什么呢?因为互相引用,导致它们的引用数都不为0。
 
  处理这种情况,可以调用显式调用gc.collect(),来启动垃圾回收。
 
  Python使用标记清除(mark-sweep)算法和分代收集(generational),来启用针对循环引用的自动垃圾回收。
 
  调试内存泄漏
 
  objgraph,一个非常好用的可视化引用关系的包.
 
  安装:
 
  pipinstallgraphviz
 
  pipinstallxdot
 
  pipinstallobjgraph
 
  windows的话要除了装以上库还要在官网https://graphviz.gitlab.io/_pages/Download/Download_windows.html下载,然后设置环境变量Path增加C:\ProgramFiles(x86)\Graphviz2.38\bin,在CMD输入dot-version验证。
 
  通过下面这段代码和生成的引用调用图,你能非常直观地发现,有两个list互相引用,说明这里极有可能引起内存泄露。
 
  importobjgraph
 
  a=[1,2,3]
 
  b=[4,5,6]
 
  a.append(b)
 
  b.append(a)
 
  objgraph.show_refs([a])
 
  注:在windows中可能会提示:
 
  GraphwrittentoC:\Users\Public\Documents\Wondershare\CreatorTemp\objgraph-wwcqiie_.dot(8nodes)
 
  Imagerenderer(dot)notfound,notdoinganythingelse
 
  这时只要在打开dot文件所在的路径,然后CMD中执行
 
  dot.\objgraph-yclwfpzr.dot-Tpng-oimage.png
 
  就可以生成文件。
 
  另一个非常有用的函数,是show_backrefs()。以下是调用show_backrefs()生成的图片。
 
  参考
 
  极客时间《Python核心技术与实战》专栏

如需转载,请注明文章出处和来源网址:http://www.divcss5.com/html/h54862.shtml