0

我有以下函数可以启动一个 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 类中做错了什么来接收以下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
4

3 回答 3

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。

于 2013-06-24T21:45:36.710 回答
0

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

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

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

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

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

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

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

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

于 2013-06-24T22:01:26.927 回答
0

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

于 2013-11-19T23:01:52.703 回答