PHP 記憶體回收機制

來源:互聯網
上載者:User

 

每一種電腦語言都有自己的自動記憶體回收機制,讓程式員不必過分關心程式記憶體配置,php也不例外,但是在物件導向編程(OOP)編程中,有些對象需要顯式的銷毀;防止程式執行記憶體溢出。 
一、PHP 記憶體回收機制(Garbage Collector 簡稱GC) 
在PHP中,沒有任何變數指向這個對象時,這個對象就成為垃圾。PHP會將其在記憶體中銷毀;這是PHP的GC垃圾處理機制,防止記憶體溢出。 
當一個PHP線程結束時,當前佔用的所有記憶體空間都會被銷毀,當前程式中所有對象同時被銷毀。GC進程一般都跟著每起一個SESSION而開始啟動並執行.gc目的是為了在session檔案到期以後自動銷毀刪除這些檔案. 

二、__destruct /unset 
__destruct() 解構函式,是在垃圾對象被回收時執行。 
unset 銷毀的是指向對象的變數,而不是這個對象。 

三、 Session 與 GC 
由於PHP的工作機制,它並沒有一個daemon線程來週期性掃描Session資訊並判斷其是否失效,當一個有效請求發生時,PHP 會根據全域變數 session.gc_probability和session.gc_divisor的值,來決定是否啟用一個GC, 在預設情況下,session.gc_probability=1, session.gc_divisor =100也就是說有1%的可能性啟動GC(也就是說100個請求中只有一個gc會伴隨100個中的某個請求而啟動). 
GC的工作就是掃描所有的Session資訊,用目前時間減去session最後修改的時間,同session.gc_maxlifetime參數進行比較,如果存留時間超過gc_maxlifetime(預設24分鐘),就將該session刪除。 
但是,如果你Web伺服器有多個網站,多個網站時,GC處理session可能會出現意想不到的結果,原因就是:GC在工作時,並不會區分不同網站的session. 

那麼這個時候怎麼解決呢? 
1. 修改session.save_path,或使用session_save_path()讓每個網站的session儲存到一個專用目錄, 
2. 提供GC的啟動率,自然,GC的啟動率提高,系統的效能也會相應減低,不推薦。 
3. 在代碼中判斷當前session的存留時間,利用session_destroy()刪除.

看下面的例子

Example 1: gc.php
<?php 
error_reporting(E_ALL); 
$a = 'I am test.'; 
$b = & $a; 

echo $b ."\n"; 
?>

不用說 % php -f gc.php 輸出結果非常明了:
hy0kl% php -f gc.php 
I am test.

好,下一個:
Example 2:
<?php 
error_reporting(E_ALL); 
$a = 'I am test.'; 
$b = & $a; 

$b = 'I will change?';                                                          

echo $a ."\n"; 
echo $b ."\n"; 
?>
執行結果依然很明顯:
hy0kl% php -f gc.php 
I will change?
I will change?

君請看:
Example 3:
<?php 
error_reporting(E_ALL); 
$a = 'I am test.'; 
$b = & $a;  

unset($a); 

echo $a ."\n"; 
echo $b ."\n";
?>
是不是得想一下下呢?
hy0kl% php -f gc.php 
Notice: Undefined variable: a in /usr/local/www/apache22/data/test/gc.php on line 8
I am test.
有點犯迷糊了嗎?

君再看:
Example 4:
<?php 
error_reporting(E_ALL); 
$a = 'I am test.'; 
$b = & $a; 

unset($b);                                                                      

echo $a ."\n"; 
echo $b ."\n";
?>
其實如果 Example 3 理解了,這個與之異曲同工.
hy0kl% php -f gc.php 
I am test.
Notice: Undefined variable: b in /usr/local/www/apache22/data/test/gc.php on line 9

君且看:
Example 5:
<?php 
error_reporting(E_ALL); 
$a = 'I am test.'; 
$b = & $a; 

$a = null; 

echo '$a = '. $a ."\n"; 
echo '$b = '. $b ."\n"; 
?>
猛的第一感覺是什麼樣的?
hy0kl% php -f gc.php 
$a = 
$b =
沒錯,這就是輸出結果,對 PHP GC 已有深入理解的 phper 不會覺得有什麼奇怪,說實話,當我第一次運行這段代碼時很意外,卻讓我對 PHP GC 有更深刻的理解了.那麼下面與之同工的例子自然好理解了.

