日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > php >内容正文

php

PHP内存管理 垃圾回收

發(fā)布時(shí)間:2024/9/20 php 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 PHP内存管理 垃圾回收 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

來源:http://www.jianshu.com/p/63a381a7f70c

概述
1) 操作系統(tǒng)直接管理著內(nèi)存,所以操作系統(tǒng)也需要進(jìn)行內(nèi)存管理,計(jì)算機(jī)中通常都有內(nèi)存管理單元(MMU) 用于處理CPU對內(nèi)存的訪問。
2) 應(yīng)用程序無法直接調(diào)用物理內(nèi)存, 只能向系統(tǒng)申請內(nèi)存。
向操作系統(tǒng)申請內(nèi)存空間會引發(fā)系統(tǒng)調(diào)用。
系統(tǒng)調(diào)用會將CPU從用戶態(tài)切換到內(nèi)核。
為了減少系統(tǒng)調(diào)用開銷。通常在用戶態(tài)進(jìn)行內(nèi)存管理。 申請大塊內(nèi)存?zhèn)溆谩J褂猛甑膬?nèi)存不馬上釋放,將內(nèi)存復(fù)用,避免多次內(nèi)存申請和釋放所帶來性能消耗。
3) PHP不需要顯示內(nèi)存管理,由Zend引擎進(jìn)行管理。
PHP內(nèi)存限制
1)php.ini中的默認(rèn)32MB
memory_limit = 32M
2)動(dòng)態(tài)修改內(nèi)存
ini_set ("memory_limit", "128M")

3)獲取目前內(nèi)存占用
memory_get_usage() : 獲取PHP腳本所用的內(nèi)存大小
memory_get_peak_usage() :返回當(dāng)前腳本到目前位置所占用的內(nèi)存峰值。

學(xué)習(xí)內(nèi)存管理的目的
了解PHP如何占用內(nèi)存,可以避免不必要的內(nèi)存浪費(fèi)。

PHP中的內(nèi)存管理

包含:
1)足夠內(nèi)存
2)可用內(nèi)存獲取部分內(nèi)存
3)使用后的內(nèi)存,是否銷毀還是重新分配

PHP內(nèi)存管理器


clipboard.png

接口層,是一些宏定義。
堆層 heap
_zend_mm_heap

初始化內(nèi)存,調(diào)用 zend_mm_startup
PHP內(nèi)存管理維護(hù)三個(gè)列表:
1)小塊內(nèi)存列表 free_buckets
2)大塊內(nèi)存列表 large_free_buckets
3)剩余內(nèi)存列表 rest_buckets

兩個(gè)HashTable 結(jié)構(gòu),難點(diǎn)是查找和計(jì)算內(nèi)存地址
1)free_buckets
Hash函數(shù)為:

#define ZEND_MM_BUCKET_INDEX(true_size) ((true_size>>ZEND_MM_ALIGNMENT_LOG2)-(ZEND_MM_ALIGNED_MIN_HEADER_SIZE>>ZEND_MM_ALIGNMENT_LOG2))

2)large_free_buckets
Hash函數(shù)為:

#define ZEND_MM_LARGE_BUCKET_INDEX(S) zend_mm_high_bit(S)static inline unsigned int zend_mm_high_bit(size_t _size){..//省略若干不同環(huán)境的實(shí)現(xiàn)unsignedint n =0;while(_size !=0) { _size = _size >>1; n++;}return n-1;}

存儲層 storage

  • 內(nèi)存分配的方式對堆層透明化,實(shí)現(xiàn)存儲層和heap層的分離。
  • 不同的內(nèi)存分配方案, 有對應(yīng)的處理函數(shù)。

內(nèi)存的申請

PHP底層對內(nèi)存的管理, 圍繞著小塊內(nèi)存列表(free_buckets)、 大塊內(nèi)存列表(large_free_buckets)和 剩余內(nèi)存列表(rest_buckets)三個(gè)列表來分層進(jìn)行的

