抛出无限循环的java stackoverflowerror

时间:2013-06-22 15:32:54

标签: java memory-leaks out-of-memory infinite-loop stack-overflow

我有以下函数启动jsvc守护程序来接收UDP消息:

 @Override
public void start() throws Exception {
    byte[] buf = new byte[1000];

    DatagramPacket dgp = new DatagramPacket(buf, buf.length);
    DatagramSocket sk;

    sk = new DatagramSocket(1000);
    sk.setSoTimeout(0);

    byte[] rcvMsg = null;


    run(sk, dgp, rcvMsg);


}

超时为0时,套接字将阻塞,直到另一条消息进入。这是触发连续运行以下while循环的原因:

 MessageConstructor tmc =null;
Message message = null;

public void run(DatagramSocket sk, DatagramPacket dgp, byte[] rcvMsg){
    while(true){
        try {
            sk.receive(dgp);
        } catch (IOException e) {
            e.printStackTrace();
        }
        rcvMsg = dgp.getData();

         tmc = new MessageConstructor();
         message = tmc.constructMessageFromBinary(rcvMsg);

        tmc =null;
        message = null;
     }


}

创建的唯一新对象是下面的MessageConstructor:

在constructTagMessageFromBinary函数内部,一个从ByteArrayInputStream填充的Message,它将收到的UDP消息转换为int。

 public Message constructTagMessageFromBinary(byte[] rcvMsg) {

Message message = new Message();
ByteArrayInputStream bais = new ByteArrayInputStream(rcvMsg);
DataInput input = new DataInputStream(bais);

    try {

        int MsgType = 0;
        MsgType = input.readShort();

        message.setType(MsgType);

        return message;

    } catch (IOException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    return null;
}

最后,消息是一个pojo。

公共类消息{

private int type;
 //getters and setters omitted

}

我已将内存泄漏范围缩小到以下几行:

 tmc = new MessageConstructor();
 message = tmc.constructMessageFromBinary(rcvMsg);

如果我发表评论,只要守护程序运行,内存就不会增长并保持一致。

我在MessageConstructor类中做错了什么来接收以下stackoverflower:

Service exit with a return value of 143
java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:616)
        at org.apache.commons.daemon.support.DaemonLoader.start(DaemonLoader.java:243)
Caused by: java.lang.NullPointerException
        at MainDaemon.start(MainDaemon.java:116)
        ... 5 more
Cannot start daemon
Service exit with a return value of 5
java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:616)
        at org.apache.commons.daemon.support.DaemonLoader.start(DaemonLoader.java:243)
Caused by: java.lang.NullPointerException
        at MainDaemon.start(MainDaemon.java:117)
        ... 5 more
Cannot start daemon
Service exit with a return value of 5
Service exit with a return value of 143
Service exit with a return value of 143
Service exit with a return value of 143
Service exit with a return value of 143
Service exit with a return value of 143
Service exit with a return value of 143
Service exit with a return value of 143
Service exit with a return value of 143
java.lang.reflect.InvocationTargetException
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:616)
        at org.apache.commons.daemon.support.DaemonLoader.start(DaemonLoader.java:243)
Caused by: java.lang.StackOverflowError

3 个答案:

答案 0 :(得分:1)

    public void run() {             
        while(!stopped){

            byte[] rcvMsg = incomingBinaryMessage;

            MessageCreator tmc = new MessageCreator();
            Message message = null;
            try {
                message = tmc.createMessage(rcvMsg);
            System.out.println(message);
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

此代码似乎不执行任何I / O. incomingBinaryMessage不是方法调用,而是对现有byte[]的对象引用。

循环反复运行,一遍又一遍地创建相同的消息。

正常情况下,GC应该跟上你的步伐,因为你在每个循环中都丢弃了消息和MessageCreator实例。但是,你没有显示的一段代码,Message的构造函数可以保存对消息的引用(即将它们添加到地图中?)并防止它们被GC化。

答案 1 :(得分:0)

我对此并不是百分之百确定,因此我将提供多个建议,希望它们的某些组合可以解决您的问题。如果您是高级用户,我也会“大声打字”,请原谅我,其中一些非常明显。

你有一个基本上无限的循环。此循环创建一个字节数组对象,该对象具有分配给它的数据。您创建一个MessageCreator引用并为其分配一个对象。您创建一个空消息引用,输入一个try块,然后为该引用分配一个对象(值)。那个赋值,或者特别是创建赋值的方法就是问题,所以让我们来看看。

您的createMessage方法接受一个字节数组,该数组是原始字节数组(rcvMsg)的值的副本。它是它自己的引用,但指向堆上的同一个对象。在实际方法中,您可以创建Message引用和对象。创建BAIS参考并指向相应的BAIS对象,该对象接收字节数组引用。这意味着BAIS对象指向ORIGINAL字节数组值。然后你有了DIS,它基本上在本地封装了BAIS。

try块创建一个值为0的int引用,然后立即将其重置为input.readShort()的值。这将从ORIGINAL字节数组中读取值。您将消息类型设置为该值,并返回消息。

退出方法时,将销毁对您创建的消息对象的引用。幸运的是,您通过return方法传输该引用。所以这个对象是安全的。不幸的是,您的输入流保持打开状态! jvm可能会因为它仍处于打开状态而单独留下,我不确定。如果是这样,更重要的信息信息是它仍然具有对字节数组值的引用,从而使它们保持活动状态。下次循环执行时,它会丢弃旧的rcvMsg引用,但不会丢弃对象,因为该对象仍有引用。每次迭代,您都会有越来越多的字节数组放在堆上,直到内存不足为止。

当您注释掉方法调用时,您永远不会打开(并保持打开)数据流,因此底层对象(字节数组)永远不会有持久引用,因此被垃圾收集器销毁。

TLDR :关闭您的信息流。在createMessage方法中input.close();之前添加return message;(可能需要更新的try / catch)。

同样,不确定这一点,但对于我扭曲的逻辑感,这是有道理的。

答案 2 :(得分:0)

问题是我在循环中打开数据库连接而没有关闭它。