2012-11-18 17 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