use*_*831 3 java multithreading
我已经实现了一个java程序.这基本上是一个具有固定线程数的多线程服务.每个线程一次执行一个任务,创建一个hashSet,hashset的大小可以在单个hashset中从10到20,000个项目变化.在每个线程结束时,使用synchronized将结果添加到共享集合List.
问题发生在某些时候我开始出现内存异常.在进行了一些研究之后,我发现当GC忙于清除内存时会发生这种内存异常,此时它会阻止整个世界执行任何操作.
请给我一些如何处理如此大量数据的建议.Hashset是否是一个正确的数据结构?如何处理内存异常,我的意思是一种方法是使用System.GC(),这又不好,因为它会减慢整个过程.或者我可以在将其添加到共享集合列表后处置"HashSet hsN"吗?
请让我知道你的想法,并指导我在哪里出错.这项服务将处理大量的数据处理.
谢谢
//business object - to save the result of thread execution
public class Location{
integer taskIndex;
HashSet<Integer> hsN;
}
//task to be performed by each thread
public class MyTask implements Runnable {
MyTask(long task) {
this.task = task;
}
@Override
public void run() {
HashSet<Integer> hsN = GiveMeResult(task);//some function calling which returns a collection of integer where the size vary from 10 to 20000
synchronized (locations) {
locations.add(task,hsN);
}
}
}
public class Main {
private static final int NTHREDS = 8;
private static List<Location> locations;
public static void main(String[] args) {
ExecutorService executor = Executors.newFixedThreadPool(NTHREDS);
for (int i = 0; i < 216000; i++) {
Runnable worker = new MyTask(i);
executor.execute(worker);
}
// This will make the executor accept no new threads
// and finish all existing threads in the queue
executor.shutdown();
// Wait until all threads are finish
while (!executor.isTerminated()) {
}
System.out.println("Finished all threads");
}
}
Run Code Online (Sandbox Code Playgroud)
对于这样的实现,JAVA是最佳选择还是C#.net4?
我可以看到几个问题:
您在MyTask对象上进行同步,该对象是为每次执行单独创建的.您应该在共享对象上进行同步,最好是您正在修改的locations对象,即对象.
216,000次运行,再乘以10,000个返回的对象,每个Integer对象最少12个字节,大约24 GB的内存.您是否在计算机上拥有那么多物理内存,更不用说JVM了?
32位JVM的堆大小限制小于2 GB.另一方面,在64位JVM上,Integer对象大约需要16个字节,这会将内存需求提高到30 GB以上.
有了这些数字,你得到一个OutOfMemoryError......就不足为奇了......
PS:如果你确实拥有那么多可用的物理内存并且你仍然认为你正在做正确的事情,你可能想看看调整JVM堆大小.
编辑:
即使JVM可以使用25GB的内存,它仍然可以推动它:
Integer现代64位JVM上的每个对象都需要16个字节.
无论您使用哪种实现,您还需要一个指向它的8字节引用List.
如果使用链表实现,则每个条目的列表条目对象的开销也至少为24字节.
最好你可能希望以Integer25GB 存储大约1,000,000,000个对象 - 如果你使用链表那么一半.这意味着每个任务平均不会产生超过5,000个(分别为2,500个)对象而不会导致错误.
我不确定你的确切要求,但是你考虑过返回一个更紧凑的物体吗?例如,int[]从每个生成的数组HashSet只保留每个结果最少4个字节而没有对象容器开销.
编辑2:
我刚刚意识到你将HashSet对象本身存储在列表中.HashSet对象在HashMap内部使用,然后使用HashMap.Entry每个条目的对象.在64位JVM上,除了存储的对象之外,入口对象还消耗大约40个字节的内存:
指向Integer对象的关键引用- 8个字节.
值引用(始终null在HashSet中) - 8个字节.
下一个条目引用 - 8个字节.
哈希值 - 4个字节.
对象开销 - 8个字节.
对象填充 - 4个字节.
即每个Integer对象,你需要56个字节存储在一个HashSet.如果典型的HashMap加载因子为0.75,则应为HashMap数组引用添加另外10个或多个字节.每个66字节Integer只能存储大约400,000,000个25 GB的此类对象,而不考虑应用程序的其余任何任何其他开销.每个任务不到2,000个对象......
编辑3:
你最好存储一个排序的 int[]数组而不是一个HashSet.对于任何任意整数,该数组在对数时间内是可搜索的,并且将每个数字的内存消耗最小化为4个字节.考虑到内存I/O,它也将与实现一样快(或更快)HashSet.
| 归档时间: |
|
| 查看次数: |
1400 次 |
| 最近记录: |