2011-09-23 67 views
2

我有一個程序可以創建一個大小約爲50MB的文件。在此過程中,程序會頻繁地重寫文件的各個部分,並將更改強制到磁盤上(大約100次)。它使用FileChannel並通過fc.read(...),fc.write(...)和fc.force(...)指導ByteBuffers。多個JVM的I/O性能(受Windows 7影響,Linux有效)

新文本:

我對現在的問題一個更好的視野。 問題似乎是,我使用三個不同的JVM來修改一個文件(一個創建它,另外兩個(從第一個啓動)寫入它)。在下一個JVM啓動之前,每個JVM都會正確關閉該文件。 問題是,該文件的fc.write()成本偶爾會通過第三個JVM的頂端(按正常成本的100倍的順序)。也就是說,所有的寫操作都是同樣緩慢的,它不僅僅是一個很長的寫操作。有趣的是,有一種方法可以在啓動JVM之間插入延遲(2秒)。毫不拖延,寫作總是緩慢,延遲,寫作每隔一段時間左右緩慢。

我也發現這個Stackoverflow: How to unmap a file from memory mapped using FileChannel in java?它描述了映射文件的問題,我沒有使用。

我懷疑可能會發生什麼: 當我調用close()時,Java不會完全釋放文件句柄。當下一個JVM啓動時,Java(或Windows)會識別對該文件的併發訪問,併爲該文件安裝一些昂貴的併發處理程序,從而導致寫入代價高昂。 會有道理嗎?

該問題發生在Windows 7(Java 6和7,在兩臺機器上測試過)上,但不在Linux下(SuSE 11.3 64)。

舊文:

問題: 從從日食一個JUnit測試工具或從控制檯啓動程序正常工作,它需要3秒左右。 通過一個ant任務(或通過使用ProcessBuilder踢出單獨的JVM通過JUnit)啓動程序,可以將程序減慢到70-80秒,以完成同一任務(因子20-30)。

使用-Xprof揭示 'force0' 和 'PWRITE' 的使用穿過屋頂從34.1%(76個+ 20抽動)至97.3%(3587 + 2913 + 751抽動): 快速運行:

27.0%  0 + 76 sun.nio.ch.FileChannelImpl.force0 
7.1%  0 + 20 sun.nio.ch.FileDispatcher.pwrite0 
[..] 

運行緩慢:

Interpreted + native Method       
48.1%  0 + 3587 sun.nio.ch.FileDispatcher.pwrite0 
39.1%  0 + 2913 sun.nio.ch.FileChannelImpl.force0 
[..] 
    Stub + native Method       
10.1%  0 + 751 sun.nio.ch.FileDispatcher.pwrite0 
[..] 

GC和編譯可以忽略不計。

更多的事實:

沒有其他的方法顯示在-Xprof輸出顯著變化。

  • 它要麼很快,要麼非常緩慢,從來不介於兩者之間。
  • 內存是沒有問題的,所有的測試機器至少有8GB,該過程使用< 200MB
  • 重新啓動計算機並不能幫助病毒掃描程序和類似的東西
  • 開關沒有影響
  • 當過程是緩慢的,幾乎沒有任何CPU使用率
  • 這是從未緩慢從一個正常的JVM運行時,它
  • 這是很在從第一JVM啓動一個JVM上運行它時,(通過的ProcessBuilder持續放緩或作爲螞蟻任務)
  • 所有JVM都完全相同。我通過RuntimeMXBean輸出System.getProperty(「java.home」)和JVM選項RuntimemxBean = ManagementFactory.getRuntimeMXBean(); List arguments = RuntimemxBean.getInputArguments();
  • 我在兩臺使用Windows7 64位,Java 7u2,Java 6u26和JRockit的機器上進行了測試,但機器的硬件不同,但結果非常相似。
  • 我也從外部Eclipse(命令行螞蟻)測試它,但沒有差異。
  • 整個程序是由我自己編寫的,它所做的只是讀取和寫入該文件,不使用其他庫,尤其是沒有本地庫。 -

而且一些可怕的事實,我只是不願相信任何意義:

  • 刪除所有的類文件,有時重建項目(很少)幫助。該程序(嵌套版本)運行速度快一次或兩次,然後又變得非常慢。
  • 安裝一個新的JVM總是有幫助的(每一次!),這樣(嵌套的)程序至少運行一次!由於JDK-jre和JRE-jre至少可以正常工作一次,因此安裝JDK的計數爲兩個。重新安裝JVM不會有幫助。重新啓動也沒有。我還沒有試過刪除/重新啓動/重新安裝...
  • 這些是我唯一能夠爲嵌套程序獲得快速程序運行時的兩種方法。

問題:

  • 什麼可能會導致嵌套的JVM這種性能下降?
  • 這些方法究竟做什麼(pwrite0/force0)? -

回答

0

您是否使用本地磁盤進行所有測試(而不是任何網絡共享)?

您可以使用RAM驅動器來設置Windows以存儲數據嗎?當JVM終止時,默認情況下它的文件句柄將被關閉,但是你可能會看到的是將數據清空到磁盤。當您覆蓋大量數據時,以前版本的數據將被丟棄,並且可能不會導致磁盤IO。關閉文件的行爲可能會使Windows內核隱式地將數據刷新到磁盤。所以使用RAM驅動器可以讓你確認自己的磁盤IO時間已從你的統計數據中刪除。

找到一個windows工具,允許你強制內核將所有緩衝區刷新到磁盤,在JVM運行之間使用它,看看當時需要多長時間。

但我想你會嘗試一些迭代過程的需求和內核試圖管理磁盤塊緩衝區緩存的需求。在linux中有一個像「/ sbin/blockdev --flushbufs」這樣的工具。

FWIW

「PWRITE」是在Linux/Unix API允許並行寫入到文件描述符(這將是最好的內核系統調用的API來使用的JVM,我想的Win32 API已經計提在一個進程中線程之間共享一個文件句柄的相同用法,但是由於Sun有unix heritige的東西以Unix方式命名)。谷歌「pwrite(2)」更多關於這個API的信息。

「force」我猜測這是文件系統同步,這意味着進程正在請求內核將未寫入的數據(即當前在磁盤塊緩衝區緩存中)刷新到磁盤上的文件中(例如需要在關閉計算機之前)。這個動作會隨着時間的推移自動發生,但事務處理系統需要知道先前寫入的數據(使用pwrite)實際上是否已經觸及物理磁盤並被存儲。因爲某些其他磁盤IO依賴於知道,例如事務檢查點。

0

有一點可以幫助的是確保您明確地將FileChannel設置爲null。然後在程序結束時撥打System.runFinalization()System.gc()。您可能需要超過1個電話。

System.runFinalizersOnExit(true)也可能有幫助,但它已被棄用,因此您將不得不處理編譯器警告。