在Full GC之后导致套接字连接变慢的原因是什么?

时间:2015-05-20 10:39:01

标签: java sockets networking inputstream high-load

我们有一个客户端服务器应用程序,1个服务器,大约10个客户端。它们使用自定义查询通过tcp套接字进行通信。

系统运行平稳了好几个月,但在某些时候,在每日预定服务器FULL GC耗时约50秒之后,我们发现客户端发送的查询之间的时间间隔并且从服务器接收的响应很大,> 10-20s。系统恢复3个小时后,一切都恢复正常。

在调查此问题时,我们发现:

  1. 客户端和服务器上没有垃圾回收问题
  2. 服务器上的查询处理时间很短。
  3. 服务器上的负载很高。
  4. 网络带宽未饱和。
  5. 在FULL GC期间未重置连接(每日FULL GC是正常事件,直到那时)
  6. 机器和操作系统最近从Centos 6(内核2.6.32)更改为Centos 7(内核3.10.0),但新配置已经过广泛测试。 Oracle JDK版本也从1.7.65更改为1.7.75。
  7. 我们在服务器上进行了一次线程转储:

    this

    java.lang.Thread.State: RUNNABLE at java.io.FilterInputStream.read(FilterInputStream.java:83) at util.network.BytesBasedSocketConnection$ReadConnectionRunnable.run(BytesBasedSocketConnection.java:293) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745) 如下:

    FilterInputStream.read()

    我们代码中的 public int read() throws IOException { return in.read(); } in

    问题是:为什么大多数连接在完全GC暂停后放慢了?为什么stacktrace以BufferedInputStream结尾?它不应该在FilterInputStream.read()或套接字输入流中的某个地方结束吗?这读取会导致服务器负载过高吗?

    我们用于阅读的代码:

    BufferedInputStream

    其中:

    int constructLength = _socketDIS.readInt();
    ByteArrayOutputStream constructBOAS = new ByteArrayOutputStream(constructLength);
    for (int i = 0; i != constructLength; i++)
          constructBOAS.write(_socketDIS.read());
    constructBOAS.close();
    byte[] bytes = constructBOAS.toByteArray();
    

    这是来自运行良好的客户端连接的堆栈跟踪:

    _socketDIS = new DataInputStream(new BufferedInputStream(_socket.getInputStream()));
    

    更新:

    关于EJP答案:

    1. 没有涉及EOS,连接已经开始,但是它们非常慢

    2. 即使有EOS,我也无法看到代码如何在EOS中旋转,java.lang.Thread.State: RUNNABLE at java.net.SocketInputStream.socketRead0(Native Method) at java.net.SocketInputStream.read(SocketInputStream.java:152) at java.net.SocketInputStream.read(SocketInputStream.java:122) at java.io.BufferedInputStream.fill(BufferedInputStream.java:235) at java.io.BufferedInputStream.read(BufferedInputStream.java:254) - locked <0x00007f522cbebca8> (a java.io.BufferedInputStream) at java.io.DataInputStream.readInt(DataInputStream.java:387) at util.network.BytesBasedSocketConnection$ReadConnectionRunnable.run(BytesBasedSocketConnection.java:287) at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:471) at java.util.concurrent.FutureTask.run(FutureTask.java:262) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:178) at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:292) at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) at java.lang.Thread.run(Thread.java:745) for值的限制。但是,建议的改进仍然有效。

    3. 问题的堆栈跟踪结束于从constructLength继承的DataInputStream(_socketDIS.read())的读取操作,请参阅上面的代码。 FilterInputStream.read(),而不是DataInputStream BufferedInputStream。 在read()中,FilterInputStream.read()上有一个in.read(),这个方法定义了自己的BufferedInputStream方法。但堆栈跟踪在中间停止,未到达read()。为什么呢?

4 个答案:

答案 0 :(得分:7)

一次读取一个字节会浪费CPU。扔掉它:

int constructLength = _socketDIS.readInt();
ByteArrayOutputStream constructBOAS = new ByteArrayOutputStream(constructLength);
for (int i = 0; i != constructLength; i++)
      constructBOAS.write(_socketDIS.read());
constructBOAS.close();
byte[] bytes = constructBOAS.toByteArray();

并使用此:

int constructLength = _socketDIS.readInt();
byte[] bytes = new byte[constructLength];
_socketDIS.readFully(bytes);

NB _socketDIS显然一个BufferedInputStream,但DataInputStream,是无缓冲的。

修改

  

为什么stacktrace以FilterInputStream.read()结束?

仔细看。 BufferedInputStream没有实现所有三个read()重载。其中一个,我忘了哪个,在FilterInputStream中实现,基类,以及其他两个重载调用它。

  

不应该在BufferedInputStream中的某个地方结束

不,见上文。

  

或套接字输入流?

是的,如果它是阻止的,但它不是,可能是因为你在流的末尾旋转,因为你的代码很差。

  

这读取会导致服务器负载过高吗?

答案 1 :(得分:3)

堆栈跟踪显示您使用的是ScheduledThreadPoolExecutor。我建议你研究时间表。延迟很可能仅仅是因为阅读是按照某种时间表进行的 - 这对我来说似乎很蠢。

答案 2 :(得分:1)

这更像是一个扩展评论,但评论时间太长,所以我会在答案中提供。

正如您所注意到的,在FilterInputStream.read()中间显示线程的线程转储是不常见的。虽然它可能偶然发生,但被重写的FilterInputStream.read()被解析为BufferedInputStream.read(),偶然发生似乎不太可能。

鉴于这发生在完全垃圾收集之后,我觉得更有可能需要更长的时间来解决FilterInputStream调用,因为BufferedInputStream类在完全垃圾收集期间被移动或卸载。例如,如果在发生完全垃圾收集时偶然没有使用BufferedInputStream对象,则可以卸载BufferedInputStream类,需要在需要read()方法时加载类。这可能会导致您看到的延迟,至少一次。

在某些情况下,即使没有完整的垃圾回收,也可以在最后一个实例被垃圾回收时卸载类。如果BufferedInputStream类在每次使用后以某种方式被卸载并在下次使用时重新加载,这可以解释你说的症状,但我通常不希望这种情况发生。

也有可能是持有BufferedInputStream类的内存页面发生颠簸,可能是因为在完全垃圾收集期间,如果您使用的是虚拟内存,则该类已重新定位。如果您在此期间有任何关于内存使用情况的记录,可能值得一看。

答案 3 :(得分:-1)

我猜你必须尝试刷新它才能在流中读写并且没有错误或减慢连接速度。