2014-03-28 33 views
2

我有一個多次調用jQuery.load('test.php')的HTML頁面。 test.php的輸出是「256kB」,如下所示,64個請求x 256kb應該需要16MB。然而,cPanel的資源使用情況表明,64個請求的單個運行實際上具有154MB的成本(或每個請求2.4 MB)。多週期違反我的共享主機1GB的虛擬內存限制而導致錯誤:對PHP腳本的多個jQuery.load()調用導致「無法分配內存:無法創建子進程」錯誤

(12)Cannot allocate memory: couldn't create child process: /opt/suphp/sbin/suphp

因爲這不符合靜態文件我懷疑PHP具有一些開銷出現,但多少?爲什麼當腳本結束時虛擬內存不能立即回收?我試過'死'和'退出()'沒有效果。有沒有辦法強制PHP(v5.2.17)垃圾收集?

的test.html:

<table> 
    <tr> 
     <td id="column_1"></td> 
     <td id="column_2"></td> 
     <td id="column_3"></td> 
     <td id="column_4"></td> 
    </tr> 
</table> 

<script> 
    function loadPage() { 
     $('<div>').load('/test.php', function (response) { 
      $('#column_1').append(response); 
     }); 
     $('<div>').load('/test.php', function (response) { 
      $('#column_2').append(response); 
     }); 
     $('<div>').load('/test.php', function (response) { 
      $('#column_3').append(response); 
     }); 
     $('<div>').load('/test.php', function (response) { 
      $('#column_4').append(response); 
     }); 
    } 
    $(document).ready(function() { 
     for (var j = 0; j < 16; j++) { 
      loadPage(); 
     } 
    }); 
</script> 

test.php的:

echo memory_get_peak_usage(true)/1024 . 'kB'; 

(注:我真的不運行jQuery.load()64次,但原始的錯誤導致這個問題的是大約12個請求,每個請求佔用5MB的空間,我曾經想過一種更好的方式,它需要更少的請求,但是希望確保這個錯誤不會在生產中重複出現,當應用程序失敗時我期望收到多個同時請求)

回答

0

PHP腳本的輸出與PHP(加上webserver)進程實際使用的內存無關。開銷取決於加載的模塊和服務器配置;基本上在你的設置中,每個請求都生成一個suphp實例,即使它是動態鏈接的(它,不是嗎?),很多內部結構將被複制。我發現Apache PHP模塊在內存使用方面更加節省(但它也取決於加載的模塊,甚至通常不包括PHP模塊;因此,如果要跳過此模塊,請檢查您的配置)。

您可以使用PHP memory reporting functions更好地評估您的請求在其生命週期中的內存佔用情況,而不僅僅是在最後,並查看哪些操作消耗的內存最多。用unset()釋放不需要的對象通常是有益的。

您可以依次運行這些請求並將中間結果保存到磁盤文件以減少整個內存佔用量。然後最後一次調用可以收集結果並將其與一個passthru函數一起發送。

使用ob_end_clean()禁用緩衝可能有助於減少服務器進程的內存使用量(即使它對PHP部分無能爲力)。在你的情況下,我不確定它會起作用,看看該過程實際上是一個獨立的suphp二進制文件,但你可以隨時嘗試看看會發生什麼。在一個大的JSON對象

合併請求

如果幾個呼叫使用相同的輸入參數,或者「可合併」的參數,你可以嘗試合併在一個單一的通話,從而也可能把一切都快得多:而不是返回一個對象,你回到幾個對象集於一身

Header('Content-Type: application/json'); 
die(json_encode(array(
    'column1' => 'Output for the call to column1', 
    'column2' => ... 
)); 

和jQuery的你會得到所有的人,並派遣他們,他們需要去

function loadPage() { 
    $.get('/test-big.php', { 
     parcol1: 'parameter the code generating column 1' 
    }, function(data, textStatus, jqXHR) { 
     $('#column_1').append('<div>').html(data.column1); 
     $('#column_2').append('<div>').html(data.column2); 
     ... 
    }); 
} 

即使總時間以滿足所有的要求是更長的時間,並且有那麼「有事頁面上的」前較長的延遲,我覺得這種做法幾乎總是preferrable有以下幾個原因:

  • 它是一個HTTP連接,節省連接開銷。通過HTTP流水線,這不再是一個非常大的優勢,但它仍然是的優勢。
  • HTTP服務器通常使用的壓縮函數具有「性能曲線」,並且不壓縮比如說第一個kB以及第二個和後續的壓縮函數,尤其是在重複次數很多時。因此,發送5個2-Kb數據包將它們轉換爲總共2.5 Kb的5個0.5 Kb數據包。但實際上,第一個Kb變爲0.3,第二個爲0.2;因此發送一個10-Kb數據包將其變爲0.3 + 0.2 + 0.18 + 0.18 + ... =也許2 Kb,這可以免費節省20%。除非請求是完全不相關的,並且完全打在您的應用程序的不同區域,這不太可能,它們都會產生相同的設置成本(數據庫連接,過濾器,緩衝區,加載類,緩存...)。 )。這些成本只是一次而不是幾次,這是一筆不錯的交易。
  • 您使用的資源要少得多:不僅內存(一個開銷對多個),而且還有一個數據庫連接,一個高速緩存連接等等。確實,總資源使用量與請求數量不成正比,因爲它們中的一些在其他一些甚至開始之前將會終止,但這樣您就是肯定即比例是常數
  • 如果請求是非常相關的(例如,您要求顯示一個股票列的總數,例如信號量圖形和另一個信號量的總數......),那麼您可以重新設計SQL查詢或任何爲了返回所有信息立即。 XXXX的二十個值不是SELECT opening, closing FROM nyse WHERE st='XXXX';,你可以做SELECT st, opening, closing FROM nyse WHERE st IN ('XXXX1','XXXX2',...);並用一個只比第一個稍微慢一點的單個替換20個查詢。這不能總是這樣做,但它經常發生,值得牢記。通過適當地構造jQuery代碼,您甚至可以使用單個.each循環來發送所有答案(例如,如果每個目標DIV都具有與代碼相同的ID,則可以發送包含例如'#XXX1.opening' => 1.23, '#XXX1.closing' => 1.24, ...的JSON)。
+0

謝謝您的建議。如何知道suphp實例是否是動態鏈接的,請記住這是一個合理限制的共享主機帳戶? – AJR

+0

它很可能是動態鏈接的。但是如果你可以執行'ldd'命令,或者''file'',你可以肯定知道。嘗試使用包含'<?php系統(「ldd/opt/suphp/sbin/suphp 2> &1");?>'看看會發生什麼。 – LSerni

+0

我還沒有完全解決這個問題,因爲在短時間內64個PHP調用仍然會觸發可用的虛擬內存,但我認爲我無法在共享主機上做很多事情。 Iserni的答案被接受了,因爲它給了我很多選擇來緩解原始問題(而JSON的東西非常酷)。 – AJR

相關問題