回收DatagramPacket对象是否安全?

时间:2016-10-08 16:43:53

标签: java sockets networking udp datagram

问题:

假设您正在传输高达10 MB / s的速度,最好回收DatagramPacket个对象,而不是每次发送数据包时都创建一个新对象?


故事:

我正在创建一个LAN文件同步应用程序,有时会处理超过30 GB的文件。文件同步应用程序将通过100Mbit有线LAN传输文件。我已经有一个防丢包系统(运行完美)。

该程序运行正常,但CPU使用率约为10%,由于这是一个后台应用程序,这对我来说太过分了。理想情况下,它将占3%左右。

在剖析时,我发现垃圾收集器正处于精神状态,每隔几秒钟激活一次。我知道对象创建(大量完成时)对Java很重要,所以现在我尝试尽可能多地回收对象和数组。每个包含文件数据的数据包大小为1450字节,这意味着以10 MB / s的速率传输大约每秒7,200个数据包。我决定开始回收这些数据包(即,当发送数据包时,DatagramPacket对象将添加到列表中,并且在5秒后可以重新使用DatagramPacket。重新使用DatagramPacket时,方法DatagramPacket.setData()用于分配要发送的数据。

除了发送包含文件数据的数据包之外,我还大致每秒发送一个小数据包,以尝试确定连接的ping。这些ping数据包的大小为10个字节。


错误:

使用DatagramPacket回收功能测试我的应用程序大约30分钟后,会突然出现奇怪的错误。有一次传输的文件被破坏了,有时候我得到了一些我无法绕过的东西......下面是我班级的一些代码。 整数length仅通过applyData()方法设置。

public class PacketToSend {

    private int length;
    private DatagramPacket packet;

    ...

    public void applyData(byte[] newData) {
        try {
            length = newData.length;
            packet.setData(newData, 0, length);
        } catch(java.lang.IllegalArgumentException e) {
            System.out.println("Arraylength = "+newData.length);
            System.out.println("length value = "+length);
        }
    }

    ...

}

每次测试大约20-40分钟后,我收到IllegalArgumentException,告诉我newData的大小为10,length的值为1450因此说长度是非法的。这怎么可能呢?除了在此方法中,变量length不会在其他任何地方修改,并且在调用setData()之前设置!好像DatagramPacket随机切换到发送ping数据......

仅当我启用DatagramPacket回收功能时才会出现这些错误。

请注意,在发送数据包之后,它会被放置在一个列表中并等待5秒钟再重新使用。我想知道操作系统是否以某种方式关注这些数据包,或者某些本机代码是否正在操纵数据。

我的程序中只有一个线程发送数据包,因此这不是线程或同步问题。

因此我的问题是:回收DatagramPacket个对象是一个好主意,而不是每次发送数据包时都创建一个新对象?或者我在玩火和我应该独自留下的东西?


尝试修复:

  • 我在打电话后放了length = newData.length; setData(newData, 0, newData.length);,阻止了 IllegalArgumentException但我仍遇到其他错误,例如 作为连接丢失。无论如何,上面提到的错误,根据 根据我对Java的了解,根本不应该发生,所以我认为其他的东西在这里工作。

1 个答案:

答案 0 :(得分:1)

  

回收DatagramPacket对象是否安全?

据我所知或可以确定,重用DatagramPacket个实例并不存在任何不安全因素。

另一方面,如果实例在两个或多个线程之间共享,并且在没有正确同步的情况下从多个线程访问共享对象肯定是不安全的,那么您描述的错误才有意义。没有多少等待替代同步,因此在重用之前施加5秒延迟的策略可能适得其反 - 它不能保证正确的操作,但它可能会导致程序维护比实际需要更多的活动对象。

如果没有您的计划架构的详细信息,我们只能说明您可以采取哪些措施来解决问题。在最一般的层面上,替代方案是避免在线程之间共享对象并以线程安全的方式访问共享对象。但是,任何线程安全对象共享机制都会带来相对较大的开销,我倾向于认为在一次文件传输过程中执行2000万次线程安全操作的成本太高而且无法接受。因此,最好的办法是避免共享对象。

在每个需要时创建新的DatagramPacket并且不允许它们逃脱创建它们的线程是实现这一目的的一种方法。由于这会导致GC过多,下一个逻辑步骤可能是维护可重用数据包的每线程队列。您可以使用ThreadLocal,但如果每个文件传输由单个线程管理,那么您还可以考虑使用每个文件队列。无论哪种方式,还要注意其他共享,例如DatagramPacket携带的数据缓冲区数组(可能是您可以重用的其他内容)。

此外,如果您小心不要在线程之间共享数据,那么您应该能够在没有重用延迟的情况下这样做。实际上,每个线程可能不需要多个DatagramPacket和一个缓冲区数组。这可以使您的代码不仅更高效,而且更简单。