我在socket服务器的while循环中接收记录。其中每个记录都有一个msg类型,后跟消息长度和实际消息内容。
问题在于,因为我获得了大约一百万条记录,每条记录的记录大小为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);
}
}
}
答案 0 :(得分:2)
您是否未能关闭某些资源并依赖终结者线程来接收它们?或者你刚刚添加了一个终结器(可能是不必要的),它可以防止大量内存被迅速释放。
答案 1 :(得分:2)
如果这实际上是您所做的唯一更改,则很难看出这将如何解决问题。每当Java虚拟机内存不足时,它会在抛出内存不足异常之前自动运行垃圾收集器。不应该自己做这个,也不需要这样做。
您描述的问题的唯一真正解决方案是确保清除对不再需要的对象的任何引用。就像你说:
byte[] ba=new byte[bignumber];
process(ba);
然后你继续做其他事情,ba仍然坐在那里记忆。您想要退出定义它的函数,或者设置ba = null以丢失引用。然后gc可以回收内存。
答案 2 :(得分:0)
您收到的数据是否有最大大小(或者您可以强制执行)?在这种情况下,你可以声明你的字节数组,并在每次迭代时重用它而不分配更多的内存:
...
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;
}
答案 3 :(得分:0)
dIN
变量的类型是什么?也许我误解了,但是你真的需要将输入读入字节数组,然后将字节数组视为流,然后逐行读取流吗?
如果您已经知道内容的结构,为什么要创建所有中间步骤。你也可以process(dIn)
某种方式。
另外,只是为了确认,这是在多线程环境中运行吗?