2010-08-04 105 views
14

我正在進行使用JProfilerEclipse Tptp.和的Java應用程序的車間性能分析,性能調整,內存分析,內存泄漏檢測等。我需要一套我可以提供的練習給參與者,他們可以: 使用該工具來分析發現問題:瓶頸,內存泄漏,次優代碼等。我相信有很多經驗和實際的例子。Java性能分析,性能調整和內存分析練習

  • 解決問題並實現優化的代碼
  • 通過
  • 理想的情況下進行分析的另一個會話演示解決方案,編寫單元測試,演示的性能增益

問題,也解決方案不應該過於複雜;應該有可能在幾分鐘之內解決它們,最好在幾分鐘之內解決它們。 一些有趣的領域行使:

  • 解決內存泄漏
  • 優化循環
  • 優化對象的創建和管理
  • 優化字符串操作
  • 解決問題的併發性和併發性加劇瓶頸

理想情況下,練習應該包括樣本未經優化的代碼和d解決方案代碼。

+1

所以你要求的課程材料? – 2010-08-04 14:07:04

+0

練習更加精確。我想在研討會中使用它們,但我會說這些對任何調整和分析Java應用程序的人都很有用。 – Dan 2010-08-04 14:19:11

+4

您可能需要考慮將VisualVM與其他兩臺顯示器一起使用。或者如果時間有問題,不包括JProfiler。 JProfiler非常漂亮,但很難讓人們按原樣進行配置,而不會讓他們付錢購買該工具。坦率地說,其他兩個通常足以發現瓶頸和僵局。 – 2010-08-04 14:19:16

回答

6

我試圖找到我在野外看到的現實生活中的例子(可能稍有改動,但基本問題都非常真實)。我也嘗試將它們聚集在同一場景中,以便您可以輕鬆構建會話。方案:你有一個耗時的函數,你想爲不同的值做很多次,但是相同的值可能會再次彈出(理想情況下,創建後不會太長)。一個很好而簡單的例子就是你需要下載和處理的url-web頁面對(對於練習它應該可能被模擬)。

循環:

  • 你要檢查是否有一組單詞在網頁上彈出。在循環中使用你的函數,但是使用相同的值,僞代碼:

    for (word : words) { 
        checkWord(download(url)) 
    } 
    

    一個解決方案很簡單,只需在循環之前下載頁面即可。 其他解決方案如下。

內存泄漏:

  • 簡單:你也可以用一種高速緩存解決您的問題。在最簡單的情況下,您可以將結果放到(靜態)地圖中。但是如果你不能阻止它,它的大小將會無限增長 - >內存泄漏。
    可能的解決方案:使用LRU映射。最有可能的性能不會降低太多,但內存泄漏應該消失。
  • 更棘手的一點:假設您使用WeakHashMap實現前一緩存,其中鍵是URL(不是字符串,請參閱後面的內容),值是包含URL,下載頁面和其他內容的類的實例。你可能會認爲它應該沒問題,但事實上並非如此:由於值(不是弱引用的)具有對密鑰(URL)的引用,所以密鑰永遠不會被清除 - >良好的內存泄漏。
    解決方案:從值中刪除URL。
  • 和以前一樣,但是url是interned字符串(「如果我們碰巧又有相同的字符串,則保存一些內存」),value並沒有指向這個。我沒有嘗試,但在我看來,它也會導致泄漏,因爲interned字符串不能進行GC編輯。
    解決方案:不要實習生,這也會導致你不能跳過的建議:不要做premature optimization, as it is the root of all evil

對象創建&字符串:

  • 說你只想顯示頁面的文本(〜刪除HTML標籤)。編寫一個逐行執行的函數,並將其附加到不斷增長的結果中。起初結果應該是一個字符串,因此追加需要很多時間和對象分配。你可以從性能的角度(爲什麼追加很慢)以及從對象創建的角度(爲什麼我們創建瞭如此多的字符串,StringBuffers,數組等等)來檢測這個問題。
    解決方案:對結果使用StringBuilder。

併發:

  • 你想通過並行執行下載/過濾,以加快整個東西了。使用它們創建一些線程並運行你的代碼,但是在一個大的同步塊(基於高速緩存)內部做所有事情,只是「保護高速緩存免受併發問題的困擾」。效果應該是,您只能有效地使用一個線程,因爲所有其他線程都在等待獲取緩存上的鎖定。
    解決方法:只同步周圍緩存操作(例如,使用'java.util.collections.synchronizedMap())

  • 同步的代碼的所有微小的碎片。這應該會損害性能,可能會阻止正常的並行執行。如果你幸運/足夠聰明,你也可以想出死鎖。 道德:同步不應該是一個特別的事情,在「它不會傷害」的基礎上,而是一個深思熟慮的事情。

獎金鍛鍊:

在開始填滿你的緩存並沒有做過多的分配之後,但仍然有一個小泄漏某處。通常這種模式不太容易理解。您可以使用探查器的「書籤」或「水印」功能,該功能應在緩存完成後立即創建。

1

不要忽視this method,因爲它適用於任何語言和操作系統,適用於these reasons。一個例子是here。此外,嘗試使用I/O和重要呼叫深度的示例。不要只使用像Mandelbrot這樣的cpu-bound程序。如果你拿這個不是太大的C例子,並用Java重新編碼,那應該說明你的大部分觀點。

讓我們來看看:

  • 解決內存泄漏。
    垃圾收集器的要點是堵塞內存泄漏。但是,您仍然可以分配太多的內存,並且對於某些對象而言,它顯示爲「新」中的大部分時間。

  • 優化循環。
    一般來說,循環不需要進行優化,除非在它們內部做得很少(並且它們佔用很多時間)。

  • 優化對象創建和管理。
    這裏的基本方法是:保持數據結構儘可能簡單。特別是遠離通知風格的嘗試來保持數據的一致性,因爲這些東西會跑掉並使呼叫樹變得非常濃密。這是大型軟件性能問題的主要原因。

  • 優化字符串操作。
    使用字符串構建器,但不要冒用不會佔用執行時間百分比的代碼。

  • 併發性。
    併發有兩個目的。
    1)性能,但是這個只有的作品的程度,它允許多件硬件同時啓動。如果硬件不在那裏,它沒有幫助。好痛。
    2)表達的清晰度,例如UI代碼不必擔心重計算或網絡I/O同時進行。

在任何情況下,都不能強調,在證明某些事情需要大量時間之前不要做任何優化。

+0

你可以肯定用GC語言有「真正的」內存泄漏。 – 2010-08-04 15:46:56

+0

@Zwei:你說得對。這種方法不包括100%的問題。可能存在內存增長問題,這不是性能問題。對於那些人來說,你需要一種工具來追蹤剩餘物品的來源以及爲什麼它們沒有被釋放。 – 2010-08-04 15:56:10

+0

Mike,在我的linux機器上使用jdk 6,'javap'顯示使用'+'連接的字符串在內部經過優化以使用StringBuilder類。在提出這個建議之前,先看看生成的字節碼。 – questzen 2010-08-08 09:41:16

0

我已經使用JProfiler來分析我們的應用程序。但它沒有太多的幫助。然後我使用JHat.Using JHat你不能實時看到堆。你必須採取堆轉儲然後分析它。使用OQL(Object Query Language)是查找堆泄漏的好技術。