【发布时间】:2017-02-17 11:48:22
【问题描述】:
问题:
假设您的传输速度高达 10 MB/s,回收 DatagramPacket 对象而不是每次发送数据包时都创建一个新对象是个好主意吗?
故事:
我正在创建一个 LAN 文件同步应用程序,该应用程序有时会处理超过 30 GB 的文件。文件同步应用程序将通过 100Mbit 有线 LAN 传输文件。我已经有一个防丢包系统(完美无缺)。
该程序运行良好,但占用了大约 10% 的 CPU 使用率,而且由于这是一个后台应用程序,这对我来说太多了。理想情况下,它会在 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 的了解,根本不应该发生,所以我认为这里还有其他事情在起作用。
【问题讨论】:
-
看起来像一个多线程错误。如果你每次都创建一个新对象,没有什么可分享的,只是回收它,你需要确保访问是线程本地的或线程安全的。
-
将字段移动到局部变量以改善事物是一个很大的提示。尝试检查您是否只从同一线程访问该对象。即保存 Thread.currentThread() 并检查它。
-
类
PacketToSend是否真的需要维护自己的length副本,与packet维护的副本分开?并不是说删除它就一定会解决潜在的线程安全问题,但保留相同数据的多个副本确实会暴露更大的错误范围。 -
@JohnBollinger 我猜你是对的。上面的代码与原始类的版本略有不同,但是是的,我可以删除该变量。我现在已经这样做了。 :) @PeterLawrey 我查看了将
DatagramPackets 添加和删除到列表中的代码,我确实发现了一个线程确实以非同步方式将DatagramPacket 对象添加到列表中的情况。我现在已经使这个线程安全了。但是,该列表使用peekFirst()来检查头部对象是否有资格重复使用。如果符合条件,则调用pollFirst()。我希望这能解决这个问题,但我仍然保持谨慎。多谢你们。 :)
标签: java sockets networking udp datagram