【问题标题】:java stackoverflowerror thrown in infinite loopjava stackoverflowerror 在无限循环中抛出
【发布时间】:2013-06-22 15:32:54
【问题描述】:

我有以下函数可以启动一个 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 填充的消息将接收到的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 类中做错了什么以收到以下 stackoverflowerror:

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

【问题讨论】:

  • 看起来不错,我看到的唯一可能是真的很奇怪...您使用的是 IPv4 还是 IPv6?因为 IPv6 允许非常大的数据报。
  • 我使用的是 ipv4,所有这些数据报都很小
  • 两个想法:1)您是否尝试过检查rcvMsg 是否为空或为空,如果是则跳过两个有问题的行? 2)您是否尝试过将tmcmessage 声明放在run() 方法中?
  • 太特别了。广大观众不感兴趣
  • 它与在无限循环中创建对象时抛出堆栈溢出有关。这怎么不适用于@user829755 的广大受众?

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


【解决方案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'ed。

【讨论】:

  • incomingBinaryMessage 是一个 UDP 数据包,看起来像: byte[] rcvMsg = datagrampacket.getData();我也更新了 Message pojo。
  • 它可能是一个 UDP 数据包,但循环中没有任何东西可以修改它。一旦你开始循环,它就会一直运行直到外部停止,一遍又一遍地重新处理相同的输入数据包。
【解决方案2】:

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

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

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

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

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

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

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

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

【讨论】:

  • 嗨罗素,我添加了关闭。这并没有解决问题。再次感谢您的帮助。
【解决方案3】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-02-11
    • 1970-01-01
    • 2023-04-03
    • 1970-01-01
    • 2016-05-13
    • 1970-01-01
    相关资源
    最近更新 更多