首頁 >後端開發 >php教程 >PHP核心--探究記憶體管理與快取機制的圖文詳解

PHP核心--探究記憶體管理與快取機制的圖文詳解

黄舟
黄舟原創
2017-03-09 09:47:011738瀏覽

前言:

#PHP在運行時所需的內存,是一次向作業系統申請開闢的,而不是分多次。那他是怎麼樣一次性申請呢,機制又是如何?請看下邊介紹。

heap層是PHP記憶體管理的核心實作,PHP底層對記憶體的管理,ZendMM向系統進行的內存申請,並不是有需要時向系統即時申請, 而是由ZendMM的最底層(heap層)先向系統申請一大塊的內存, 建立一個類似於內存池的管理機制,unset後,ZendMM並不會直接立刻將記憶體交回系統,而是只在自身維護的#記憶體池(storge層)中將其重新標識為可用,。

優點:

1. 預先定義常數變數多,對記憶體的請求有數百次,避免了PHP向系統頻繁的記憶體申請操作,減少了對OS的請求次數。

2.運行速度會更快,缺點是隨著程式的運行時間的變長,記憶體使用越來越多,所以5.3引進新垃圾回收機制。

詳細分析如下:

PHP的記憶體管理可以被視為分層(hierarchical)的。 它分為三層:儲存層(storage)、堆疊層(heap)和介面層(emalloc/efree)。 儲存層透過 malloc()、mmap() 等函數向系統真正的申請內存,並透過 free() 函數釋放所申請的內存。



#儲存層通常申請的記憶體區塊都比較大,這裡申請的記憶體大併不是指storage層結構所需的記憶體大, 只是堆層透過呼叫儲存層的分配方法時,其以大塊大塊的方式申請的記憶體,儲存層的作用是將記憶體分配的方式對堆層透明化。

如圖下所示,PHP記憶體管理器。 PHP在儲存層共有4種記憶體分配方案: malloc,win32,mmap_anon,mmap_zero, 預設使用malloc分配內存,如果設定了ZEND_WIN32宏,則為windows版本,呼叫HeapAlloc分配內存,剩下兩種記憶體方案為匿名記憶體映射,PHP的記憶體方案可以透過設定環境變數來修改。




###################################################################################################### ###################一.記憶體的申請##########

heap層是PHP記憶體管理的核心實作,PHP底層對記憶體的管理, 圍繞著小塊記憶體清單(free_buckets)、 大塊記憶體清單(large_free_buckets)和剩餘記憶體列表(rest_buckets)三個清單來分層進行的。 ZendMM向系統進行的記憶體申請,並不是有需要時向系統即時申請, 而是由ZendMM的最底層(heap層)先向系統申請一大塊的內存,透過對上面三種列表的填充, 建立一個類似記憶體池的管理機制。


在程式運行需要使用記憶體的時候,ZendMM會在記憶體池中分配對應的記憶體供使用。 這樣做的好處是避免了PHP向系統頻繁的記憶體申請操作,如下面的程式碼:

<?php
  $tipi = "o_o\n";
  echo $tipi;
?>

這是一個簡單的php程序,但透過對emalloc的呼叫計數,只是PHP程序,只賦值了一個變數而已,但是卻發現對記憶體的請求有數百次之多,當然這非常容易解釋,因為PHP腳本的執行,需要大量的環境變數以及內部變數的定義(詳細見PHP內核--生命週期), 這些定義本身都是需要在記憶體中儲存的。

在編寫PHP的擴充功能時,推薦使用emalloc(申請的是zend_mm_storage層的記憶體區塊)來代替malloc(申請的是作業系統的記憶體區塊),其實也就是用PHP的ZendMM來代替 手動直接呼叫系統層級的記憶體管理。


ZendMM使用_zend_mm_alloc_int函數進行記憶體分配,流程如下:




##從上面的分配可以看出,PHP對內存的分配,是結合PHP的用途來設計的,PHP一般用於web應用程序的數據支持, 單個腳本的運行週期一般比較短(最多達到秒級),內存大塊整塊的申請,自主進行小塊的分配, 沒有進行比較複雜的不相臨位址的空閒記憶體合併,而是集中再次向系統請求。 這樣做的好處是運行速度會更快,缺點是隨著程式的運行時間的變長, 記憶體的使用情況會「越來越多」(PHP5.2及更早版本)。 所以PHP5.3之前的版本並不適合做為守護程式長期運行。 (當然,可以有其他方法解決,而且在PHP5.3中引入了新的GC機制,詳見後邊小節PHP內核--記憶體洩漏與新垃圾回收機制


二.記憶體的銷毀

#

ZendMM在記憶體銷毀的處理上採用與記憶體申請相同的策略,當程式unset一個變數或是其他的釋放行為時, ZendMM並不會直接立刻將記憶體交回系統,而是只在自身維護的#記憶體池(storge層)在 #中將其重新標識為可用, 依照記憶體的大小整理到上面所說的三種清單(small,large,free)之中,以備下次記憶體申請時使用。


#記憶體銷毀的最終實作函數是_efree。在_efree中,記憶體的銷毀首先要進行是否放回cache的判斷。 如果記憶體的大小滿足ZEND_MM_SMALL_SIZE且cache還沒有超過系統設定的ZEND_MM_CACHE_SIZE, 那麼,目前記憶體區塊zend_mm_block就會放回mm_heap- >cache中。 

在記憶體的銷毀過程中,也涉及引用計數和垃圾回收(GC),將在後邊小節進行討論。參見PHP核心--記憶體洩漏與新垃圾回收機制


三.快取

在維基百科中有這樣一段描述: 凡是位於兩種速度相差較大的硬體之間的,用於協調兩者資料傳輸速度差異的結構,​​均可稱為Cache。  從最初始的處理器與記憶體間的Cache開始,都是為了讓資料存取的速度適應CPU的處理速度, 其基於的原理是記憶體中「程式執行與資料存取的局域性行為」。 同樣PHP記憶體管理中的快取也是基於「程式執行與資料存取的局域性行為」的原理。 引入緩存,就是為了減少小塊記憶體區塊的查詢次數(查詢前先看是否能命中緩存),為最近訪問的資料提供更快的存取方式。

PHP將快取加入到記憶體管理機制中做了以下一些操作:

##·標識快取和快取的大小限制,即何時使用緩存,在某些情況下可以以最少的修改停用掉快取

·快取的儲存結構,即快取的存放位置、結構和存放的邏輯

·初始化快取

·取得快取中內容

#·寫入快取

##釋放快取或者清空快取清單

快取本身也是儲存在storage層申請的記憶體中的,如果記憶體都不夠用了,那就得釋放快取啦。

當堆的記憶體溢出時,程式會呼叫zend_mm_free_cache釋放快取中。 整個釋放的過程是一個遍歷數組, 對於每個數組的元素程序都遍歷其所在鍊錶中在自己之前的元素,執行合併內存操作,減少堆結構中緩存計量數字。 


以上是PHP核心--探究記憶體管理與快取機制的圖文詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn