自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Python內(nèi)存分配,常駐內(nèi)存和測(cè)量

開(kāi)發(fā) 后端
對(duì)于動(dòng)態(tài)語(yǔ)言,比如Python,內(nèi)存在語(yǔ)言層自動(dòng)管理,所以程序員無(wú)需關(guān)注太多細(xì)節(jié),但是如果要想自己寫(xiě)的代碼高效可靠,則也必須了解語(yǔ)言的內(nèi)存機(jī)制。本文給大家介紹Python語(yǔ)言的內(nèi)存機(jī)制,以及如何對(duì)其內(nèi)存進(jìn)行度量。

要精通一門(mén)語(yǔ)言,熟悉其內(nèi)容分配和使用機(jī)制很重要。對(duì)于編譯型語(yǔ)言比如C,C++,內(nèi)存的使用完全由程序員自己代碼分配和管理,所以對(duì)C,C++程序員內(nèi)存機(jī)制非常熟悉。但是對(duì)于動(dòng)態(tài)語(yǔ)言,比如Python,內(nèi)存在語(yǔ)言層自動(dòng)管理,所以程序員無(wú)需關(guān)注太多細(xì)節(jié),但是如果要想自己寫(xiě)的代碼高效可靠,則也必須了解語(yǔ)言的內(nèi)存機(jī)制。本文蟲(chóng)蟲(chóng)給大家介紹Python語(yǔ)言的內(nèi)存機(jī)制,以及如何對(duì)其內(nèi)存進(jìn)行度量。

概述

考慮以下代碼:

  1. import numpy as np 
  2. ccnp.ones((1024, 1024, 1024, 3), dtype=np.uint8) 

該代碼將會(huì)創(chuàng)建一個(gè)3GB字節(jié)的數(shù)組,并且都用1來(lái)填充。同學(xué)們,可能會(huì)這樣預(yù)想運(yùn)行該代碼后,進(jìn)程將會(huì)自動(dòng)分配3GB的內(nèi)存用來(lái)使用,事實(shí)是不是如此呢?

測(cè)量?jī)?nèi)存的一種方法是使用“常駐內(nèi)存”,在Python中可以使用psutil庫(kù)工具獲取方便的這些信息,檢查當(dāng)前進(jìn)程的常駐內(nèi)存:

  1. import psutil 
  2. psutil.Process().memory_info().rss /(1024 * 1024) 
  3. 3093 

在該示例中,進(jìn)程使用了3093MB或3.09GB,與數(shù)組大小的無(wú)區(qū)別,和預(yù)想的一樣。

但是常駐內(nèi)存實(shí)際上沒(méi)那么簡(jiǎn)單。假設(shè)在機(jī)器上運(yùn)行一些耗內(nèi)存的任務(wù)。然后切換回解釋器,再次運(yùn)行完全相同的命令:

  1. psutil.Process().memory_info().rss / (1024 * 1024) 
  2. 2903.12109375 

這是怎么回事? 內(nèi)存少了200MB。

為了解釋這個(gè)現(xiàn)象,需要了解操作系統(tǒng)如何內(nèi)存管理機(jī)制。

簡(jiǎn)化模型

當(dāng)前正運(yùn)行的程序都會(huì)分配一些內(nèi)存,即從操作系統(tǒng)取回虛擬內(nèi)存中的地址。 虛擬內(nèi)存是一個(gè)特定于進(jìn)程的地址空間,本質(zhì)上是來(lái)自0至264-1,進(jìn)程可以讀取或?qū)懭胱止?jié)。

在C語(yǔ)言中,程序員可以使用malloc()或者mmap()函數(shù)進(jìn)行手動(dòng)內(nèi)存分配;而在Python中,我們只需創(chuàng)建對(duì)象,Python 解釋器將在底層自動(dòng)調(diào)用malloc()或者mmap()。然后該進(jìn)程可以讀取或?qū)懭朐撎囟ǖ刂泛瓦B續(xù)字節(jié)。

Linux下可以用ltrace工具跟蹤調(diào)用malloc(),運(yùn)行下面Python代碼:

  1. import numpy as np 
  2. cc = np.ones((170_000,), dtype=np.uint8) 

