2012-11-18 20 views
18

最近我寫了一些微型基準測試代碼,所以我必須打印出JVM行爲以及基準信息。我用如何重定向JVM輸出而不撕裂應用程序的輸出?

-XX:+PrintCompilation 
-XX:+PrintGCDetails 

和其他選項來獲得JVM狀態。對於基準信息,我只需使用System.out.print()方法。因爲我需要知道我打印的消息和JVM輸出的順序。

雖然JVM輸出有時會撕掉我的消息,但是由於它們位於不同的線程中,所以我可以在控制檯中將它們打印出來,但可以理解並且可以接受。

當我需要做一些批次的基準,我想redirect the output into a filepipe (> in Linux system),並使用Python從文件中獲取結果,並進行分析。

這裏的問題是:

The JVM output always overlapped with the messages I printed in the Java application.它毀了消息的完成。

任何想法如何處理這種情況?我需要both the JVM output and application output in the same place in order to preserve the sequence because it is important. And they do not overlap on each other so I don't lose anything.

回答

9

我建議採取輕微繞道看使用Java API的儀器 - 使用(寫)一個簡單的Java 代理做到這一點。從你的基準角度來看,這也會給你更多的權力。您可以使用Java代理記錄所有內容(因此不同記錄器線程之間不會發生爭用)。

你可以閱讀更多的http://www.javabeat.net/2012/06/introduction-to-java-agents/http://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html

+0

好主意,我試着在代理中重定向stdout和stderr,我可以重定向從我的java代碼的輸出,但JVM輸出不重定向。在你提到的文章中介紹的方法 - 在加載類時修改字節碼,我必須知道JVM在輸出信息時調用了什麼,然後我可以修改它,但是我沒有那種信息,你有什麼建議嗎? – dawnstar

0

我建議嘗試以下。這更像是一種黑客攻擊,需要一些修補。但掌握這種方法可能會長期得到回報。尤其是,如果你做了很多基準測試。

話雖如此,我認爲HS(現在,甲骨文)應該有一個選項來重定向編譯器輸出到一個文件。您只需要足夠的努力就可以了:-) HS應該有一個選項來打印出所有的JVM和編譯器選項,其中可能是將輸出重定向到文件的選項。

不管怎麼說,我離題...

1)應該在你的$ JAVA_HOME或%JAVA_HOME%src.zip。它包含Java類庫的源代碼。

2)修改System.out將所有輸出重定向到一個特定的失敗,或者簡單地插入一些特殊的符號,你可以grep捕獲stdout和stderr。不幸的是,由於我們公司的政策禁止我們檢查src.zip的內容,因此我無法更具體地採取這一特定步驟。我只能想象這一步會有多困難。也許和輸出流交換出來一樣簡單,或者像修改應用程序直接使用的每一種打印方法一樣困難。我甚至不知道System.out使用了多少本地文件。

3)將您的編譯版本放在一個jar文件中。

4)將此選項添加到您的命令行:-Xbootclasspath/p:full_path_to_your_jar這將告訴JVM首先使用您的版本的類。 「P」代表前置。

希望這有助於...

5

嘗試使用System.out.println()代替System.out.print()System.out.println()在syncronized部分內強制刷新一個流,至少你的輸出不會混合在一起。

0

首先,我會嘗試@barracel關於使用System.out.println()的注意事項。

我不知道很多關於Java的,但你也可以寫出你所有的調試消息到stderr並留下標準輸出爲JVM。這可以防止當多個線程寫入相同的文件描述符時顯然發生的stdout污染。

0

嘗試拆分JVM和您的應用程序的輸出。

  • 輸出JVM的信息到stdout
  • 輸出應用程序的信息,標準錯誤,以「通信System.err.println()」
  • 分析與您最喜愛的工具的輸出。

所以,命令行是這樣的:

$java -XX:+PrintCompilation -XX:+PrintGCDetails MainClass 1>stdout.txt 2>stderr.txt 
5

使用Log4j或消息驅動的日誌框架與System.out.println()