ZendMM向系統(tǒng)進(jìn)行的內(nèi)存申請,并不是有需要時(shí)向系統(tǒng)即時(shí)申請, 而是由ZendMM的最底層(heap層)先向系統(tǒng)申請一大塊的內(nèi)存,通過對上面三種列表的填充, 建立一個(gè)類似于內(nèi)存池的管理機(jī)制。 在程序運(yùn)行需要使用內(nèi)存的時(shí)候,ZendMM會在內(nèi)存池中分配相應(yīng)的內(nèi)存供使用。 這樣做的好處是避免了PHP向系統(tǒng)頻繁的內(nèi)存申請操作

ZendMM對內(nèi)存分配的處理步驟:

1)內(nèi)存檢查;
2)命中緩存,找到內(nèi)存塊,調(diào)至步驟5;
3)在ZendMM管理的heap層存儲中搜索合適大小的內(nèi)存塊, 是在三種列表中小到大進(jìn)行的,找到block后,調(diào)至步驟5;
4)步驟3未找到內(nèi)存,則使用 ZEND_MM_STORAGE_ALLOC 申請新內(nèi)存塊 (至少為ZEND_MM_SEG_SIZE),進(jìn)行步驟6

5)使用zend_mm_remove_from_free_list函數(shù)將已經(jīng)使用block節(jié)點(diǎn)在zend_mm_free_block中移除;
6) 內(nèi)存分配完畢,對zend_mm_heap結(jié)構(gòu)中的各種標(biāo)識型變量進(jìn)行維護(hù),包括large_free_buckets, peak,size等;
7) 返回分配的內(nèi)存地址;


PHP內(nèi)存管理器

內(nèi)存的銷毀

ZendMM在內(nèi)存銷毀的處理上采用與內(nèi)存申請相同的策略,當(dāng)程序unset一個(gè)變量或者是其他的釋放行為時(shí), ZendMM并不會直接立刻將內(nèi)存交回給系統(tǒng),而是只在自身維護(hù)的內(nèi)存池中將其重新標(biāo)識為可用, 按照內(nèi)存的大小整理到上面所說的三種列表(small,large,free)之中,以備下次內(nèi)存申請時(shí)使用。

ZendMM將內(nèi)存塊以整理收回到zend_mm_heap的方式,回收到內(nèi)存池中。
程序使用的所有內(nèi)存,將在進(jìn)程結(jié)束時(shí)統(tǒng)一交還給系統(tǒng)。

垃圾回收

自動(dòng)回收內(nèi)存的過程叫垃圾收集。PHP提供了語言層的垃圾回收機(jī)制,讓程序員不必過分關(guān)心程序內(nèi)存分配。

PHP5.3之前
引用計(jì)數(shù)方式的內(nèi)存動(dòng)態(tài)管理。

PHP中所有的變量都是以zval變量的形式存在。

變量引用計(jì)數(shù)變?yōu)?時(shí),PHP將在內(nèi)存中銷毀這個(gè)變量。只是這里的垃圾并不能稱之為垃圾。并且PHP在一個(gè)生命周期結(jié)束后就會釋放此進(jìn)程/線程所占的內(nèi)容,這種方式?jīng)Q定了PHP在前期不需要過多考慮內(nèi)存的泄露問題。

PHP5.3的垃圾回收

引入垃圾收集機(jī)制的目的是為了打破引用計(jì)數(shù)中的循環(huán)引用,從而防止因?yàn)檫@個(gè)而產(chǎn)生的內(nèi)存泄露。 垃圾收集機(jī)制基于PHP的動(dòng)態(tài)內(nèi)存管理而存在。PHP5.3為引入垃圾收集機(jī)制,在變量存儲的基本結(jié)構(gòu)上有一些變動(dòng).

struct _zval_struct {/* Variable information */ zvalue_value value;/* value */ zend_uint refcount__gc; zend_uchar type;/* active type */ zend_uchar is_ref__gc; };

添加了 __gc 以用于新的垃圾回收機(jī)制。

PHP5.3中的垃圾回收算法——Concurrent Cycle Collection in Reference Counted Systems

