2012-12-01 27 views
2

我一直在努力尋找非常基本的東西,問題與使用Jetty延續長輪詢有關。碼頭延續和無法解釋的內存使用模式

爲了簡單起見,我已經刪除了所有特定於應用程序的代碼,只留下簡單的延續相關代碼。

我正在粘貼下面我的servlet的doPost方法。關鍵的問題,在這裏我需要一些專家的指導

  • 在下面的代碼塊,如果我運行它是和火災後請求攜帶大約200字節後身體那麼內存量500長輪詢連接大約20 MB。
  • 在哪裏,如果我評論了塊突出顯示爲「減少到低於內存佔用::註釋塊」,那麼存儲器足跡歸結爲7 MB

在兩種情況下我等待系統穩定,多次調用GC,然後通過jConsole讀取內存。它不是確切的,但差別非常大並且可以解釋,在這裏或那裏幾乎100個字節的精度並不重要。

我的問題爆炸,考慮到我的服務器需要保持100K連接,如果不是更多。在這裏,這個無法解釋的增長最終導致接近GB的額外堆使用。

(是什麼原因造成這種額外的堆使用的時候,連什麼是從流中讀取時不保留doPost方法的範圍之內,但仍然把它添加到堆....我錯過什麼?)

@Override 
    protected void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { 

    Continuation cc = ContinuationSupport.getContinuation(req); 

    //if continuation is resumed, then send an answer back with 
    //hardcoded answer 
    if (cc.isResumed()) { 
     String myJson = "{\"1\",\"2\"}"; 
     res.setContentType("application/json"); 
     res.setContentLength(myJson.length()); 
     PrintWriter writer = res.getWriter(); 
     writer.write(myJson); 
     writer.close(); 
    } 
    // if it is the first call to doPost (not reentrant call) 
    else if (cc.isInitial()) {   

     //START :: decrease memory footprint :: comment this block :: START 

     // store the json from the request body in a string 
     StringBuffer jsonString = new StringBuffer(); 
     String line = null;      
     BufferedReader bufferedReader = req.getReader(); 
     while ((line = bufferedReader.readLine()) != null) { 
      jsonString.append(line); 
     } 

     //here jsonString was parsed and some values extracted 
     //though that code is removed for the sake of this publish 
     // as problem exists irrespective...of any processing 

     line = null;    
     bufferedReader.close(); 
     bufferedReader = null; 
     jsonString = null; 

     // END :: decrease memory footprint :: comment this block :: END 

     cc.setTimeout(150000);   

     cc.suspend(); 
    } 
} 

回答

1

是什麼原因造成這種額外的堆使用...

看看這一行:

BufferedReader bufferedReader = req.getReader(); 

請注意,您實際上並未創建新的BufferedReader。當你調用getBufferedReader時,Jetty創建了一個BufferedReader,該BufferedReader包裝一個InputStreamReader,該InputStreamReader封裝了一個自定義InputStream實現,該實現封裝了一個字節緩衝區。我非常肯定,通過執行讀取整個消息的代碼,您可以在請求對象內部創建大量字節緩衝區,該緩衝區存儲消息正文的全部內容。另外請求對象保持對讀者的引用。

在函數的開頭你叫:

Continuation cc = ContinuationSupport.getContinuation(req); 

我相信你繼續持有,其上存儲所有數據的請求。因此,讀取數據的簡單行爲是分配將保留的內存,直到您繼續執行爲止。

有一件事你可能會嘗試只是作爲一個實驗。將您的代碼更改爲:

BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(req.getInputStream())); 

這樣Jetty不會分配它自己的讀者。再次 - 我不知道與請求對象的其餘部分相比,讀取器中真正存儲了多少數據 - 但它可能會有所幫助。

[更新]

另一種選擇是避免該問題。這就是我所做的(儘管我使用的是servlet 3.0而不是Continuations)。我有一個資源 - 我們稱之爲/transfer,它會發布一些數據,然後使用AsyncContext等待響應。我將其更改爲具有不同URL的兩個請求 - /push/pull。任何時候我有一些需要從客戶端發送到服務器的內容,它會在/push請求中發送,然後立即返回而不創建AsyncContext。因此,請求中的任何存儲都會立即釋放。然後等待響應,我發送了第二個沒有消息體的GET請求。當然 - 請求會暫停一段時間 - 但是誰在乎 - 它沒有任何內容。

您可能不得不重新考慮您的問題,並確定您是否可以分批執行任務 - 多個請求 - 或者您是否真的必須在單個請求中處理所有內容。

+0

謝謝,我會嘗試你的建議....在平均值,而我試着從ServletInputStream讀取(這是非常在你指出的行),它大大改善了這種情況。我不明白的是,當我從閱讀器讀取數據被讀取並存儲到方法範圍內的字符串時,爲什麼對象保留在內存中,直到清理了繼續對象。所有我的字符串的範圍是doPost ...所以從流/輸入或讀者讀什麼應該在doPost後清理,除了我繼續繼續。我知道我錯了某處...但不知道我在哪裏接受 – ehsgkat

+0

答案,但我繼續其他方式。我嘗試了基於netty的自己的服務器。在這種情況下,我可以在讀取結束後釋放內存,因此我認爲這是Jetty工作的方式......理想情況下,一旦有人暫停繼續,他應該有一個選項,他已完成請求選項,唯一剩下的就是發送一個響應。所以資源/緩衝請求對象的任何內容都應該被釋放。 (會嘗試一些碼頭特定的論壇有這個線索,雖然我把這個文本justincase爲這個Q的未來讀者) – ehsgkat

+0

在碼頭論壇創建了一個話題,incase未來的讀者想跟蹤...我加入鏈接http: //jetty.4.n6.nabble.com/Jetty-Continuations-Long-poll-and-release-of-request-object-td4959635.html – ehsgkat