删除分配给字节数组的内存
我正在套接字服务器上的 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
您收到的数据是否有最大大小(或者您可以强制执行)吗?在这种情况下,您可以在 while 之外声明字节数组,并在每次迭代时重用它,而无需分配更多内存:
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:
您是否未能关闭某些资源并依赖终结器线程来获取它们?或者您是否刚刚添加了一个终结器(可能不必要),该终结器会阻止立即释放大量内存。
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.
如果这确实是您所做的唯一更改,则很难看出这将如何解决问题。每当 Java 虚拟机内存不足时,它都会在抛出内存不足异常之前自动运行垃圾收集器。您不需要自己这样做,这样做也没有任何价值。
解决您所描述的问题的唯一真正解决方案是确保清除对不再需要的对象的任何引用。就像如果你说:
然后你继续做其他事情,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:
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.
您的
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??