删除分配给字节数组的内存

发布于 2024-11-03 19:45:55 字数 2427 浏览 1 评论 0原文

我正在套接字服务器上的 while 循环中接收记录。其中每个记录都有一个消息类型,后跟消息长度和实际消息内容。

问题是,因为我得到了大约一百万条记录,并且每条记录的记录大小为 277 字节。因此,在大约 40000 条记录之后,我收到 OutOfMemory 错误。代码流程如下所示:

while (true) {              
            msgType = dIn.readByte();

            int msgIntType = msgType & 0xff;

                  // get message length

                  int msgIntLen = dIn.readInt();
                  if (msgIntLen != 0) {

     msgContent = new byte[msgIntLen];
                   switch(msgIntType) {
            case 4:
            //case MSG_DATA:
                // MSG_DATA
                recordCount++;
                processData(msgContent);
                if (recordCount == 2000) {
                sendACK(dOut, msgIntType);
                logger.info("sent ACK for MSG_DATA");
                recordCount = 0;
                }               
                break;

}

我通过在每处理 2000 条记录后发送 ACK 后显式调用 System.gc() 解决了 OutOfMemory 问题,现在它工作得很好,能够在 10 分钟内处理 100 万条记录,没有任何错误。用于调用 System.gc() 的 case 语句的修改代码如下所示:

            case 4:
            //case MSG_DATA:
                // MSG_DATA
                recordCount++;
                processData(msgContent);
                if (recordCount == 2000) {
                sendACK(dOut, msgIntType);
                logger.info("sent ACK for MSG_DATA");
                recordCount = 0;
                             System.gc();
                }               
                break;

但我在其他一些帖子上读到调用 System.gc() 不是一个好的设计方法?是这样吗?如果是的话,你们能建议我其他方法来摆脱这个 OutOfMemory 错误吗?

提前致谢 -JJ

编辑:processData() 的逻辑:

public void processData(byte[] msgContent) throws Exception {

    InputStreamReader inp = new InputStreamReader(

            new ByteArrayInputStream(msgContent));

    BufferedReader br = null;
    try {

        br = new BufferedReader(inp);
                             String line;
        while ((line = br.readLine()) != null) {

                             process each line
                             .
                             }
                  } catch (Exception e) {
        logger.error("exception in " + Utils.getExecutingMethodName(e) 
                + " :" + e.getMessage());
    } finally {
        try {
            if (br != null)
                br.close();
        } catch (IOException e) {
            logger.error("Error: " + e);
        }
    }
}

I am receiving records in while loop at a socket server. where each records has a msg type followed by message length and actual message content.

The problem is that since I get around a million records and each record has a record size of 277 bytes. So, after around 40000 records, I get OutOfMemory error. The code flow looks something like this:

while (true) {              
            msgType = dIn.readByte();

            int msgIntType = msgType & 0xff;

                  // get message length

                  int msgIntLen = dIn.readInt();
                  if (msgIntLen != 0) {

     msgContent = new byte[msgIntLen];
                   switch(msgIntType) {
            case 4:
            //case MSG_DATA:
                // MSG_DATA
                recordCount++;
                processData(msgContent);
                if (recordCount == 2000) {
                sendACK(dOut, msgIntType);
                logger.info("sent ACK for MSG_DATA");
                recordCount = 0;
                }               
                break;

}

I resolved the OutOfMemory issue by explicitly calling System.gc() after sending ACK after every 2000 records processed and now it works perfectly fine and able to handle 1 million records without any error in less than 10 minutes. The modified code for case statement for calling System.gc() looks like:

            case 4:
            //case MSG_DATA:
                // MSG_DATA
                recordCount++;
                processData(msgContent);
                if (recordCount == 2000) {
                sendACK(dOut, msgIntType);
                logger.info("sent ACK for MSG_DATA");
                recordCount = 0;
                             System.gc();
                }               
                break;

But I read here on some other posts that calling System.gc() is not a good design approach ? Is it so ? If yes, could you guys suggest me some other way to get rid of this OutOfMemory error ?

Thanks in advance
-JJ

EDIT: logic for processData():

public void processData(byte[] msgContent) throws Exception {

    InputStreamReader inp = new InputStreamReader(

            new ByteArrayInputStream(msgContent));

    BufferedReader br = null;
    try {

        br = new BufferedReader(inp);
                             String line;
        while ((line = br.readLine()) != null) {

                             process each line
                             .
                             }
                  } catch (Exception e) {
        logger.error("exception in " + Utils.getExecutingMethodName(e) 
                + " :" + e.getMessage());
    } finally {
        try {
            if (br != null)
                br.close();
        } catch (IOException e) {
            logger.error("Error: " + e);
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

烂人 2024-11-10 19:45:57

您收到的数据是否有最大大小(或者您可以强制执行)吗?在这种情况下,您可以在 while 之外声明字节数组,并在每次迭代时重用它,而无需分配更多内存:

...
private static final int BUFFER_SIZE = 102400; //start with a buffer big enough to lower the chances of resizing it -- e.g. 100K
...
msgContent = new byte[BUFFER_SIZE];
while (true) {              
            msgType = dIn.readByte();

            int msgIntType = msgType & 0xff;

                  // get message length

                  int msgIntLen = dIn.readInt();
                  if (msgIntLen != 0) {
                   if( msgIntLen > msgContent.length ) //only resize when needed otherwise reuse
                     msgContent = new byte[msgIntLen];

                   switch(msgIntType) {
            case 4:
            //case MSG_DATA:
                // MSG_DATA
                recordCount++;
                processData(msgContent, msgIntLen); //need to change your function to also pass in the size of the message read!
                if (recordCount == 2000) {
                sendACK(dOut, msgIntType);
                logger.info("sent ACK for MSG_DATA");
                recordCount = 0;
                }               
                break;

}

Is there a maximum size to the data you receive (or can you enforce one)? In which case you can declare your byte array outisde the while and reuse it at each iteration without allocating more memory:

...
private static final int BUFFER_SIZE = 102400; //start with a buffer big enough to lower the chances of resizing it -- e.g. 100K
...
msgContent = new byte[BUFFER_SIZE];
while (true) {              
            msgType = dIn.readByte();

            int msgIntType = msgType & 0xff;

                  // get message length

                  int msgIntLen = dIn.readInt();
                  if (msgIntLen != 0) {
                   if( msgIntLen > msgContent.length ) //only resize when needed otherwise reuse
                     msgContent = new byte[msgIntLen];

                   switch(msgIntType) {
            case 4:
            //case MSG_DATA:
                // MSG_DATA
                recordCount++;
                processData(msgContent, msgIntLen); //need to change your function to also pass in the size of the message read!
                if (recordCount == 2000) {
                sendACK(dOut, msgIntType);
                logger.info("sent ACK for MSG_DATA");
                recordCount = 0;
                }               
                break;

}
栖竹 2024-11-10 19:45:56

您是否未能关闭某些资源并依赖终结器线程来获取它们?或者您是否刚刚添加了一个终结器(可能不必要),该终结器会阻止立即释放大量内存。

Are you failing to close some resource and relying upon the finaliser thread to pick them up? Or have you just added a finaliser (probably unnecessary) that is preventing a significant amount of memory to be freed promptly.

嗳卜坏 2024-11-10 19:45:56

如果这确实是您所做的唯一更改,则很难看出这将如何解决问题。每当 Java 虚拟机内存不足时,它都会在抛出内存不足异常之前自动运行垃圾收集器。您不需要自己这样做,这样做也没有任何价值。

解决您所描述的问题的唯一真正解决方案是确保清除对不再需要的对象的任何引用。就像如果你说:

byte[] ba=new byte[bignumber];
process(ba);

然后你继续做其他事情,ba仍然坐在那里占用内存。您想要退出定义该函数的函数,或者设置 ba=null 以丢失引用。然后gc就可以回收内存了。

If that is literally the only change you made, it is difficult to see how that would fix the problem. Whenever the Java Virtual Machine is running low on memory, it runs the garbage collector automatically before throwing an out of memory exception. There should be no need to do this yourself nor any value in doing so.

The only real solution to the problem you describe is to make sure that you clear out any references to objects that are no longer needed. Like if you say:

byte[] ba=new byte[bignumber];
process(ba);

and then you go on and do other things, ba is still sitting there hogging up memory. You want to either exit the function where it was defined, or set ba=null to lose the reference. Then the gc can recycle the memory.

↙厌世 2024-11-10 19:45:56

您的 dIN 变量的类型是什么?也许我误解了,但是您真的需要将输入读入字节数组,然后将字节数组视为流,然后逐行读取流吗?
如果您已经知道内容的结构,为什么还要创建所有中间步骤。您也可以以某种方式process(dIn)

另外,只是为了确认一下,这是在多线程环境中运行吗?

What is the the type of your dIN variable? Maybe I am misunderstanding, but do you really need to read your input into a byte array, then consider the byte array as a stream, and then read the stream line by line?
If you already know the structure of your content, why create all the intermediate steps. You could just as well process(dIn) somehow.

Also, just to confirm, is this running in a multi-threaded environment??

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文