㈠ 怎麼讓python在內存中運行
對象的內存使用
賦值語句是語言最常見的功能了。但即使是最簡單的賦值語句,也可以很有內涵。Python的賦值語句就很值得研究。
a = 1
整數1為一個對象。而a是一個引用。利用賦值語句,引用a指向對象1。Python是動態類型的語言(參考動態類型),對象與引用分離。Python像使用「筷子」那樣,通過引用來接觸和翻動真正的食物——對象。
引用和對象
為了探索對象在內存的存儲,我們可以求助於Python的內置函數id()。它用於返回對象的身份(identity)。其實,這里所謂的身份,就是該對象的內存地址。
a = 1
print(id(a))
print(hex(id(a)))
在我的計算機上,它們返回的是:
11246696
'0xab9c68'
分別為內存地址的十進制和十六進製表示。
在Python中,整數和短小的字元,Python都會緩存這些對象,以便重復使用。當我們創建多個等於1的引用時,實際上是讓所有這些引用指向同一個對象。
a = 1
b = 1
print(id(a))
print(id(b))
上面程序返回
11246696
11246696
可見a和b實際上是指向同一個對象的兩個引用。
為了檢驗兩個引用指向同一個對象,我們可以用is關鍵字。is用於判斷兩個引用所指的對象是否相同。
# True
a = 1
b = 1
print(a is b)
# True
a = "good"
b = "good"
print(a is b)
# False
a = "very good morning"
b = "very good morning"
print(a is b)
# False
a = []
b = []
print(a is b)
上面的注釋為相應的運行結果。可以看到,由於Python緩存了整數和短字元串,因此每個對象只存有一份。比如,所有整數1的引用都指向同一對象。即使使用賦值語句,也只是創造了新的引用,而不是對象本身。長的字元串和其它對象可以有多個相同的對象,可以使用賦值語句創建出新的對象。
在Python中,每個對象都有存有指向該對象的引用總數,即引用計數(reference count)。
我們可以使用sys包中的getrefcount(),來查看某個對象的引用計數。需要注意的是,當使用某個引用作為參數,傳遞給getrefcount()時,參數實際上創建了一個臨時的引用。因此,getrefcount()所得到的結果,會比期望的多1。
from sys import getrefcount
a = [1, 2, 3]
print(getrefcount(a))
b = a
print(getrefcount(b))
由於上述原因,兩個getrefcount將返回2和3,而不是期望的1和2。
對象引用對象
Python的一個容器對象(container),比如表、詞典等,可以包含多個對象。實際上,容器對象中包含的並不是元素對象本身,是指向各個元素對象的引用。
我們也可以自定義一個對象,並引用其它對象:
class from_obj(object):
def __init__(self, to_obj):
self.to_obj = to_obj
b = [1,2,3]
a = from_obj(b)
print(id(a.to_obj))
print(id(b))
可以看到,a引用了對象b。
對象引用對象,是Python最基本的構成方式。即使是a = 1這一賦值方式,實際上是讓詞典的一個鍵值"a"的元素引用整數對象1。該詞典對象用於記錄所有的全局引用。該詞典引用了整數對象1。我們可以通過內置函數globals()來查看該詞典。
當一個對象A被另一個對象B引用時,A的引用計數將增加1。
from sys import getrefcount
a = [1, 2, 3]
print(getrefcount(a))
b = [a, a]
print(getrefcount(a))
由於對象b引用了兩次a,a的引用計數增加了2。
容器對象的引用可能構成很復雜的拓撲結構。我們可以用objgraph包來繪制其引用關系,比如
x = [1, 2, 3]
y = [x, dict(key1=x)]
z = [y, (x, y)]
import objgraph
objgraph.show_refs([z], filename='ref_topo.png')
objgraph是Python的一個第三方包。安裝之前需要安裝xdot。
sudo apt-get install xdot
sudo pip install objgraph
objgraph官網
兩個對象可能相互引用,從而構成所謂的引用環(reference cycle)。
a = []
b = [a]
a.append(b)
即使是一個對象,只需要自己引用自己,也能構成引用環。
a = []
a.append(a)
print(getrefcount(a))
引用環會給垃圾回收機制帶來很大的麻煩,我將在後面詳細敘述這一點。
引用減少
某個對象的引用計數可能減少。比如,可以使用del關鍵字刪除某個引用:
from sys import getrefcount
a = [1, 2, 3]
b = a
print(getrefcount(b))
del a
print(getrefcount(b))
del也可以用於刪除容器元素中的元素,比如:
a = [1,2,3]
del a[0]
print(a)
如果某個引用指向對象A,當這個引用被重新定向到某個其他對象B時,對象A的引用計數減少:
from sys import getrefcount
a = [1, 2, 3]
b = a
print(getrefcount(b))
a = 1
print(getrefcount(b))
垃圾回收
吃太多,總會變胖,Python也是這樣。當Python中的對象越來越多,它們將占據越來越大的內存。不過你不用太擔心Python的體形,它會乖巧的在適當的時候「減肥」,啟動垃圾回收(garbage collection),將沒用的對象清除。在許多語言中都有垃圾回收機制,比如Java和Ruby。盡管最終目的都是塑造苗條的提醒,但不同語言的減肥方案有很大的差異 (這一點可以對比本文和Java內存管理與垃圾回收
)。
從基本原理上,當Python的某個對象的引用計數降為0時,說明沒有任何引用指向該對象,該對象就成為要被回收的垃圾了。比如某個新建對象,它被分配給某個引用,對象的引用計數變為1。如果引用被刪除,對象的引用計數為0,那麼該對象就可以被垃圾回收。比如下面的表:
a = [1, 2, 3]
del a
del a後,已經沒有任何引用指向之前建立的[1, 2, 3]這個表。用戶不可能通過任何方式接觸或者動用這個對象。這個對象如果繼續待在內存里,就成了不健康的脂肪。當垃圾回收啟動時,Python掃描到這個引用計數為0的對象,就將它所佔據的內存清空。
然而,減肥是個昂貴而費力的事情。垃圾回收時,Python不能進行其它的任務。頻繁的垃圾回收將大大降低Python的工作效率。如果內存中的對象不多,就沒有必要總啟動垃圾回收。所以,Python只會在特定條件下,自動啟動垃圾回收。當Python運行時,會記錄其中分配對象(object allocation)和取消分配對象(object deallocation)的次數。當兩者的差值高於某個閾值時,垃圾回收才會啟動。
我們可以通過gc模塊的get_threshold()方法,查看該閾值:
import gc
print(gc.get_threshold())
返回(700, 10, 10),後面的兩個10是與分代回收相關的閾值,後面可以看到。700即是垃圾回收啟動的閾值。可以通過gc中的set_threshold()方法重新設置。
我們也可以手動啟動垃圾回收,即使用gc.collect()。
分代回收
Python同時採用了分代(generation)回收的策略。這一策略的基本假設是,存活時間越久的對象,越不可能在後面的程序中變成垃圾。我們的程序往往會產生大量的對象,許多對象很快產生和消失,但也有一些對象長期被使用。出於信任和效率,對於這樣一些「長壽」對象,我們相信它們的用處,所以減少在垃圾回收中掃描它們的頻率。
小傢伙要多檢查
Python將所有的對象分為0,1,2三代。所有的新建對象都是0代對象。當某一代對象經歷過垃圾回收,依然存活,那麼它就被歸入下一代對象。垃圾回收啟動時,一定會掃描所有的0代對象。如果0代經過一定次數垃圾回收,那麼就啟動對0代和1代的掃描清理。當1代也經歷了一定次數的垃圾回收後,那麼會啟動對0,1,2,即對所有對象進行掃描。
這兩個次數即上面get_threshold()返回的(700, 10, 10)返回的兩個10。也就是說,每10次0代垃圾回收,會配合1次1代的垃圾回收;而每10次1代的垃圾回收,才會有1次的2代垃圾回收。
同樣可以用set_threshold()來調整,比如對2代對象進行更頻繁的掃描。
import gc
gc.set_threshold(700, 10, 5)
孤立的引用環
引用環的存在會給上面的垃圾回收機制帶來很大的困難。這些引用環可能構成無法使用,但引用計數不為0的一些對象。
a = []
b = [a]
a.append(b)
del a
del b
上面我們先創建了兩個表對象,並引用對方,構成一個引用環。刪除了a,b引用之後,這兩個對象不可能再從程序中調用,就沒有什麼用處了。但是由於引用環的存在,這兩個對象的引用計數都沒有降到0,不會被垃圾回收。
孤立的引用環
為了回收這樣的引用環,Python復制每個對象的引用計數,可以記為gc_ref。假設,每個對象i,該計數為gc_ref_i。Python會遍歷所有的對象i。對於每個對象i引用的對象j,將相應的gc_ref_j減1。
遍歷後的結果
在結束遍歷後,gc_ref不為0的對象,和這些對象引用的對象,以及繼續更下游引用的對象,需要被保留。而其它的對象則被垃圾回收。
㈡ python 內存佔用分析工具
pip install memory_profiler
pip install psutil
pip install matplotlib
使用方法
from memory_profiler import profile
@profile(precision=4, stream=open('test.log', 'w+'))
def test(args: List):
...
運行:
python3 test.py
Mem usage:表示執行該行後Python解釋器的內存使用情況
Increment:表示當前行的內存相對於上一行的差異,即自己本身增長了多少,如果減少了則不顯示.
㈢ 如何釋放Python佔用的內存
在上文的優化中,對每500個用戶,會進行一些計算並記錄結果在磁碟文件中。原本以為這么做,這些結果就在磁碟文件中了,而不會再繼續佔用內存;但實際上,python的大坑就是Python不會自動清理這些內存。這是由其本身實現決定的。具體原因網上多有文章介紹,這里就不了。
本篇博客將貼一個筆者的實驗腳本,用以說明Python確實存在這么一個不釋放內存的現象,另外也提出一個解決方案,即:先del,再顯式調用gc.collect(). 腳本和具體效果見下。
實驗環境一:Win 7, Python 2.7
[python] view plain
from time import sleep, time
import gc
def mem(way=1):
print time()
for i in range(10000000):
if way == 1:
pass
else: # way 2, 3
del i
print time()
if way == 1 or way == 2:
pass
else: # way 3
gc.collect()
print time()
if __name__ == "__main__":
print "Test way 1: just pass"
mem(way=1)
sleep(20)
print "Test way 2: just del"
mem(way=2)
sleep(20)
print "Test way 3: del, and then gc.collect()"
mem(way=3)
sleep(20)
運行結果如下:
[plain] view plain
Test way 1: just pass
1426688589.47
1426688590.25
1426688590.25
Test way 2: just del
1426688610.25
1426688611.05
1426688611.05
Test way 3: del, and then gc.collect()
1426688631.05
1426688631.85
1426688631.95
對於way 1和way 2,結果是完全一樣的,程序內存消耗峰值是326772KB,在sleep 20秒時,內存實時消耗是244820KB;
對於way 3,程序內存消耗峰值同上,但是sleep時內存實時消耗就只有6336KB了。
實驗環境二: Ubuntu 14.10, Python 2.7.3
運行結果:
[plain] view plain
Test way 1: just pass
1426689577.46
1426689579.41
1426689579.41
Test way 2: just del
1426689599.43
1426689601.1
1426689601.1
Test way 3: del, and then gc.collect()
1426689621.12
1426689622.8
1426689623.11
[plain] view plain
ubuntu@my_machine:~$ ps -aux | grep test_mem
Warning: bad ps syntax, perhaps a bogus '-'? See
ubuntu 9122 10.0 6.0 270916 245564 pts/1 S+ 14:39 0:03 python test_mem.py
ubuntu 9134 0.0 0.0 8104 924 pts/2 S+ 14:40 0:00 grep --color=auto test_mem
ubuntu@my_machine:~$ ps -aux | grep test_mem
Warning: bad ps syntax, perhaps a bogus '-'? See
ubuntu 9122 10.0 6.0 270916 245564 pts/1 S+ 14:39 0:03 python test_mem.py
ubuntu 9134 0.0 0.0 8104 924 pts/2 S+ 14:40 0:00 grep --color=auto test_mem
ubuntu@my_machine:~$ ps -aux | grep test_mem
Warning: bad ps syntax, perhaps a bogus '-'? See
ubuntu 9122 11.6 0.1 30956 5608 pts/1 S+ 14:39 0:05 python test_mem.py
結論:
以上說明,當調用del時,其實Python並不會真正release內存,而是將其繼續放在其內存池中;只有在顯式調用gc.collect()時,才會真正release內存。
進一步:
其實回到上一篇博客的腳本中,也讓其引入gc.collect(),然後寫個監控腳本監測內存消耗情況:
[plain] view plain
while ((1)); do ps -aux | sort -n -k5,6 | grep my_script; free; sleep 5; done
結果發現:內存並不會在每500個用戶一組執行完後恢復,而是一直持續消耗到僅存約70MB時,gc才好像起作用。本環境中,機器使用的是Cloud instance,總內存2G,可用內存約為1G,本腳本內存常用消耗是900M - 1G。換句話說,對於這個腳本來說,gc並沒有立即起作用,而是在系統可用內存從1 - 1.2G下降到只剩70M左右時,gc才開始發揮作用。這點確實比較奇怪,不知道和該腳本是在Thread中使用的gc.collect()是否有關,或者是gc發揮作用原本就不是可控的。筆者尚未做相關實驗,可能在下篇博客中繼續探討。
但是,可以肯定的是,若不使用gc.collect(), 原腳本將會將系統內存耗盡而被殺死。這一點從syslog中可以明顯看出。
㈣ Python 怎樣獲取當前計算機的 cpu,內存等信息
用psutil包
cpu:
>>>importpsutil
>>>psutil.cpu_times()
scputimes(user=3961.46,nice=169.729,system=2150.659,idle=16900.540,iowait=629.59,irq=0.0,softirq=19.42,steal=0.0,guest=0,nice=0.0)
>>>
>>>forxinrange(3):
...psutil.cpu_percent(interval=1)
...
4.0
5.9
3.8
>>>
>>>forxinrange(3):
...psutil.cpu_percent(interval=1,percpu=True)
...
[4.0,6.9,3.7,9.2]
[7.0,8.5,2.4,2.1]
[1.2,9.0,9.9,7.2]
>>>
>>>
>>>forxinrange(3):
...psutil.cpu_times_percent(interval=1,percpu=False)
...
scputimes(user=1.5,nice=0.0,system=0.5,idle=96.5,iowait=1.5,irq=0.0,softirq=0.0,steal=0.0,guest=0.0,guest_nice=0.0)
scputimes(user=1.0,nice=0.0,system=0.0,idle=99.0,iowait=0.0,irq=0.0,softirq=0.0,steal=0.0,guest=0.0,guest_nice=0.0)
scputimes(user=2.0,nice=0.0,system=0.0,idle=98.0,iowait=0.0,irq=0.0,softirq=0.0,steal=0.0,guest=0.0,guest_nice=0.0)
>>>
>>>psutil.cpu_count()
4
>>>psutil.cpu_count(logical=False)
2
>>>
內存:
>>>psutil.virtual_memory()
svmem(total=8374149120L,available=2081050624L,percent=75.1,used=8074080256L,free=300068864L,active=3294920704,inactive=1361616896,buffers=529895424L,cached=1251086336)
>>>psutil.swap_memory()
sswap(total=2097147904L,used=296128512L,free=1801019392L,percent=14.1,sin=304193536,sout=677842944)
>>>
㈤ python的內存管理機制
論壇
活動
招聘
專題
打開CSDN APP
Copyright © 1999-2020, CSDN.NET, All Rights Reserved
登錄
XCCS_澍
關注
Python 的內存管理機制及調優手段? 原創
2018-08-05 06:50:53
XCCS_澍
碼齡7年
關注
內存管理機制:引用計數、垃圾回收、內存池。
一、引用計數:
引用計數是一種非常高效的內存管理手段, 當一個 Python 對象被引用時其引用計數增加 1, 當其不再被一個變數引用時則計數減 1. 當引用計數等於 0 時對象被刪除。
二、垃圾回收 :
1. 引用計數
引用計數也是一種垃圾收集機制,而且也是一種最直觀,最簡單的垃圾收集技術。當 Python 的某個對象的引用計數降為 0 時,說明沒有任何引用指向該對象,該對象就成為要被回收的垃圾了。比如某個新建對象,它被分配給某個引用,對象的引用計數變為 1。如果引用被刪除,對象的引用計數為 0,那麼該對象就可以被垃圾回收。不過如果出現循環引用的話,引用計數機制就不再起有效的作用了
2. 標記清除
如果兩個對象的引用計數都為 1,但是僅僅存在他們之間的循環引用,那麼這兩個對象都是需要被回收的,也就是說,它們的引用計數雖然表現為非 0,但實際上有效的引用計數為 0。所以先將循環引用摘掉,就會得出這兩個對象的有效計數。
3. 分代回收
從前面「標記-清除」這樣的垃圾收集機制來看,這種垃圾收集機制所帶來的額外操作實際上與系統中總的內存塊的數量是相關的,當需要回收的內存塊越多時,垃圾檢測帶來的額外操作就越多,而垃圾回收帶來的額外操作就越少;反之,當需回收的內存塊越少時,垃圾檢測就將比垃圾回收帶來更少的額外操作。
㈥ python如何進行內存管理
Python的內存管理主要有三種機制:引用計數機制,垃圾回收機制和內存池機制。
引用計數機制
簡介
python內部使用引用計數,來保持追蹤內存中的對象,Python內部記錄了對象有多少個引用,即引用計數,當對象被創建時就創建了一個引用計數,當對象不再需要時,這個對象的引用計數為0時,它被垃圾回收。
特性
1.當給一個對象分配一個新名稱或者將一個對象放入一個容器(列表、元組或字典)時,該對象的引用計數都會增加。
2.當使用del對對象顯示銷毀或者引用超出作用於或者被重新賦值時,該對象的引用計數就會減少。
3.可以使用sys.getrefcount()函數來獲取對象的當前引用計數。多數情況下,引用計數要比我們猜測的大的多。對於不可變數據(數字和字元串),解釋器會在程序的不同部分共享內存,以便節約內存。
垃圾回收機制
特性
1.當內存中有不再使用的部分時,垃圾收集器就會把他們清理掉。它會去檢查那些引用計數為0的對象,然後清除其在內存的空間。當然除了引用計數為0的會被清除,還有一種情況也會被垃圾收集器清掉:當兩個對象相互引用時,他們本身其他的引用已經為0了。
2.垃圾回收機制還有一個循環垃圾回收器, 確保釋放循環引用對象(a引用b, b引用a, 導致其引用計數永遠不為0)。
內存池機制
簡介
在Python中,許多時候申請的內存都是小塊的內存,這些小塊內存在申請後,很快又會被釋放,由於這些內存的申請並不是為了創建對象,所以並沒有對象一級的內存池機制。這就意味著Python在運行期間會大量地執行malloc和free的操作,頻繁地在用戶態和核心態之間進行切換,這將嚴重影響Python的執行效率。為了加速Python的執行效率,Python引入了一個內存池機制,用於管理對小塊內存的申請和釋放。
內存池概念
內存池的概念就是預先在內存中申請一定數量的,大小相等的內存塊留作備用,當有新的內存需求時,就先從內存池中分配內存給這個需求,不夠了之後再申請新的內存。這樣做最顯著的優勢就是能夠減少內存碎片,提升效率。內存池的實現方式有很多,性能和適用范圍也不一樣。
特性
1.Python提供了對內存的垃圾收集機制,但是它將不用的內存放到內存池而不是返回給操作系統。
2.Pymalloc機制。為了加速Python的執行效率,Python引入了一個內存池機制,用於管理對小塊內存的申請和釋放。
3.Python中所有小於256個位元組的對象都使用pymalloc實現的分配器,而大的對象則使用系統的 malloc。
4.對於Python對象,如整數,浮點數和List,都有其獨立的私有內存池,對象間不共享他們的內存池。也就是說如果你分配又釋放了大量的整數,用於緩存這些整數的內存就不能再分配給浮點數。
㈦ 如何釋放Python佔用的內存
釋放Python佔用的內存
象的引用計數減少;
函數運行結束,所有局部變數都被銷毀,對象的引用計數也就隨之減少。例如 foo(x) 運行結束,x 被銷毀;
當變數被賦值給另一個對象時,原對象的引用計數也會減少。例如 x = 4,這時候 3 這個對象的引用計數就減 1 了;
使用 del 刪除一個變數也會導致對象引用減少。例如 del x;
對象從集合對象中移除。例如 lst.remove(x);
包含對象的集合對象被銷毀。例如 del lst;
這些操作都可能使對象變成垃圾回收對象,由垃圾收集器負責收集,當然垃圾收集器也負責處理循環引用對象。
要立即釋放,可以使用下面的代碼
import gc
gc.collect()
㈧ python如何控制內存
python控制內存的方法:
一、對象的引用計數機制
二、垃圾回收機制
三、內存池機制
一、對象的引用計數機制
Python內部使用引用計數,來保持追蹤內存中的對象,所有對象都有引用計數。
引用計數增加的情況:
1、一個對象分配一個新名稱
2、將其放入一個容器中(如列表、元組或字典)
引用計數減少的情況:
1、使用del語句對對象別名顯示的銷毀
2、引用超出作用域或被重新賦值 sys.getrefcount( )函數可以獲得對象的當前引用計數
多數情況下,引用計數比你猜測得要大得多。對於不可變數據(如數字和字元串),解釋器會在程序的不同部分共享內存,以便節約內存。
二、垃圾回收
1、當一個對象的引用計數歸零時,它將被垃圾收集機制處理掉。
2、當兩個對象a和b相互引用時,del語句可以減少a和b的引用計數,並銷毀用於引用底層對象的名稱。然而由於每個對象都包含一個對其他對象的應用,因此引用計數不會歸零,對象也不會銷毀。(從而導致內存泄露)。為解決這一問題,解釋器會定期執行一個循環檢測器,搜索不可訪問對象的循環並刪除它們。
三、內存池機制
Python提供了對內存的垃圾收集機制,但是它將不用的內存放到內存池而不是返回給操作系統。
1、Pymalloc機制。為了加速Python的執行效率,Python引入了一個內存池機制,用於管理對小塊內存的申請和釋放。
2、Python中所有小於256個位元組的對象都使用pymalloc實現的分配器,而大的對象則使用系統的malloc。
3、對於Python對象,如整數,浮點數和List,都有其獨立的私有內存池,對象間不共享他們的內存池。也就是說如果你分配又釋放了大量的整數,用於緩存這些整數的內存就不能再分配給浮點數。
更多Python知識請關注Python視頻教程欄目。
㈨ import暫用內存
import指令可以用來導入模塊,以便在當前程序中使用模塊中定義的變數、函數和類。在Python中,import指令會在內存中載入模塊的代碼,以便程序可以訪問模塊中定義的變數、函數和類。因此,import指令會暫用一定的內存空間,以便程序可以正確使用模塊中定義的變數、函數和類。