Java GC日志中充满了奇怪的字符

时间:2015-10-07 07:11:35

标签: java logging garbage-collection jvm java-8

我在使用GC日志的几台服务器上遇到问题。它充满了这个:

^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@
^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@^@

注意到这发生在给JVM提供大量内存的服务器上:-Xms32G -Xmx48G。这可能是一个红鲱鱼,但想提到它。

由于这些是低延迟/高吞吐量的应用程序,分析日志至关重要。但相反,它充满了上面的那些角色。

我们正在使用Java 8:

java version "1.8.0_40"
Java(TM) SE Runtime Environment (build 1.8.0_40-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.40-b25, mixed mode)

我们使用它来创建日志:

-verbose:gc
-Xloggc:/path/to/gc.log
-XX:+PrintGCDetails
-XX:+PrintGCDateStamps

之前有人见过这个问题吗?可能导致什么呢?

2 个答案:

答案 0 :(得分:4)

TL; DR

请勿使用logrotate(或任何第三者轮换)来轮换JVM GC日志。它的行为与JVM写入GC日志文件的方式不太匹配。 JVM可以使用JVM标志来轮换自己的GC日志:

  • -XX:+UseGCLogFileRotation启用GC日志文件轮换
  • -XX:NumberOfGCLogFiles=5将告诉JVM保留5个旋转文件
  • -XX:GCLogFileSize=20M将在文件达到20M时通知JVM旋转

问题

对我们来说,这是因为logrotate和JVM都试图在不加锁的情况下写入文件。 JVM垃圾回收日志看起来很特殊,因为它们是直接从JVM本身写入文件的。发生的情况是,JVM保留了该文件的句柄,以及该文件中写入日志的位置。

^@实际上只是文件中的一个空字节。如果运行hexdump -C your_gc.log,则可以看到此信息。引起这些空字节的原因是有趣的部分-logrotate截断文件。

$ hexdump -C gc.log | head -3
00000000  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
*
061ca010  00 00 00 00 00 00 00 32  30 32 30 2d 30 37 2d 30  |.......2020-07-0|

这仅对我们浮出水面,因为我们使用Logstash监视GC日志。每次OutOfMemoryError运行时,Logstash都因logrotate而崩溃,并且从检查堆转储中我们注意到,logstash试图发送看起来像这样的HUGE(JVM内部存储器中为600MB)日志行:

{ "message": "\u0000\u0000\u0000...

在这种情况下,因为logstash将空值转义为Unicode(6个字符),并且每个字符在JVM中内部表示为UTF-16,所以这意味着其堆上编码比12的大倍。磁盘上的空字节。因此,它所需要的日志要比您用尽的内存少。

这导致我们在垃圾收集日志中找到空值以及它们的来源:

1。 JVM愉快地编写日志

*-------------------------*
^                         ^
JVM's file start          JVM's current location

2。 logrotate已进入游戏

                         **
\________________________/
^                    |    ^
JVM's file start     |    JVM's current location
                     |
                     logrotate copies contents elsewhere and truncates file
                     to zero-length

3。 JVM一直在写

*xxxxxxxxxxxxxxxxxxxxxxxxx-*
\________________________/^^
^                    |    |JVM's current location
JVM's file start     |    JVM writes new log
                     |
                     File is now zero-length, but JVM still tries to write
                     to the end, so everything before it's pointer is 
                     filled in with zeros

答案 1 :(得分:1)

如果您保存的文字是用UTF-16编码的,它可能附加一个" ^ @"在常规文本文件中。我以前在UNIX系统中打开一些编码文件时遇到了这个问题。