【问题标题】:Multiple Threads Accessing the socket read causing jvm OutOfMemory Issue多个线程访问套接字读取导致 jvm OutOfMemory 问题
【发布时间】:2013-06-04 08:42:10
【问题描述】:

假设我们有套接字客户端,它正在执行读写操作。有两个线程执行发送和接收操作(请求发送方/响应接收方)。这些线程一一运行Junit测试用例(像一些压力测试,简单查询)。对于我的情况,我正在运行客户端,它将在 DB 端和它自己的端之间建立套接字通信。在这个逻辑中,请求发送者线程将请求发送到DB端(将请求写入套接字),接收者线程通过读取套接字接收响应(从套接字读取响应)。实际问题随机出现。假设如果请求发送者线程发送请求,响应接收者尚未收到来自发送请求的另一端的响应。在此期间,如果任何其他测试用例(我的意思是请求线程)开始(在特定的超时时间之后,如果没有来自 DB 端的响应)发送它们的请求并执行执行意味着,响应接收器线程具有逻辑

byte[] ofsRes = new byte[size]; 

这部分会收到jVm Out of memory错误发生。我怀疑同时读取响应接收器线程会导致此问题,在堆中分配更多大小的字节对象。

从套接字读取数据

int size = dataInputStream.readInt();

您能否帮助任何人解决此问题,内存不足错误/可能是线程并发,即使我使用了同步。但没有用。如果您需要任何东西,请告诉我。

private static class ResponseReceiver implements Runnable {

        private InputStream _in;
        private List<String> _responses = new ArrayList<String>();
        private int _expectedCount;
        private volatile boolean _finished = false;

        public ResponseReceiver(InputStream in, int expectedCount) {
            _in = in;
            _expectedCount = expectedCount;
        }

        public List<String> getResponses() {
            return _responses;
        }

        public void run() {
            DataInputStream dataInputStream = new DataInputStream(_in);
            try {
                while (true) {
                    _logger.info("_responses.size() >= _expectedCount : -> " + (_responses.size() >= _expectedCount) +
                            " : " + "_responses.size() -> " + _responses.size() + " : " + "_expectedCount -> " + _expectedCount);
                    if (_responses.size() >= _expectedCount) {
                        _finished = true;
                        return;
                    }
                    int size = dataInputStream.readInt();
                    _logger.info("size captured : " + size);
                    if (size > 0) {
                        int readSize = 0;
                        _logger.info( " ofsRes Creating with this size : " + size);
                        **byte[] ofsRes = new byte[size];**
                        while (size > 0) {
                            _logger.info( " Buffer Creating with this size : " + size);
                            byte[] buffer = new byte[size];
                            int bufferSize = _in.read(buffer);
                            _logger.info( " Reading data created Buffer Size : " + bufferSize);
                            System.arraycopy(buffer, 0, ofsRes, readSize,
                                    bufferSize);
                            readSize += bufferSize;
                            _logger.info( " readSize (readSize += bufferSize): " + readSize);
                            size -= bufferSize;
                            _logger.info( " size (size -= bufferSize): " + size);
                        }
                        _responses.add(new String(ofsRes));
                        _logger.info("ofsRes added..");
                    }
                }
            } catch (IOException e) {
                throw new RuntimeException("Error reading data from socket: "
                        + e.getMessage());
            }
        }
    }

执行顺序的痕迹:


2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:_responses.size() >= _expectedCount:-> false:_responses.size() -> 905:_expectedCount -> 1000 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:捕获的大小:1529 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:ofsRes 使用此大小创建:1529 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:使用此大小创建缓冲区:1529 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:读取数据创建缓冲区大小:1529 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:readSize(readSize += bufferSize):1529 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:大小(大小-= bufferSize):0 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:ofsRes 添加.. 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 INFO: _responses.size() >= _expectedCount : -> false : _responses.size() -> 906 : _expectedCount -> 1000

假设测试用例正在运行等待响应 ****** (906)

2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.SocketClientTest ofsmlEnqSimpleTest13 信息:OFSML 13 简单查询测试正在进行中...

又一个案例开始

2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient 发送 信息:System.currentTimeMillis():-> 1369635313336:System.currentTimeMillis()- startTime:-> 0:超时-> 5000:startTime-> 1369635313336:条件:真 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:_responses.size() >= _expectedCount : -> false : _responses.size() -> 0 : _expectedCount -> 1

* 假设可能有不同的读取机会*

2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:捕获的大小:195399 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:捕获的大小:1195528239 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 INFO: ofsRes 使用这个尺寸创建 : 195399 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:ofsRes 使用此大小创建:1195528239 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:使用此大小创建缓冲区:195399 线程“Thread-22”java.lang.OutOfMemoryError 中的异常:Java 堆空间 在 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver.run(SocketClient.java:147) 在 java.lang.Thread.run(Thread.java:662) 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:读取数据创建缓冲区大小:759 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:readSize(readSize += bufferSize):759 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:大小(大小-= bufferSize):194640 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:使用此大小创建缓冲区:194640 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:读取数据创建缓冲区大小:1533 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:readSize(readSize += bufferSize):2292 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:大小(大小-= bufferSize):193107 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:使用此大小创建缓冲区:193107 2013 年 5 月 27 日上午 11:45:13 com.xxx.tocf.test.socket.SocketClient$ResponseReceiver 运行 信息:读取数据创建缓冲区大小:767

【问题讨论】:

    标签: java multithreading sockets memory-leaks out-of-memory


    【解决方案1】:

    假设发送者线程是否等待来自另一端的特定响应。

    为什么?你已经有一个接收线程。

    在此期间,如果任何其他测试用例(我的意思是线程)开始推送他们的请求并执行执行意味着,响应接收器线程具有以下逻辑

    byte[] ofsRes = new byte[size];
    

    size 的值从何而来?发送者线程和接收者线程都试图读取响应的确切含义是什么?我不知道您希望发生什么,但 发生的事情完全未定义。这没有任何意义。您需要决定谁应该阅读响应,并让他们阅读。可能您需要在发送者和接收者线程之间引入一些同步,以便发送者在接收者收到前一个请求的结果之前不会发送下一个请求。更好的是,摆脱接收线程并让发送线程接收自己的响应。

    这部分会收到jVm Out of memory错误发生。

    当然会。它从某个地方获得size 的基本随机值,并尝试分配一定数量的内存,因此也是随机的。

    我怀疑同时读取响应接收器线程会导致此问题,在堆中分配更多大小的字节对象。

    再次正确。解决办法是不要这样做。

    【讨论】:

    • 感谢您的回复。我在这里修改了一些内容。对不起,我错了。发件人线程实际上没有等待。大小是捕获数据的变量 int size = dataInputStream.readInt();.请给出您的建议!
    猜你喜欢
    • 2014-08-01
    • 1970-01-01
    • 2016-12-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-14
    • 2014-03-12
    • 1970-01-01
    相关资源
    最近更新 更多