java - Full GC 后 socket 连接变慢的原因是什么?

标签 java sockets networking inputstream high-load

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

系统已经顺利运行了好几个月,但在某个时候,在每天安排的服务器 FULL GC 花费大约 50 秒之后,我们发现客户端发送的查询之间的时间从服务器收到的响应很大,> 10-20 秒。大约 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。

我们在服务器上进行了线程转储:

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是一个BufferedInputStream

问题是:为什么大多数连接在 Full GC 暂停后变慢?为什么堆栈跟踪以 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()));

这是工作良好的客户端连接的堆栈跟踪:

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)

更新:

关于 EJP 答案:

  1. 没有涉及 EOS,连接已连接,但速度很慢

  2. 即使存在 EOS,我也看不到代码如何在 EOS 上旋转,forconstructLength 值限制。但是,建议的改进仍然有效。

  3. 有问题的堆栈跟踪以对继承自 FilterInputStream 的 DataInputStream ((_socketDIS.read()) 完成的读取结束。 read(),参见上面的代码。DataInputStream,而不是 BufferedInputStream 缺少 read()。 在 FilterInputStream.read() 中有一个 in.read()BufferedInputStream 上调用,这个有它自己的 read () 方法定义。但堆栈跟踪在中间停止,未到达 BufferedInputStream.read()。为什么?

最佳答案

一次读取一个字节很浪费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, 没有缓冲。

编辑

Why the stacktrace ends in FilterInputStream.read()?

仔细看。 BufferedInputStream 没有实现所有三个 read() 重载。其中之一,我忘记了,是在基类 FilterInputStream 中实现的,另外两个重载调用它。

Shouldn't it end somewhere in the BufferedInputStream

不,见上文。

or in socket input stream?

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

Can this read lead to high load on server?

是的。

关于java - Full GC 后 socket 连接变慢的原因是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30347231/

相关文章:

c++ - 如何在 Symbian C++ 中获取网络类型(2G/3G/Wi-Fi)?

networking - Haskell 网络.浏览器 HTTPS 连接

java - 如何访问两个特殊字符之间的字符串,它们的顺序很重要?

java - n 维数组到 n 嵌套列表

java - 正面或反面游戏 - 循环问题

java - 从客户端发送多个服务器对象到服务器

java - 应用程序在 intelliJ 中运行但 .jar 未运行

java - 如何在 JComboBox 中加载树?

c - Posix AIO 损坏/损坏?

python - 为什么我的客户端套接字在第一次发送后就死了?