深刻探討PHP的垃圾回收機制

每一種計算機語言都有本身的自動垃圾回收機制,讓程序員沒必要過度關心程序內存分配,php也不例外,可是在面向對象編程(OOP)編程中,有些對象須要顯式的銷燬;防止程序執行內存溢出。
1、PHP 垃圾回收機制(Garbage Collector 簡稱GC)
在PHP中,沒有任何變量指向這個對象時,這個對象就成爲垃圾。PHP會將其在內存中銷燬;這是PHP的GC垃圾處理機制,防止內存溢出。當一個PHP線程結束時,當前佔用的全部內存空間都會被銷燬,當前程序中全部對象同時被銷燬。GC進程通常都跟着每起一個SESSION而開始運行的。gc目的是爲了在session文件過時之後自動銷燬刪除這些文件。
2、__destruct /unset
__destruct() 析構函數,是在垃圾對象被回收時執行。unset 銷燬的是指向對象的變量,而不是這個對象。
3、 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
1 <?php 
2 error_reporting(E_ALL); 
3 $a = 'I am test.'
4 $b = & $a
5   
6 echo $b ."\n"
7 ?>
不用說 % php -f gc.php 輸出結果很是明瞭:
1 hy0kl% php -f gc.php 
2 I am test.
好,下一個:
01 <?php 
02 error_reporting(E_ALL); 
03 $a = 'I am test.'
04 $b = & $a
05   
06 $b = 'I will change?';                                                          
07   
08 echo $a ."\n"
09 echo $b ."\n"
10 ?>
執行結果依然很明顯:
1 hy0kl% php -f gc.php 
2 I will change?
3 I will change?
君請看:
01 <?php 
02 error_reporting(E_ALL); 
03 $a = 'I am test.'
04 $b = & $a;  
05   
06 unset($a); 
07   
08 echo $a ."\n"
09 echo $b ."\n";
10 ?>
是否是得想一下下呢?
1 hy0kl% php -f gc.php 
2 Notice: Undefined variable: a in /usr/local/www/apache22/data/test/gc.php on line 8
3 I am test.
君再看:
01 <?php 
02 error_reporting(E_ALL); 
03 $a = 'I am test.'
04 $b = & $a
05   
06 unset($b);                                                                      
07   
08 echo $a ."\n"
09 echo $b ."\n";
10 ?>
其實若是 Example 3 理解了,這個與之殊途同歸。
1 hy0kl% php -f gc.php 
2 I am test.
3 Notice: Undefined variable: b in /usr/local/www/apache22/data/test/gc.php on line 9
君且看:
01 <?php 
02 error_reporting(E_ALL); 
03 $a = 'I am test.'
04 $b = & $a
05   
06 $a = null; 
07   
08 echo '$a = '. $a ."\n"
09 echo '$b = '. $b ."\n"
10 ?>
猛的第一感受是什麼樣的?
1 hy0kl% php -f gc.php 
2 $a
3 $b =
沒錯,這就是輸出結果,對 PHP GC 已有深刻理解的 phper 不會以爲有什麼奇怪,說實話,當我第一次運行這段代碼時很意外,卻讓我對 PHP GC 有更深入的理解了。那麼下面與之同工的例子天然好理解了。
01 <?php                                                                         
02 error_reporting(E_ALL); 
03 $a = 'I am test.'
04 $b = & $a
05   
06 $b = null; 
07   
08 echo '$a = '. $a ."\n"
09 echo '$b = '. $b ."\n"
10 ?>
下面咱們來詳細分析 GC 與引用:
全部例子中,建立了一個變量,這個過程通俗一點講:是在內存中開闢了一塊空間,在裏面存放了一個字符串 I am test。 。 PHP 內部有個符號表,用來記錄各塊內存引用計數,那麼此時會將這塊內存的引用計數 加 1,而且用一個名爲 $a 的標籤(變量)指向這塊內存,方便依標籤名來操做內存。
對變量 $a 進行 & 操做,個人理解是找到 $a 所指向的內存,併爲 $b 創建一樣的一引用指向,並將存放字符串 I am test。 的內存塊在符號表中引用計數 加 1。換言之,咱們的腳本執行到這一行的時候,存放字符串 I am test。 的那塊內存被引用了兩次。這裏要強調的是, & 操做是創建了引用指向,而不是指針, PHP 沒有指針的概念!同時有人提出說相似於 UNIX 的文件軟連接。能夠在必定程度上這麼理解: 存放字符 I am test。 的那塊內存是咱們的一個真實的文件,而變量 $a 與 $b 是針對真實文件創建的軟連接,但它們指向的是同一個真實文件。 So, 咱們看到,在 Example 2 中給 $b 賦值的同時, $a 的值也跟着變化了。與經過某一軟鏈操做了文件相似。
在 Example 3 與 4 中,進行了 unset() 操做。根據實際的執行結果,能夠看出: unset() 只是斷開這個變量對它原先指向的內存的引用,使變量自己成爲沒有定義過空引用,所在調用時發出了 Notice ,而且使那塊內存在符號表中引用計數 減 1,並無影響到其餘指向這塊內存的變量。換言之,只有當一塊內存在符號表中的引用計數爲 0 時, PHP 引擎纔會將這塊內存回收。
看看下面的代碼與其結果:
01 <?php 
02 error_reporting(E_ALL); 
03 $a = 'I am test.'
04 $b = & $a
05   
06 unset($a); 
07 unset($a); 
08 unset($a); 
09   
10 echo '$a = '. $a ."\n"
11 echo '$b = '. $b ."\n"
12 ?>
輸出:
1 hy0kl% php -f gc.php 
2   
3 Notice: Undefined variable: a in /usr/local/www/apache22/data/test/gc.php on line 10
4 $a
5 $b = I am test.
第一次 unset() 的操做已經斷開了指向,因此後繼的操做不會對符號表的任何內存的引用記數形成影響了。
賦值 null操做是至關猛的,它會直接將變量所指向的內存在符號號中的引用計數置 0,那這塊內存天然被引擎回收了,至於什麼時候被再次利用不得而知,有可能立刻被用做存儲別的信息,也許再也沒有使用過。可是不管如何,原來全部指向那塊內存變量都將沒法再操做被回收的內存了,任何試圖調用它的變量都將返回 null。
01 <?php 
02 error_reporting(E_ALL); 
03 $a = 'I am test.'
04 $b = & $a
05   
06 $b = null; 
07   
08 echo '$a = '. $a ."\n"
09 echo '$b = '. $b ."\n"
10   
11 if (null === $a
12 {                                                                               
13 echo '$a is null.';    
14 } else 
15
16 echo 'The type of $a is unknown.';    
17
18 ?>
輸出:
1 hy0kl% php -f gc.php 
2 $a
3 $b
4 $a is null.
綜上所述,充分說明了爲何咱們在看開源產品源碼的時候,常看到一些比較大的臨時變量,或使用完再也不調用的重用信息都會被集中或顯示的賦值爲 null 了。它至關於 UNIX 中直接將真實文件幹掉了,全部指向它的軟連接天然成了空鏈了。
以前在討論到這些細節點時有不少想固然的念頭,在實際的執行了測試代碼後才發現: 哦,原來如此!紙上得來終覺淺,絕知此事須躬行。
相關文章
相關標籤/搜索