我们有一个客户端服务器应用程序,一台服务器,大约10个客户端。它们使用自定义查询通过tcp套接字进行通信。
该系统已经运行了好几个月,但是在某个时间点(每天排定的服务器FULL GC花费了大约50s )之后,我们发现,客户端发送的查询与从服务器收到的响应之间的时间很大,> 10-20s。 3小时后,系统恢复正常,一切恢复正常。
在调查问题时,我们发现:
我们在服务器上进行了一个线程转储:
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
。
问题是:为什么在完全GC暂停后大多数连接速度变慢?为什么stacktrace以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答案:
for
受constructLength
值限制。但是,建议的改进仍然有效。 DataInputStream
继承的(_socketDIS.read()
(FilterInputStream.read()
)的读取,请参见上面的代码。 DataInputStream
而不是BufferedInputStream
缺少read()
。在
FilterInputStream.read()
中,有一个在in.read()
上调用的BufferedInputStream
,它定义了自己的read()
方法。但是stacktrace在中间停止,没有到达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);
注意_socketDIS
显然不是BufferedInputStream
,而是未缓冲的DataInputStream,
。
编辑
为什么堆栈跟踪以FilterInputStream.read()结尾?
仔细地看。 BufferedInputStream
没有实现所有三个read()重载。我忘记了其中一个是在FilterInputStream,基类中实现的,另外两个重载调用了该实现。
它不应该在BufferedInputStream中结束吗
不,请参见上面。
还是在套接字输入流中?
是的,如果它正在阻塞,但是不是,可能是因为您的代码较差,导致您在流的末尾旋转。
这种读取会导致服务器上的高负载吗?
是。
java.util.Scanner不返回到提示 - javaimport java.util.Scanner; class newClass { public static void main(String args[]) { Scanner s = new Scanner(System.in); while (s.hasNext()) { System.out.println(s.next()); } s.clos…
Java:线程池如何将线程映射到可运行对象 - java试图绕过Java并发问题,并且很难理解线程池,线程以及它们正在执行的可运行“任务”之间的关系。如果我创建一个有10个线程的线程池,那么我是否必须将相同的任务传递给池中的每个线程,或者池化的线程实际上只是与任务无关的“工人无人机”可用于执行任何任务?无论哪种方式,Executor / ExecutorService如何将正确的任务分配给正确的线程? 参考方案 …
JAVA:字节码和二进制有什么区别? - javajava字节代码(已编译的语言,也称为目标代码)与机器代码(当前计算机的本机代码)之间有什么区别?我读过一些书,他们将字节码称为二进制指令,但我不知道为什么。 参考方案 字节码是独立于平台的,在Windows中运行的编译器编译的字节码仍将在linux / unix / mac中运行。机器代码是特定于平台的,如果在Windows x86中编译,则它将仅在Win…
java:继承 - java有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…
Java:BigInteger,如何通过OutputStream编写它 - java我想将BigInteger写入文件。做这个的最好方式是什么。当然,我想从输入流中读取(使用程序,而不是人工)。我必须使用ObjectOutputStream还是有更好的方法?目的是使用尽可能少的字节。谢谢马丁 参考方案 Java序列化(ObjectOutputStream / ObjectInputStream)是将对象序列化为八位字节序列的一种通用方法。但…