why php7 throw Fatal error: Allowed memory size of xx bytes exhausted (tried to allocate xx bytes)

用swoole 寫的協程 server能做到高併發,但這有一個問題必須考慮的,多個協程跑在一個進程內,某個協程把所有的內存吃光了,

其它協程怎麼辦?這個進程會怎麼處理?

 

首先不要管協程這麼一回事,php最初設計就是一個腳本引擎,目前php版本實現的是,遇到 內存不足,不是一個 E_RECOVERABLE_ERROR,直接就 調用shutdown 函數了。

當swoole擴展引入協程概念時,還是跑在這個腳本引擎上,

一個協程吃光內存  --觸發-->   腳本shutdown  --導致-->   進程內所有資源被回收(包括其它協程)

<?php
register_shutdown_function(function() {
    echo "register_shutdown_function\n";
});

$var = 'clwu';
try {
    new aa;
    $var = @str_repeat($var, 1024*1024*1024*1024);
} catch (\Throwable $e) {
    echo 'OOM',PHP_EOL;
}

上面的實驗代碼中,new aa 找不到這個類,php 可以catch 到這個異常,但下一行 str_repeat($var, 1024*1024*1024*1024) 會吃光所有內存,拋出一個 php 沒有catch 的致命錯誤:

1)爲什麼 new aa 找不到這個類拋出的異常可以 catch ?

因爲 new aa 是轉換成執行php腳本引擎的代碼,引擎邏輯有 判斷 找不到這個類就 HANDLE_EXCEPTION()

 

2)爲什麼 str_repeat($var, 1024*1024*1024*1024) 吃光內存的異常沒有 catch 呢?因爲 php 腳本引擎的邏輯就沒有寫要去檢測有沒有異常拋出

php腳本引擎把 str_repeat 轉換爲一個可以 FAST CALL 的外部函數調用(底層libc庫的原生代碼),腳本引擎在調用完這個函數時,是執行

ZEND_VM_NEXT_OPCODE()

而不是

ZEND_VM_NEXT_OPCODE_CHECK_EXCEPTION()

,see 腳本引擎的設計時就不知道要怎麼處理 外部資源的問題。目前php版本的實現是拋出一個致命的錯誤然後腳本中止。

 

瞭解了問題的根源,就有了問題的解決方案了,我不要改動 php的源碼,因爲到時 服務器自動更新到新版本php時又會覆蓋我的改動,facebook自己維護的HHVM就不能很好的跟上生態。

還是要自己在協程的最開始主動用 memory_get_usage() 檢查進程的內存是否還足夠,如果不足夠就 拒絕服務,以免把其它協程給坑了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章