Log4J使用消息事件模型來保證消息的排序。此外,可以使用各種'appender'來登錄數據庫或其他輸出/文件,從而允許通過Java包和其他屬性進行分離,以便數據不會混合。此外,考慮使用高性能定時器和/或不要嘗試測量非常短的(毫秒)事件。原因在於撥打System.currentTimeMillis()只需調用操作系統時鐘即可。在每個操作系統上都有一些「時鐘漂移」和緩存,這樣底層系統功能可能會返回相同的值,導致實際時間內的+/- 30 ms偏移量。爲了解決這個問題或提高準確性,將被測量的函數分成足夠大的樣本大小,然後除以迭代次數。

例如,執行平均1-2毫秒的10K操作作爲一次測量操作。然後除以10K以獲得每次操作的時間。

否則,再次,一個高性能計時器將是必要的。

+0

這不回答原來的問題。 Log4J不支持HotSpot診斷日誌,也不支持將它們與應用程序輸出交叉存儲在同一個文件中,以反映它們的時間關係的方式。 –

+0

問題中未提及HotSpot診斷。問題是如何在不重疊的情況下保存測井序列,解決方案當然是一種方法。 –

+0

問題(當我閱讀它時,它已被編輯)提到HotSpot診斷,例如-XX:+ PrintCompilation,並且需要在第一段中使用其他日誌記錄正確命令它們。 –

4

通過System.out.print/println直接記錄被認爲是不好的做法。

爲什麼?

  1. 這不是 '線程安全的'。從多個線程記錄導致出現亂碼
  2. 它不靈活,因爲它是硬編碼的並且未配置。
  3. 它不靈活,因爲您無法指定您希望在日誌中看到的詳細程度(例如,詳細的跟蹤/特定的調試邏輯/應用程序警告/應用程序錯誤處理/應用程序致命錯誤)。你總是得到很多東西,並且需要註釋許多代碼行以避免日誌過載。
  4. 這是不靈活的,因爲你不能指定哪些包/類你或不感興趣的 - 你又總是得到很多,需要許多註釋線什麼簡單&更具體
  5. 這是不靈活的,因爲您無法將日誌重定向到數據庫表&列,文件,電子郵件,消息系統,SMS警報等
  6. 由於無法將不同的日誌級別/包或類傳送到不同的日誌記錄目的地,因此它不靈活。您也可以對其進行配置,以登錄到同一目的地或不同目的地的應用服務器,它的JVM
  7. 它很慢,當你有幾千/百萬行被記錄到的物理磁盤

2000年,引入了Log4J。它解決了所有這些問題,並且自那時以來一直是或多或少的標準解決方案。儘管有一些最新的日誌工具嘗試超越Log4J,但仍然可以通過Log4J獲得功能強大且靈活的結果。如果你把所有的System.out.print調用切換到Log4J,那麼你引用的問題和許多其他問題將會消失。

http://logging.apache.org/log4j/1.2/manual.html

0

爲了具有在非重疊的方式輸出,使用的System.out.println。那麼在哪裏重定向到同一個文件是這樣的:

java -XX:+PrintCompilation -XX:+PrintGCDetails MainClass 1>stdout.txt 2>&1 

這是有所有的錯誤以及正常的控制檯輸出文件名stdout.txt

而且,如果日誌中有任何形式的線程/時間信息,您可以簡單地使用

sort -n -k 1 

其中-k 1代表中,你必須線程/數據(時期)信息的列。

2

對於-XX:+PrintCompilation,您可以使用-XX:+UnlockDiagnosticVMOptions -XX:+LogCompilation標誌來獲得單獨的「hotspot.log」文件中的「詳細」輸出。該文件採用XML格式,包含來自-XX:+PrintCompilation的信息以及此類編譯的原因。文件路徑可以通過-XX:LogFile=<new_hotspot_log>更改。參考:https://wikis.oracle.com/display/HotSpotInternals/LogCompilation+overview

對於-XX:+PrintGCDetails,您可以使用-Xloggc:<gc_log>將GC輸出重定向到指定的文件。參考:java -X