导致完全GC后套接字连接变慢的原因是什么? - java

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

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

    我们代码中的inBufferedInputStream

    问题是:为什么在完全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答案:

  • 没有涉及EOS,连接建立了,但是它们非常慢
  • 即使有EOS,我也看不到代码如何在EOS上旋转,但forconstructLength值限制。但是,建议的改进仍然有效。
  • 有问题的堆栈跟踪结束于对从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不返回到提示 - java

    import 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:字节码和二进制有什么区别? - java

    java字节代码(已编译的语言,也称为目标代码)与机器代码(当前计算机的本机代码)之间有什么区别?我读过一些书,他们将字节码称为二进制指令,但我不知道为什么。 参考方案 字节码是独立于平台的,在Windows中运行的编译器编译的字节码仍将在linux / unix / mac中运行。机器代码是特定于平台的,如果在Windows x86中编译,则它将仅在Win…

    java:继承 - java

    有哪些替代继承的方法? java大神给出的解决方案 有效的Java:偏重于继承而不是继承。 (这实际上也来自“四人帮”)。他提出的理由是,如果扩展类未明确设计为继承,则继承会引起很多不正常的副作用。例如,对super.someMethod()的任何调用都可以引导您通过未知代码的意外路径。取而代之的是,持有对本来应该扩展的类的引用,然后委托给它。这是与Eric…

    Java:BigInteger,如何通过OutputStream编写它 - java

    我想将BigInteger写入文件。做这个的最好方式是什么。当然,我想从输入流中读取(使用程序,而不是人工)。我必须使用ObjectOutputStream还是有更好的方法?目的是使用尽可能少的字节。谢谢马丁 参考方案 Java序列化(ObjectOutputStream / ObjectInputStream)是将对象序列化为八位字节序列的一种通用方法。但…