php回收,php的內存回收機制

本文目錄一覽:

php curl 是否需要主動關閉回收資源

可以不用,因為php頁面執行完後會自動回收釋放資源。

如果是命令行運行的程序,需要長時間運行的話,建議主動關閉鏈接,減少資源佔用。

從開發規範和習慣上來說,不需要的資源最好養成用完關閉或消除的習慣。

怎麼用php編寫一個像windows的回收站

刪除的文件設置狀態,比如0是正常的,1是放入回收站的。回收站只顯示狀態為1的文件。如果恢復文件,把1改為0就行了。

php有垃圾回收機制嗎

析構函數會在到某個對象的所有引用都被刪除或者當對象被顯式銷毀時執行(php手冊 語言參考-類與對象-構造函數和析構函數)。利用這一點,就可以知道php什麼時候執行了垃圾回收。通常垃圾回收都是在程序結束階段,但是如果讓系統知道這個變量以後不會使用了,php就會啟動垃圾回收的。

PHP命令執行PHP腳本,結束之前,內存會回收嗎

再詳細說下問題:

unix下,用php命令來執行php腳本,在php結束之前,內存有沒有機會被回收?新的GC算法有沒有機會被調用?

出現這個問題,是因為線上有個 離線數據導入腳本,需要把幾千萬行數據篩選入庫,發現,在執行過程中,到達一定程度,就會拋出 內存使用超過最大值。

1    Fatal error: Allowed memory size of 293601280 bytes exhausted    

那第一想到的就是程序是不是有什麼bug,造成內存超出,看了半天沒有發現問題,於是,突然出現了這個疑問。 那要解決這個疑問,最好的辦法就去翻源碼吧。

在之前我這麼說:

都知道,PHP5.3有了新的垃圾回收機制:GC,它不是重點,不談它的原理。

經過翻閱PHP源碼,發現,調用這個的時機是在 main/main.c ::php_request_shutdown這個函數中,

12    /* 7. Shutdown scanner/executor/compiler and restore ini entries */        zend_deactivate(TSRMLS_C);    

php_request_shutdown,通過名字就能看出,它是在php請求結束的時候執行的,在這裡會執行 gc_collect_cycles 來清理內存。

其實這句話是沒錯,但它只針對於SAPI接口(之前我就錯在這個地方。),在用PHP命令執行php腳本的時候,是不會執行這個php_request_shutdown的。

那回到最初的問題,過程中到底有沒有執行GC呢?

為了更直觀有效的知道答案,我選擇了最BT,最暴力的方法,攔截gc_collect_cycles,輸出error_log到文件,只要執行了,

那肯定會輸出log來。

重新編譯PHP後,果不其然,符合官方的說法,只要buckets滿超過默認值1000,就會啟動GC來清理沒用的內存,防止內存泄露。

那問 “什麼時間 觸發的GC呢?”,答 “buckets超過1000的時候啊”,這不屁話嘛,要的是真真正正的執行流程,so。。不斷的debug,

不斷的grep,不斷的step,不斷的C+T,終於搞清楚了。下面就來根據官方的說法詳細談談,PHP到底是怎麼觸發的。

有一點要注意,PHP的命令入口 和 sapi接口的入口 是不同的,我就載在這個地方,以為都公用一個。

測試代碼以官方文檔為例:

1234567891011121314    ?phpclass Foo{    public $var = ‘3.1415962654’;} for ( $i = 0; $i = 1000000; $i++ ){    $a = new Foo;    $a-self = $a;} echo memory_get_peak_usage(), “\n”;?    

這樣的代碼,在PHP5.3之前,肯定會造成大量的 內存泄露,不過,誰在開發時又能開發出這麼變態的代碼來?除非這個人很變態。^.*

那PHP的命令入口是什麼?流程又是什麼?

主要函數流程如下:

入口main函數(sapi/cli/php_cli.c) ==》php_execute_script(main/main.c)==zend_execute_scripts(Zend/zend.c)==execute(Zend/zend_vm_execute.h)

調用GC的地方在execute里。

簡單描述下這個過程,

main 是入口,它的作用是根據我們傳遞的參數做不同的設置,最後會把我們的php腳本作為一個zend_file_handle指針傳遞給

php_execute_script函數,zend_file_handle其實就是把FILE*做了一下封裝,保存了一些其他的文件信息。

php_execute_script會做一些文件檢查工作,把php腳本加到 哈希表included_files中。

php_execute_scripts 會執行 zend_compile_file函數來解釋我們寫的PHP代碼,最後執行execute。