Example 6:
<?php                                                                         
error_reporting(E_ALL); 
$a = 'I am test.'; 
$b = & $a; 

$b = null; 

echo '$a = '. $a ."\n"; 
echo '$b = '. $b ."\n"; 
?>

下面我們來詳細分析 GC 與引用.
1. 所有例子中,建立了一個變數,這個過程通俗一點講:是在記憶體中開闢了一塊空間,在裡面存放了一個字串 I am test. . PHP 內部有個符號表,用來記錄各塊記憶體引用計數,那麼此時會將這塊記憶體的引用計數 加 1,並且用一個名為 $a 的標籤(變數)指向這塊記憶體,方便依標籤名來操作記憶體.

2. 對變數 $a 進行 & 操作,我的理解是找到 $a 所指向的記憶體,並為 $b 建立同樣的一引用指向,並將存放字串 I am test. 的記憶體塊在符號表中引用計數 加 1.換言之,我們的指令碼執行到這一行的時候,存放字串 I am test. 的那塊記憶體被引用了兩次.這裡要強調的是, & 操作是建立了引用指向,而不是指標, PHP 沒有指標的概念!同時有人提出說類似於 UNIX 的檔案軟連結.可以在一定程度上這麼理解: 存放字元 I am test. 的那塊記憶體是我們的一個真實的檔案,而變數 $a 與 $b 是針對真實檔案建立的軟連結,但它們指向的是同一個真實檔案. So, 我們看到,在 Example 2   中給 $b 賦值的同時, $a 的值也跟著變化了.與通過某一軟鏈操作了檔案類似.

3. 在 Example 3 與 4 中,進行了 unset() 操作.根據實際的執行結果,可以看出: unset() 只是斷開這個變數對它原先指向的記憶體的引用,使變數本身成為沒有定義過Null 參考,所在調用時發出了 Notice ,並且使那塊記憶體在符號表中引用計數 減 1,並沒有影響到其他指向這塊記憶體的變數.換言之,只有當一塊記憶體在符號表中的引用計數為 0 時, PHP 引擎才會將這塊記憶體回收.

PHP 手冊
4.0.0                unset() became an expression. (In PHP 3,       unset() would always return 1). 
這意味著什麼?
看看下面的代碼與其結果:
<?php 
error_reporting(E_ALL); 
$a = 'I am test.'; 
$b = & $a; 

unset($a); 
unset($a); 
unset($a); 

echo '$a = '. $a ."\n"; 
echo '$b = '. $b ."\n"; 
?>
hy0kl% php -f gc.php 

Notice: Undefined variable: a in /usr/local/www/apache22/data/test/gc.php on line 10
$a = 
$b = I am test.
第一次 unset() 的操作已經斷開了指向,所以後繼的操作不會對符號表的任何記憶體的引用記數造成影響了.

4. 通過 Example 5 & 6 可以明確無誤得出: 賦值 null操作是相當猛的,它會直接將變數所指向的記憶體在符號號中的引用計數置 0,那這塊記憶體自然被引擎回收了,至於何時被再次利用不得而知,有可能馬上被用作儲存別的資訊,也許再也沒有使用過.但是無論如何,原來所有指向那塊記憶體變數都將無法再操作被回收的記憶體了,任何試圖調用它的變數都將返回 null.

<?php 
error_reporting(E_ALL); 
$a = 'I am test.'; 
$b = & $a; 

$b = null; 

echo '$a = '. $a ."\n"; 
echo '$b = '. $b ."\n"; 

if (null === $a) 
{                                                                               
echo '$a is null.';    
} else 

echo 'The type of $a is unknown.';    

?>
hy0kl% php -f gc.php 
$a = 
$b = 
$a is null.

綜上所述,充分說明了為什麼我們在看開源產品源碼的時候,常看到一些比較大的臨時變數,或使用完不再調用的重用資訊都會被集中或顯示的賦值為 null 了.它相當於 UNIX 中直接將真實檔案幹掉了,所有指向它的軟連結自然成了空鏈了.
之前在討論到這些細節點時有很多想當然的念頭,在實際的執行了測試代碼後才發現: 哦,原來如此!
紙上得來終覺淺,絕知此事要躬行.

 



聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.