我们有一个客户端服务器应用程序,1 个服务器,大约 10 个客户端。他们使用自定义查询通过 TCP 套接字进行通信。
系统已经顺利运行了好几个月,但在某个时候,在每天安排的服务器 FULL GC 花费大约 50 秒之后,我们发现客户端发送的查询之间的时间从服务器收到的响应很大,> 10-20 秒。大约 3 小时后系统恢复,一切正常。
在调查该问题时,我们发现:
- 客户端和服务器都没有垃圾回收问题
- 服务器上的查询处理时间很短。
- 服务器上的负载很高。
- 网络带宽未饱和。
- 在 FULL GC 期间未重置连接(在此之前每日 FULL GC 是正常事件)
- 机器和操作系统最近从 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 答案:
没有涉及 EOS,连接已连接,但速度很慢
即使存在 EOS,我也看不到代码如何在 EOS 上旋转,
for
受constructLength
值限制。但是,建议的改进仍然有效。有问题的堆栈跟踪以对继承自
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/