應該都知道 Zend把腳本解析完會生成op代碼保存到 哈希表:active_op_array中,execute會逐個執行每個op,

op基本上都對應一個ZEND_ASSIGN_*_HANDLER這樣的一個宏,它就保存在active_op_array-opline-handlers中。

在進入到 execute之後:

首先初始化execute_data,它保存了很多重要信息,上下文信息,然後調用 ZEND_VM_SET_OPCODE宏,

把execute_data-opline的指針指向active_op_array-opline-handlers。

之後,execute會執行一個while循環,逐條執行opline:

123456789101112131415161718192021222324            while (1) {        int ret;#ifdef ZEND_WIN32                if (EG(timed_out)) {                        zend_timeout(0);                }#endif                 if ((ret = EX(opline)-handler(execute_data TSRMLS_CC))  0) {                        switch (ret) {                                case 1:                                        EG(in_execution) = original_in_execution;                                        return;                                case 2:                                        op_array = EG(active_op_array);                                        goto zend_vm_enter;                                case 3:                                        execute_data = EG(current_execute_data);                                default:                                        break;                        }                }         }    

每個handlers都會執行一個宏:ZEND_VM_NEXT_OPCODE(),它意思就是跳到下一個Opline,這樣就能逐條執行了。

最後跟蹤 上面的PHP代碼會執行 ZEND_ASSIGN_SPEC_CV_VAR_HANDLER這個宏,它是幹嘛的?他就是 變量賦值

下面代碼執行的操作:

1234    class A{ }$a=new A();    

這裡就會執行 這個宏。

在這個宏里有段代碼:

12345678910111213141516171819202122232425262728293031        static int ZEND_FASTCALL  ZEND_ASSIGN_SPEC_CV_VAR_HANDLER(ZEND_OPCODE_HANDLER_ARGS){        zend_op *opline = EX(opline);        zend_free_op free_op2;        zval *value = _get_zval_ptr_var(opline-op2, EX(Ts), free_op2 TSRMLS_CC);        zval **variable_ptr_ptr = _get_zval_ptr_ptr_cv(opline-op1, EX(Ts), BP_VAR_W TSRMLS_CC);         if (IS_CV == IS_VAR  !variable_ptr_ptr) {                if (zend_assign_to_string_offset(EX_T(opline-op1.u.var), value, IS_VAR TSRMLS_CC)) {                        if (!RETURN_VALUE_UNUSED(opline-result)) {                                EX_T(opline-result.u.var).var.ptr_ptr = EX_T(opline-result.u.var).var.ptr;                                ALLOC_ZVAL(EX_T(opline-result.u.var).var.ptr);                                INIT_PZVAL(EX_T(opline-result.u.var).var.ptr);                                ZVAL_STRINGL(EX_T(opline-result.u.var).var.ptr, Z_STRVAL_P(EX_T(opline-op1.u.var).str_offset.str)+EX_T(opline-op1.u.var).str_offset.offset, 1, 1);                        }                } else if (!RETURN_VALUE_UNUSED(opline-result)) {                        AI_SET_PTR(EX_T(opline-result.u.var).var, EG(uninitialized_zval_ptr));                        PZVAL_LOCK(EG(uninitialized_zval_ptr));                }        } else {                value = zend_assign_to_variable(variable_ptr_ptr, value, 0 TSRMLS_CC);                if (!RETURN_VALUE_UNUSED(opline-result)) {                        AI_SET_PTR(EX_T(opline-result.u.var).var, value);                        PZVAL_LOCK(value);                }        }         /* zend_assign_to_variable() always takes care of op2, never free it! */        if (free_op2.var) {zval_ptr_dtor(free_op2.var);};         ZEND_VM_NEXT_OPCODE();}    

free_op2.var保存的是 new A的對象.

free_op2.var這個是哪兒來的呢?

在整個execute期間,維持一個 execute_data結構,裡面有個 Ts指針

1    union _temp_variable *Ts;    

它用來保存一些臨時的變量信息,比如 new A(),這個會保存到Ts鏈表裡,

opline-op2.u.var這個裡面保存了此臨時變量所在的位置,然後Ts+這個值是一個zval*指針,它就保存了new A產生的對象.

在代碼中

1    if (free_op2.var) {zval_ptr_dtor(free_op2.var);};    

zval_ptr_dtor會根據free_op2.var的值執行到 Zend/zend_execute_API.c::_zval_ptr_dtor函數中,

1234567891011121314151617181920212223242526    ZEND_API void _zval_ptr_dtor(zval **zval_ptr ZEND_FILE_LINE_DC) /* {{{ */{        zval *zv = *zval_ptr; #if DEBUG_ZEND=2        printf(“Reducing refcount for %x (%x): %d-%d\n”, *zval_ptr, zval_ptr, Z_REFCOUNT_PP(zval_ptr), Z_REFCOUNT_PP(zval_ptr) – 1);#endif        Z_DELREF_P(zv);        if (Z_REFCOUNT_P(zv) == 0) {                TSRMLS_FETCH();                 if (zv != EG(uninitialized_zval)) {                        GC_REMOVE_ZVAL_FROM_BUFFER(zv);                        zval_dtor(zv);                        efree_rel(zv);                }        } else {                TSRMLS_FETCH();                 if (Z_REFCOUNT_P(zv) == 1) {                        Z_UNSET_ISREF_P(zv);                }                 GC_ZVAL_CHECK_POSSIBLE_ROOT(zv);        }}    

GC_ZVAL_CHECK_POSSIBLE_ROOT(zv);

它就是最終GC算法執行的地方.

gc_collect_cycles就在這個宏中執行了..

所以..

回到上面的問題,

php無論在SAPI接口或命令端,都會執行 GC算法來進行垃圾內存回收.

原創文章,作者:小藍,如若轉載,請註明出處:https://www.506064.com/zh-hant/n/159728.html

(0)
打賞 微信掃一掃 微信掃一掃 支付寶掃一掃 支付寶掃一掃
小藍的頭像小藍
上一篇 2024-11-20 00:15
下一篇 2024-11-20 00:15

相關推薦

  • PHP和Python哪個好找工作?

    PHP和Python都是非常流行的編程語言,它們被廣泛應用於不同領域的開發中。但是,在考慮擇業方向的時候,很多人都會有一個問題:PHP和Python哪個好找工作?這篇文章將從多個方…

    編程 2025-04-29
  • Python創建分配內存的方法

    在python中,我們常常需要創建並分配內存來存儲數據。不同的類型和數據結構可能需要不同的方法來分配內存。本文將從多個方面介紹Python創建分配內存的方法,包括列表、元組、字典、…

    編程 2025-04-29
  • PHP怎麼接幣

    想要在自己的網站或應用中接受比特幣等加密貨幣的支付,就需要對該加密貨幣擁有一定的了解,並使用對應的API進行開發。本文將從多個方面詳細闡述如何使用PHP接受加密貨幣的支付。 一、環…

    編程 2025-04-29
  • Python變量在內存中的存儲

    該文章將從多個方面對Python變量在內存中的存儲進行詳細闡述,包括變量的聲明和賦值、變量的引用和指向、內存地址的變化、內存管理機制等。 一、聲明和賦值 在Python中,變量聲明…

    編程 2025-04-29
  • Python計算內存佔用

    Python是一種高級的、解釋性的、面向對象的、動態的程序語言,因其易於學習、易於閱讀、可移植性好等優點,越來越受到開發者的青睞。當我們編寫Python代碼時,可能經常需要計算程序…

    編程 2025-04-28
  • 使用Go-Redis獲取Redis集群內存使用率

    本文旨在介紹如何使用Go-Redis獲取Redis集群的內存使用率。 一、Go-Redis簡介 Go-Redis是一個用於連接Redis服務器的Golang客戶端。它支持Redis…

    編程 2025-04-28
  • 使用PHP foreach遍歷有相同屬性的值

    本篇文章將介紹如何使用PHP foreach遍歷具有相同屬性的值,並給出相應的代碼示例。 一、基礎概念 在講解如何使用PHP foreach遍歷有相同屬性的值之前,我們需要先了解幾…

    編程 2025-04-28
  • Spring S_CSRF防護機制實現及應用

    Spring S_CSRF防護機制是Spring Security框架提供的一個針對跨站請求偽造攻擊(CSRF)的保護機制。本文將從以下幾個方面詳細介紹Spring S_CSRF防…

    編程 2025-04-28
  • PHP獲取301跳轉後的地址

    本文將為大家介紹如何使用PHP獲取301跳轉後的地址。301重定向是什麼呢?當我們訪問一個網頁A,但是它已經被遷移到了另一個地址B,此時若服務器端做了301重定向,那麼你的瀏覽器在…

    編程 2025-04-27
  • PHP登錄頁面代碼實現

    本文將從多個方面詳細闡述如何使用PHP編寫一個簡單的登錄頁面。 1. PHP登錄頁面基本架構 在PHP登錄頁面中,需要包含HTML表單,用戶在表單中輸入賬號密碼等信息,提交表單後服…

    編程 2025-04-27

發表回復

登錄後才能評論