請手動釋放你的資源

2021-06-09 00:21:34 字數 2808 閱讀 4802

我從來不認為這個問題是個問題, 直到昨天.

昨天晚上的時候, 我提交了乙個rfc, 關於引入finally到php, 實現這個功能的出發點很簡單, 因為我看見不少人的需求, 另外還有就是stas說, 一直只看到討論, 沒看到有人實現. 於是我就給實現了.

「php在請求結束後會釋放所有的資源, 所以我們沒有必要呼叫fclose,或者mysql_close來釋放資源, php會替我們做」

並且他表示, 他從來都不會呼叫fclose, 認為fclose的存在只是為了繼承c函式族.

我很驚訝, 我也不知道還有多少人是和他一樣的想法, 所以我決定寫這篇文章.

在php5.2以前, php使用引用計數(reference count)來做資源管理, 當乙個zval的引用計數為0的時候, 它就會被釋放. 雖然存在迴圈引用(cycle reference), 但這樣的設計對於開發web指令碼來說, 沒什麼問題, 因為web指令碼的特點和它追求的目標就是執行時間短, 不會長期執行. 對於迴圈引用造成的資源洩露, 會在請求結束時釋放掉. 也就是說, 請求結束時釋放資源, 是一種部補救措施(backup).

然而, 隨著php被越來越多的人使用, 就有很多人在一些後台指令碼使用php, 這些指令碼的特點是長期執行, 如果存在迴圈引用, 導致引用計數無法及時釋放不用的資源, 則這個指令碼最終會記憶體耗盡退出.

所以在php5.3以後, 我們引入了gc, 也就是說, 我們引入gc是為了解決使用者無法解決的問題.

這個是歷史, 我簡單介紹下, 現在讓我們回頭來看開頭的問題, 是不是因為php會在請求結束後釋放所有的資源, 於是我們就可以不用手動釋放呢?

看乙個例子:

<?php

$db=

mysql_connect();

$resut

=mysql_query

();// process result...

usleep

(500

);//mysql_close($db); let's say, you didn't call to this

// other logic, assuming it costs 5s

sleep(5

);exit(0

);//finish

上面的例子, 我們會保持乙個和mysql的連線5秒鐘, 這樣的指令碼對於一般的應用來說沒有關係, 但是對於乙個請求量很大的指令碼來說, 會導致乙個致命問題:

比如乙個繁忙的應用, 每秒要處理來自使用者的1000個請求, 那麼5秒鐘請求多少個? 5 * 1000 = 5000, 而mysql有最大連線數限制(mysql.max_connections), 這個數字一般不超過2000, 預設的會更低:(mysql.max_connections),

那麼, 這樣**會導致你的應用, 根本無法正常提供服務. 而如果我們在對mysql的處理完成後就關閉這個連線, 那麼就不會觸發這個問題.

而我們在實踐中, 遇到過乙個更加實際的問題, 看下面的例子:

<?php

$mmc

=new

memcached

();$mysql

=mysql_connect

();//process

mysql_close

($mysql

);$mmc

->

close

();

這是乙個真實的教訓, **如上面所示, 突然有一天我們的mysql出現了問題, 導致連線mysql的耗時增大, 然後就導致, 乙個指令碼對memcached連線占用過長, 最後memcache因為連線數太多, 就拒絕服務了..

所以, 我們一定要讓連線代價最高的資源, 最先初始化.

這個很簡單, 如果你持續開啟控制代碼, 而不釋放, 那麼你有可能觸發系統最大控制代碼限制, 對於程序來說, 自己還有程序可開啟控制代碼數限制(ulimit -n).

php之所以會在請求結束後正確的釋放掉所有的資源, 記憶體, 這是因為當我們在指令碼中使用新的記憶體的時候, php會向os申請一大塊記憶體(zend_mm_seg_size大小), 然後分給你你需要的合適的一塊小記憶體.

當你不使用這塊小記憶體的時候, php也不會返還給os, 而是保留下來給後續的處理使用.

我們知道, malloc(3)會導致系統呼叫(brk(2))(當然也可能是mmap, 我們此處不考慮這個細節, thanks to 華裔), 而系統呼叫是昂貴的.

所以, 如果你使用完了資源不及時釋放, 那麼後續的邏輯如果請求記憶體, php發現之前申請的一大塊記憶體已經分光了, 它就只好再次向os發起malloc呼叫, 得到一塊新的大記憶體. 並且它還需要對這個大記憶體做一些標記處理..

而如果你使用完資源, 及時釋放的話, 那麼下次指令碼申請記憶體的時候, 你之前歸還的記憶體塊就可以被重複利用, 那麼也許你的整個指令碼只需要和os申請一次記憶體.

這個和上面的有一定的關係, 當你使用完資源就釋放, 然後後續又使用這樣的資源. 那麼php的記憶體占用會是:

資源+1 -> 資源-1 -> 資源+1 -> 資源-1 (峰值是1)

而如果你是等到php請求結束再釋放:

資源+1 -> 資源 + 1 …. -> 資源 -1 -> 資源 – 1 (峰值是2)

也就說, 乙個良好的編寫的指令碼可能要比乙個瞎寫的指令碼, 要省很多峰值記憶體..

考慮乙個極端情況, 對乙個很繁忙的伺服器來說, 比如有10個php程序, 每個php程序最大1g記憶體, 而伺服器只有8g記憶體.

結論很明顯, 我開頭也說過了, 我從來不認為這個是個問題.

這裡說一句, 如果你買了一本php的書, 它告訴你: 「不用在php主動釋放資源, 因為php會幫你釋放」的話, 我建議你, 燒了它.

java中手動釋放資源的先後順序

1 流相關資源 流相關資源一般遵循 1 先開後關,先開的輸入流,再開的輸出流,通過讀取輸入流寫入輸出流中,那麼應該先關輸出流,再關輸入流,但是一般關閉輸入輸出流操作都是在讀寫完成後的finally中執行的,所以即使先關輸入流,再關輸出流也不會任何問題,因為讀寫操作沒有進行了。2 先關外層,再關內層。...

如何手動釋放Python的記憶體

在上篇部落格中,提到了對乙個指令碼進行的多次優化。當時以為已經優化得差不多了,但是當測試人員測試時,我才發現,踩到了python的乙個大坑。在上文的優化中,對每500個使用者,會進行一些計算並記錄結果在磁碟檔案中。原本以為這麼做,這些結果就在磁碟檔案中了,而不會再繼續占用記憶體 但實際上,pytho...

關於類資源的釋放

關於類資源的釋放 我們自定義的類並不像窗體類那樣當窗體關閉時自動呼叫釋放函式 實際上是過載基類函式 如下 清理所有正在使用的資源。如果應釋放託管資源,為 true 否則為 false。protected override void dispose bool disposing base.dispos...