程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> 從幾個簡單的程序看PHP的垃圾回收機制

從幾個簡單的程序看PHP的垃圾回收機制

編輯:關於PHP編程

每一種計算機語言都有自己的自動垃圾回收機制,讓程序員不必過分關心程序內存分配,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.

好,下一個:

<?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?

君請看:

<?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.

君再看:

<?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

君且看:

<?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 有更深刻的理解了。那麼下面與之同工的例子自然好理解了。

<?php                                                                         
error_reporting(E_ALL); 
$a = 'I am test.'; 
$b = & $a; 
$b = null; 
echo '$a = '. $a ."n"; 
echo '$b = '. $b ."n"; 
?>

下面我們來詳細分析 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 引擎才會將這塊內存回收。

看看下面的代碼與其結果:

<?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() 的操作已經斷開了指向,所以後繼的操作不會對符號表的任何內存的引用記數造成影響了。

賦值 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 中直接將真實文件干掉了,所有指向它的軟鏈接自然成了空鏈了。

之前在討論到這些細節點時有很多想當然的念頭,在實際的執行了測試代碼後才發現: 哦,原來如此!紙上得來終覺淺,絕知此事須躬行。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved