PHP的垃圾回收機制-PHP高級面試題+詳解

面試10家公司,收穫9個offer,2020年PHP 面試問題圖標php

ps:本篇內容包括精選面試題與知識篇。java

PHP面試題關於PHP的垃圾回收機制,PHP的垃圾回收機制引用計數 (reference counting) GC 機制,PHP能夠自動進行內存管理,清除不須要的對象,PHP面試題分享PHP關於垃圾回收機制的面試題:laravel

★個人php學習交流社羣——856460874。羣內管理已準備好 整理好的BAT等一線大廠進階知識體系備好(相關學習資料以及筆面試題)歡迎獲取一塊兒晉升=點擊加程序員

面試題篇

  • 介紹一下PHP的垃圾回收機制

PHP使用了引用計數(reference counting)GC機制,同時使用根緩衝區機制,當php發現有存在循環引用的zval時,就會把其投入到根緩衝區,當根緩衝區達到配置文件中的指定數量後,就會進行垃圾回收,以此解決循環引用致使的內存泄漏問題。面試

  • 1. 若是引用計數減小到零,所在變量容器將被清除(free),不屬於垃圾;
  • 2. 若是一個zval的引用計數減小後還大於0,那麼它會進入垃圾週期。其次,在一個垃圾週期中,經過檢查引用計數是否減1,而且檢查哪些變量容器的引用次數是零,來發現哪部分是垃圾。

每一個對象都內含一個引用計數器refcount,每一個reference鏈接到對象,計數器加1。當reference離開生存空間或被設爲 NULL,計數器減1。當某個對象的引用計數器爲零時,PHP知道你將再也不須要使用這個對象,釋放其所佔的內存空間。算法

  • 下列關於PHP垃圾回收的說法,錯誤的是?

A、開啓/關閉垃圾回收機制能夠經過修改php配置實現sql

B、能夠在程序中使用gc_enable() 和 gc_disable()開啓和關閉。shell

C、PHP中的垃圾回收機制,會大幅度提高系統性能。編程

D、開啓垃圾回收機制後,針對內存泄露的狀況,能夠節省大量的內存空間,可是因爲垃圾回收算法運行耗費時間,開啓垃圾回收算法會增長腳本的執行時間。數組

參考答案:C
答案解析:PHP中的垃圾回收機制,僅僅在循環回收算法確實運行時會有時間消耗上的增長。可是在日常的(更小的)腳本中應根本就沒有性能影響。

  • php垃圾回收機制的說法錯誤的是?

A、在一個垃圾週期中,經過檢查引用計數是否減1,而且檢查哪些變量容器的引用次數是零,來發現哪部分是垃圾

B、能夠經過調用gc_enable() 和 gc_disable()函數來打開和關閉垃圾回收機制

C、經過清理未被使用的變量來節省內存的佔用

D、php代碼執行完畢後會自動執行垃圾回收,因此不須要手動執行垃圾回收

參考答案:D
答案解析:php一段代碼有可能要長時間執行,但若此期間有未引用的變量的話,就會佔用內存的空間,致使運行緩慢等問題


知識篇

1、概念

垃圾回收是一個多數編程語言中都帶有的內存管理機制。與非託管性語言相反:C, C++ 和 Objective C,用戶須要手動收集內存,帶有 GC 機制的語言:Java, javaScript 和 PHP 能夠自動管理內存。

垃圾回收機制(gc)顧名思義,就是廢物重利用的意思,是一種動態存儲分配的方案。它會自動釋放程序再也不須要的已分配的內存塊。垃圾回收機制可讓程序員沒必要過度關心程序內存分配,從而將更多的精力投入到業務邏輯。

在如今的流行各類語言當中,垃圾回收機制是新一代語言所共有的特徵,如Python、PHP、C#、Ruby等都使用了垃圾回收機制。

2、PHP垃圾回收機制

一、在PHP5.3版本以前,使用的垃圾回收機制是單純的「引用計數」。

什麼叫作引用計數?
因爲PHP是用C來寫的,C裏面有一種東西叫作結構體,咱們PHP的變量在C中就是用這種方式存儲的。
每一個PHP的變量都存在於一個叫作zval的容器中,一個zval容器,除了包含變量名和值,還包括兩個字節的額外信息:
● 一個叫作'is_ref',是個布爾值,用來表示這個變量是否屬於引用集合,經過這個字節,咱們php才能把普通變量和引用變量區分開來。
● 第二個額外字節就是'refcount',用來表示指向這個容器的變量的個數。

即:

① 每一個內存對象都分配一個計數器,當內存對象被變量引用時,計數器+1;

② 當變量引用撤掉後(執行unset()後),計數器-1;

③ 當計數器=0時,代表內存對象沒有被使用,該內存對象則進行銷燬,垃圾回收完成。

而且PHP在一個生命週期結束後就會釋放此進程/線程所佔的內容,這種方式決定了PHP在前期不須要過多考慮內存的泄露問題。

可是當兩個或多個對象互相引用造成環狀後,內存對象的計數器則不會消減爲0;這時候,這一組內存對象已經沒用了,可是不能回收,從而致使內存泄露的現象。

php5.3開始,使用了新的垃圾回收機制,在引用計數基礎上,實現了一種複雜的算法,來檢測內存對象中引用環的存在,以免內存泄露。

  • 二、隨着PHP的發展,PHP開發者的增長以及其所承載的業務範圍的擴大,在PHP5.3中引入了更加完善的垃圾回收機制,新的垃圾回收機制解決了沒法處理循環的引用內存泄漏問題。

如官方文檔所說:每一個php變量存在一個叫"zval"的變量容器中。一個zval變量容器,除了包含變量的類型和值,還包括兩個字節的額外信息。第一個是"is_ref",是個bool值,用來標識這個變量是不是屬於引用集合(reference set)。經過這個字節,php引擎才能把普通變量和引用變量區分開來,因爲php容許用戶經過使用&來使用自定義引用,zval變量容器中還有一個內部引用計數機制,來優化內存使用。

第二個額外字節是"refcount",用以表示指向這個zval變量容器的變量(也稱符號即symbol)個數。全部的符號存在一個符號表中,其中每一個符號都有做用域(scope)。

官方文檔所說,可使用Xdebug來檢查引用計數狀況:

<?php
$a = "new string";
$c = $b = $a;
xdebug_debug_zval( 'a' );
unset( $b, $c );
xdebug_debug_zval( 'a' );
?>

以上例程會輸出:

a: (refcount=3, is_ref=0)='new string'
a: (refcount=1, is_ref=0)='new string'

注意:從PHP7的NTS版本開始,以上例程的引用將再也不被計數,即$c=$b=$a以後a的引用計數也是1.具體分類以下:

在PHP 7中,zval能夠被引用計數或不被引用。在zval結構中有一個標誌肯定了這一點。

① 對於null,bool,int和double的類型變量,refcount永遠不會計數;

② 對於對象、資源類型,refcount計數和php5的一致;

③ 對於字符串,未被引用的變量被稱爲「實際字符串」。而那些被引用的字符串被重複刪除(即只有一個帶有特定內容的被插入的字符串)並保證在請求的整個持續時間內存在,因此不須要爲它們使用引用計數;若是使用了opcache,這些字符串將存在於共享內存中,在這種狀況下,您不能使用引用計數(由於咱們的引用計數機制是非原子的);

④對於數組,未引用的變量被稱爲「不可變數組」。其數組自己計數與php5一致,可是數組裏面的每一個鍵值對的計數,則按前面三條的規則(即若是是字符串也不在計數);若是使用opcache,則代碼中的常量數組文字將被轉換爲不可變數組。

再次,這些生活在共享內存,所以不能使用refcounting。

咱們的demo例子以下:

<?php
echo '測試字符串引用計數';
$a = "new string";
$b = $a;
xdebug_debug_zval( 'a' );
unset( $b);
xdebug_debug_zval( 'a' );
$b = &$a;
xdebug_debug_zval( 'a' );
echo '測試數組引用計數';
$c = array('a','b');
xdebug_debug_zval( 'c' );
$d = $c;
xdebug_debug_zval( 'c' );
$c[2]='c';
xdebug_debug_zval( 'c' );
echo '測試int型計數';
$e = 1;
xdebug_debug_zval( 'e' );

看到的輸出以下:

3、回收週期

默認的,PHP的垃圾回收機制是打開的,而後有個php.ini設置容許你修改它:zend.enable_gc 。

當垃圾回收機制打開時,算法會判斷每當根緩存區存滿時,就會執行循環查找。根緩存區有固定的大小,默認10,000,能夠經過修改PHP源碼文件Zend/zend_gc.c中的常量GC_ROOT_BUFFER_MAX_ENTRIES,而後從新編譯PHP,來修改這個值。當垃圾回收機制關閉時,循環查找算法永不執行,然而,根將一直存在根緩衝區中,無論在配置中垃圾回收機制是否激活。

除了修改配置zend.enable_gc ,也能經過分別調用gc_enable() 和 gc_disable()函數在運行php時來打開和關閉垃圾回收機制。調用這些函數,與修改配置項來打開或關閉垃圾回收機制的效果是同樣的。即便在可能根緩衝區還沒滿時,也能強制執行週期回收。你能調用gc_collect_cycles()函數達到這個目的。這個函數將返回使用這個算法回收的週期數。

容許打開和關閉垃圾回收機制而且容許自主的初始化的緣由,是因爲你的應用程序的某部分多是高時效性的。在這種狀況下,你可能不想使用垃圾回收機制。固然,對你的應用程序的某部分關閉垃圾回收機制,是在冒着可能內存泄漏的風險,由於一些可能根也許存不進有限的根緩衝區。

所以,就在你調用gc_disable()函數釋放內存以前,先調用gc_collect_cycles()函數可能比較明智。由於這將清除已存放在根緩衝區中的全部可能根,而後在垃圾回收機制被關閉時,可留下空緩衝區以有更多空間存儲可能根。

4、性能影響

一、內存佔用空間的節省

首先,實現垃圾回收機制的整個緣由是爲了一旦先決條件知足,經過清理循環引用的變量來節省內存佔用。在PHP執行中,一旦根緩衝區滿了或者調用gc_collect_cycles() 函數時,就會執行垃圾回收。

二、執行時間增長

垃圾回收影響性能的第二個領域是它釋放已泄漏的內存耗費的時間。

一般,PHP中的垃圾回收機制,僅僅在循環回收算法確實運行時會有時間消耗上的增長。可是在日常的(更小的)腳本中應根本就沒有性能影響。

三、在日常腳本中有循環回收機制運行的狀況下,內存的節省將容許更多這種腳本同時運行在你的服務器上。由於總共使用的內存沒達到上限。

這種好處在長時間運行腳本中尤爲明顯,諸如長時間的測試套件或者daemon腳本此類。同時,對一般比Web腳本運行時間長的腳本應用程序,新的垃圾回收機制,應該會大大改變一直以來認爲內存泄漏問題難以解決的見解。

最後,祝全部你們在面試中過關斬將,拿到心儀offer。

對此我整理了一些資料,包括但不限於:分佈式架構、高可擴展、高性能、高併發、服務器性能調優、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql優化、shell腳本、Docker、微服務、Nginx等多個知識點高級進階乾貨須要的能夠免費分享給你們

若是想與一羣3-8年資深開發者一塊兒交流學習的話,須要,個人官方羣-點擊此處

騰訊T3-T4標準精品PHP架構師教程目錄大全,只要你看完保證薪資上升一個臺階(持續更新)​!

本文由博客羣發一文多發等運營工具平臺 OpenWrite 發佈

相關文章
相關標籤/搜索