然后可以運(yùn)行l(wèi)trace:

  1. ltrace -e malloc python ones.py 
  2. ... 
  3. _multiarray_umath.cpython-39-x86_64-linux-gnu.so->malloc(170000) = 0x5638862a45e0 
  4. ... 

整個(gè)過(guò)程Python 創(chuàng)建一個(gè)NumPy數(shù)組。

在Python引擎NumPy調(diào)用malloc()。

這樣做的結(jié)果malloc()是內(nèi)存中的地址:0x5638862a45e0。

然后,用于實(shí)現(xiàn)NumPy的C代碼可以讀取和寫(xiě)入該地址和下一個(gè)連續(xù)的169,999 個(gè)地址,每個(gè)地址代表虛擬內(nèi)存中的一個(gè)字節(jié)。

這 170,000個(gè)字節(jié)存儲(chǔ)在哪里?

它們可以存儲(chǔ)在RAM中;這是默認(rèn)設(shè)置。

它們可以存儲(chǔ)在計(jì)算機(jī)的硬盤(pán)驅(qū)動(dòng)器或磁盤(pán)上,即swap分區(qū)交換中。

一些字節(jié)可能存儲(chǔ)在 RAM 中,一些字節(jié)可能存儲(chǔ)在交換分區(qū)中。

常駐內(nèi)存

RAM很快,而硬盤(pán)IO很慢,但RAM很貴。通常電腦硬盤(pán)驅(qū)動(dòng)器空間比RAM多得多。例如,目前主流的計(jì)算機(jī)都會(huì)有2T左右的硬盤(pán)存儲(chǔ)空間,但只會(huì)16GB的RAM。

理想情況下,程序的所有內(nèi)存都將存儲(chǔ)在內(nèi)存RAM中,但計(jì)算機(jī)上運(yùn)行的各種進(jìn)程可能分配的內(nèi)存比RAM中可用的內(nèi)存多。如果發(fā)生這種情況,操作系統(tǒng)會(huì)將一些數(shù)據(jù)從RAM移動(dòng)或“交換”到硬盤(pán)驅(qū)動(dòng)器。必要時(shí),從交換分區(qū)中獲取數(shù)據(jù),并將未積極使用的數(shù)據(jù)置換進(jìn)去。

現(xiàn)在我們準(zhǔn)備定義我們的第一個(gè)內(nèi)存使用量度:常駐內(nèi)存。常駐內(nèi)存是進(jìn)程分配的內(nèi)存中有多少常駐或存儲(chǔ)在RAM中。

在第一個(gè)示例中,首先將所有3GB的已分配數(shù)組存儲(chǔ)在RAM中。

然后,當(dāng)運(yùn)行一些任務(wù)時(shí),加載這些任務(wù)需要分配很多RAM,因此操作系統(tǒng)會(huì)將一些數(shù)據(jù)從RAM交換到磁盤(pán)交換分區(qū)。結(jié)果,Python進(jìn)程的常駐內(nèi)存下降了:所有數(shù)據(jù)仍然可以訪(fǎng)問(wèn),但其中一些已移至磁盤(pán)交換分區(qū)。

分配內(nèi)存

測(cè)量分配內(nèi)存會(huì)很有用,無(wú)論操作系統(tǒng)是將數(shù)據(jù)放在RAM中還是將其交換到磁盤(pán),總是3GB內(nèi)存,程序?qū)嶋H需要多少內(nèi)存。

在 Python 中(如果使用的是Linux 或macOS),可以使用Fil memory profiler測(cè)量分配的內(nèi)存,它專(zhuān)門(mén)測(cè)量峰值分配的內(nèi)存。對(duì)于之前的示例:

常駐內(nèi)存和分配內(nèi)存之間的權(quán)衡

常駐內(nèi)存存在一些問(wèn)題:

  • 內(nèi)存的使用和測(cè)量會(huì)受到其他進(jìn)程的影響,由于其他進(jìn)程可能會(huì)爭(zhēng)搶常駐內(nèi)存導(dǎo)致使用的實(shí)際使用的RAM會(huì)變化。
  • 常駐內(nèi)存的上限是可用的物理RAM,所以一旦達(dá)到上限,就永遠(yuǎn)不會(huì)真正了解程序要求多少內(nèi)存。比如主機(jī)物理內(nèi)存16GB,對(duì)需要17GB內(nèi)存的程序和需要30GB 內(nèi)存的程序,它們駐留內(nèi)存的量都將一致,都將是16GB。
  • 另一方面,分配的內(nèi)存不受其他進(jìn)程的影響,并告訴程序?qū)嶋H請(qǐng)求的內(nèi)容。