PHP5.3的垃圾回收算法仍然以引用計(jì)數(shù)為基礎(chǔ),但是不再是使用簡單計(jì)數(shù)作為回收準(zhǔn)則,而是使用了一種同步回收算法,這個(gè)算法由IBM的工程師在論文Concurrent Cycle Collection in Reference Counted Systems中提出。
論文較復(fù)雜, 列出一些大體描述。
首先PHP會分配一個(gè)固定大小的“根緩沖區(qū)”,這個(gè)緩沖區(qū)用于存放固定數(shù)量的zval,這個(gè)數(shù)量默認(rèn)是10,000,如果需要修改則需要修改源代碼Zend/zend_gc.c中的常量GC_ROOT_BUFFER_MAX_ENTRIES然后重新編譯。
由上文我們可以知道,一個(gè)zval如果有引用,要么被全局符號表中的符號引用,要么被其它表示復(fù)雜類型的zval中的符號引用。因此在zval中存在一些可能根(root)。這里我們暫且不討論P(yáng)HP是如何發(fā)現(xiàn)這些可能根的,這是個(gè)很復(fù)雜的問題,總之PHP有辦法發(fā)現(xiàn)這些可能根zval并將它們投入根緩沖區(qū)。
當(dāng)根緩沖區(qū)滿額時(shí),PHP就會執(zhí)行垃圾回收,此回收算法如下:
1、對每個(gè)根緩沖區(qū)中的根zval按照深度優(yōu)先遍歷算法遍歷所有能遍歷到的zval,并將每個(gè)zval的refcount減1,同時(shí)為了避免對同一zval多次減1(因?yàn)榭赡懿煌母鼙闅v到同一個(gè)zval),每次對某個(gè)zval減1后就對其標(biāo)記為“已減”。
2、再次對每個(gè)緩沖區(qū)中的根zval深度優(yōu)先遍歷,如果某個(gè)zval的refcount不為0,則對其加1,否則保持其為0。
3、清空根緩沖區(qū)中的所有根(注意是把這些zval從緩沖區(qū)中清除而不是銷毀它們),然后銷毀所有refcount為0的zval,并收回其內(nèi)存。
如果不能完全理解也沒有關(guān)系,只需記住PHP5.3的垃圾回收算法有以下幾點(diǎn)特性:
1、并不是每次refcount減少時(shí)都進(jìn)入回收周期,只有根緩沖區(qū)滿額后在開始垃圾回收。
2、可以解決循環(huán)引用問題。
3、可以總將內(nèi)存泄露保持在一個(gè)閾值以下。

PHP5.2與PHP5.3垃圾回收算法的性能比較

PHP Manual中的相關(guān)章節(jié):http://docs.php.net/manual/zh/features.gc.performance-considerations.php

首先是內(nèi)存泄露試驗(yàn),下面直接引用PHP Manual中的實(shí)驗(yàn)代碼和試驗(yàn)結(jié)果圖:

<?phpclass Foo {public $var = '3.1415962654'; }$baseMemory = memory_get_usage();for ( $i = 0; $i <= 100000; $i++ ) {$a = new Foo;$a->self = $a;if ( $i % 500 === 0 ){echo sprintf( '%8d: ', $i ), memory_get_usage() - $baseMemory, "\n";} }?>
gc-benchmark.png

可以看到在可能引發(fā)累積性內(nèi)存泄露的場景下,PHP5.2發(fā)生持續(xù)累積性內(nèi)存泄露,而PHP5.3則總能將內(nèi)存泄露控制在一個(gè)閾值以下(與根緩沖區(qū)大小有關(guān))。

與垃圾回收算法相關(guān)的PHP配置

1、可以通過修改php.ini中的zend.enable_gc來打開或關(guān)閉PHP的垃圾回收機(jī)制,也可以通過調(diào)用gc_enable()或gc_disable()打開或關(guān)閉PHP的垃圾回收機(jī)制。
2、在PHP5.3中即使關(guān)閉了垃圾回收機(jī)制,PHP仍然會記錄可能根到根緩沖區(qū),只是當(dāng)根緩沖區(qū)滿額時(shí),PHP不會自動(dòng)運(yùn)行垃圾回收
3、當(dāng)然,任何時(shí)候您都可以通過手工調(diào)用gc_collect_cycles()函數(shù)強(qiáng)制執(zhí)行內(nèi)存回收。



文/codefine(簡書作者)
原文鏈接:http://www.jianshu.com/p/63a381a7f70c
著作權(quán)歸作者所有,轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),并標(biāo)注“簡書作者”。

總結(jié)

以上是生活随笔為你收集整理的PHP内存管理 垃圾回收的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。