當(dāng)然,常駐內(nèi)存確實(shí)比分配內(nèi)存的優(yōu)勢(shì):

  • 交換的內(nèi)存很可能永遠(yuǎn)不會(huì)被使用:想象一下創(chuàng)建一個(gè)數(shù)組,忘記刪除引用,然后在程序的其余部分不再實(shí)際使用它。
  • 更廣泛地說(shuō),由于駐留內(nèi)存從操作系統(tǒng)的角度衡量實(shí)際使用的內(nèi)存,因此它可以捕獲對(duì)分配的內(nèi)存跟蹤不可見(jiàn)的邊緣情況。

讓我們看一個(gè)這樣的邊緣情況的例子。

總結(jié)

到目前為止示例中,我們一直在分配充滿(mǎn)1的數(shù)組。如果測(cè)量已分配的內(nèi)存,則數(shù)組填充的內(nèi)容沒(méi)有區(qū)別:可以切換到創(chuàng)建充滿(mǎn)零的數(shù)組,并且仍然得到完全相同的結(jié)果。

但是在Linux 上,再看一個(gè)例子:

  1. import numpy as np 
  2. import psutil 
  3. arr = np.zeros((1024, 1024, 1024, 3), dtype=np.uint8) 
  4. psutil.Process().memory_info().rss/(1024 * 1024) 
  5. 28.5546875 

這次,還是分配了一個(gè)3GB的數(shù)組,但是給數(shù)組的元素都是零。然后測(cè)量常駐內(nèi)存——數(shù)組并沒(méi)有被計(jì)算到,常駐內(nèi)存只有29M。數(shù)組占用的內(nèi)存呢?

事實(shí)證明,Linux 不會(huì)費(fèi)心將所有這些零存儲(chǔ)在RAM中。而只是在實(shí)際訪(fǎng)問(wèn)數(shù)據(jù)時(shí)向RAM添加零塊,并不會(huì)實(shí)際分配內(nèi)存。

最后,需要提及的是,我們?cè)谡f(shuō)的內(nèi)存使用模型也是理想狀態(tài)的。還沒(méi)有包括文件緩存、分配器中的內(nèi)存碎片或其他可用指標(biāo)等。

話(huà)雖如此,對(duì)于許多應(yīng)用程序來(lái)說(shuō),分配的內(nèi)存可能足以作為幫助優(yōu)化程序內(nèi)存使用的必要措施。

 

責(zé)任編輯:趙寧寧 來(lái)源: 今日頭條
相關(guān)推薦

2023-10-18 13:31:00

Linux內(nèi)存

2013-10-12 11:15:09

Linux運(yùn)維內(nèi)存管理

2013-10-12 13:01:51

Linux運(yùn)維內(nèi)存管理

2023-01-10 09:18:37

Go內(nèi)存分配逃逸

2022-01-07 15:10:53

C++動(dòng)態(tài)內(nèi)存

2022-03-07 10:54:34

內(nèi)存Linux

2010-09-25 14:12:50

Java內(nèi)存分配

2021-02-28 13:22:54

Java內(nèi)存代碼

2019-09-10 16:25:19

Python內(nèi)存空對(duì)象

2022-03-16 08:39:19

StackHeap內(nèi)存

2023-01-28 08:32:04

Go內(nèi)存分配

2009-06-03 15:52:34

堆內(nèi)存棧內(nèi)存Java內(nèi)存分配

2022-01-13 10:30:21

C語(yǔ)言內(nèi)存動(dòng)態(tài)

2011-07-15 01:10:13

C++內(nèi)存分配

2018-02-08 14:57:22

對(duì)象內(nèi)存分配

2021-12-16 06:52:33

C語(yǔ)言內(nèi)存分配

2010-09-25 15:40:52

配置JVM內(nèi)存

2021-04-23 07:27:31

內(nèi)存分配CPU

2010-09-17 16:14:22

Java內(nèi)存分配

2011-12-20 10:43:21

Java
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)