Java 多线程-OutOfMemory
我使用的Java 多线程-OutOfMemory,java,multithreading,memory-management,hashmap,Java,Multithreading,Memory Management,Hashmap,我使用的ThreadPoolExecutor有5个活动线程,任务数量非常庞大。 队列几乎立即被可运行任务的实例填满(pool.execute(new WorkingThreadTask())) 每个工作读取任务都有一个哈希映射: Map<Integer, HashMap<Integer, String>> themap ; 我不知道它是从哪里来的 Name Instance count Size (bytes) byte[ ] 2519560 918117496
ThreadPoolExecutor
有5个活动线程,任务数量非常庞大。队列几乎立即被可运行任务的实例填满(
pool.execute(new WorkingThreadTask())
)
每个工作读取任务
都有一个哈希映射
:
Map<Integer, HashMap<Integer, String>> themap ;
我不知道它是从哪里来的
Name Instance count Size (bytes)
byte[ ] 2519560 918117496
oracle.jdbc.ttc7.TTCItem 2515402 120739296
char[ ] 357882 15549280
java.lang.String 9677 232248
int[ ] 2128 110976
short[ ] 2097 150024
java.lang.Class 1537 635704
java.util.concurrent.locks.ReentrantLock$NonfairSync 1489 35736
java.util.Hashtable$Entry 1417 34008
java.util.concurrent.ConcurrentHashMap$HashEntry[ ] 1376 22312
java.util.concurrent.ConcurrentHashMap$Segment 1376 44032
java.lang.Object[ ] 1279 60216
java.util.TreeMap$Entry 828 26496
oracle.jdbc.dbaccess.DBItem[ ] 802 10419712
oracle.jdbc.ttc7.v8TTIoac 732 52704
我不确定内部映射是否正确,但我怀疑问题在于您正在创建大量的任务,这些任务占用了内存。您应该使用有界任务队列并限制作业生产者 看看我的答案: 总之,您应该创建自己的有界队列,然后使用
RejectedExecutionHandler
阻止生产者,直到队列中有空间为止。比如:
final BlockingQueue<WorkingThreadTask> queue =
new ArrayBlockingQueue<WorkingThreadTask>(100);
ThreadPoolExecutor threadPool =
new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, queue);
// we need our RejectedExecutionHandler to block if the queue is full
threadPool.setRejectedExecutionHandler(new RejectedExecutionHandler() {
@Override
public void rejectedExecution(WorkingThreadTask task,
ThreadPoolExecutor executor) {
try {
// this will block the producer until there's room in the queue
executor.getQueue().put(task);
} catch (InterruptedException e) {
throw new RejectedExecutionException(
"Unexpected InterruptedException", e);
}
}
});
final BlockingQueue队列=
新的ArrayBlockingQueue(100);
线程池执行器线程池=
新的ThreadPoolExecutor(nThreads,nThreads,0L,TimeUnit.millizes,queue);
//如果队列已满,我们需要我们的RejectedExecutionHandler来阻止
setRejectedExecutionHandler(新的RejectedExecutionHandler(){
@凌驾
public void拒绝执行(工作读取任务任务,
线程池执行器(执行器){
试一试{
//这将阻止制作人,直到队列中有空间为止
executor.getQueue().put(任务);
}捕捉(中断异常e){
抛出新的RejectedExecutionException(
“意外中断异常”,e);
}
}
});
编辑:
我想hashmap中没有韭菜。。。当线程完成时,hashmap是否被清理
在任务完成时,您可以考虑在工作<代码> HashMap < /C>和其他集合上积极调用<代码> CULL()/代码>。虽然GC最终应该会获得这些信息,但如果您的内存有限,给GC一些帮助可能会解决您的问题
如果这不起作用,可以使用探查器来帮助您确定内存存放的位置 编辑:查看探查器输出后,
字节[]
很有趣。通常,这表示某种序列化或其他IO。您也可能正在数据库中存储blob。然而,oracle.jdbc.ttc7.TTCItem
非常有趣。这向我表明您没有关闭某个数据库连接。请确保使用适当的try/finally块来关闭连接。HashMap在内存使用方面会带来很大的开销。。。。。它每个条目至少包含36个字节,加上键/值本身的大小——每个至少包含32个字节(我认为这大约是32位sun JVM的典型值)。。。。快速计算:
20,000 tasks, each with map with 2000 entry hashmap. The value in the map is another map with 5 entries.
-> 5-entry map is 1* Map + 5* Map.Object entries + 5*keys + 5*values = 16 objects at 32 bytes => 512 bytes per sub-map.
-> 2000 entry map is 1* Map, 2000*Map.Object + 2000 keys + 2000 submaps (each is 512 bytes) => 2000*(512+32+32) + 32 => 1.1MB
-> 20,000 tasks, each of 1.1MB -> 23GB
因此,您的总占地面积是23GB
合乎逻辑的解决方案是限制向ExecutorService提供信息的阻塞队列的深度,并且只创建足够的子任务使其保持忙碌。。。。。在队列中设置大约64个条目的限制,那么一次实例化的任务将不会超过64+5个。当wpace在执行者队列中可用时,您可以创建并添加另一个任务。您可以通过不在正在处理的任务之前添加太多任务来提高效率。尝试检查队列,并且仅当条目少于1000条时才添加到队列中 您还可以提高数据结构的效率。带有整数键的映射通常可以简化为某种数组
最后,现在1GB已经不是那么多了。我的手机有2GB。如果您要处理大量数据,我建议您使用32-64 GB内存和64位JVM的机器。从大的
字节[]
s,我会怀疑IO相关问题(除非您正在处理视频/音频或其他内容)
需要注意的事项:
- DB:你想一次读大量的东西吗?你可以 e、 g.使用光标不要这样做
- 文件/网络:您是否试图同时从文件/网络中读取大量内容?您应该将负载“传播”到任何正在读取的对象,并调节读取速率李>
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(queueSize);
ThreadPoolExecutor tpe = new ThreadPoolExecutor(
threadNum,
threadNum,
1000,
TimeUnit.HOURS,
queue,
new ThreadPoolExecutor.CallerRunsPolicy());
BlockingQueue=newlinkedblockingqueue(queueSize);
ThreadPoolExecutor tpe=新的ThreadPoolExecutor(
threadNum,
threadNum,
1000,
时间单位:小时,
队列
新的ThreadPoolExecutor.CallerRunPolicy());
现在,当您从DB读取的代码发布到此服务时,它将在队列已满时阻塞(调用线程用于运行任务,因此阻塞) 尝试分析内存,看看是什么在消耗它。
HashMap
s应该得到GC'd,但前提是以后没有任何东西保留对它们的引用。如果HashMap没有被共享,它怎么能保留引用?我不知道你的代码。例如,可以使用任务的结果将引用传递到其他地方。但它可能是完全不同的,内存配置文件会告诉您程序在内存中填充了什么。20000个可运行项x 2000个映射项x 5个子映射=200000000个对象…Igor,200000000 bu每个踏板只处理2000*5个,在它找到hashmap后应该清理,但为什么?每个任务处理200*5个项目。5个线程同时运行。任务完成后,它的所有数据都将被清除。不是吗?这取决于排队等待执行的任务数量。此外,您可能还需要处理未来的对象。如果任务正在等待,则HM为空。它只有在运行“名称问题”时才被填充,即使ArrayBlockingQueue
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(queueSize);
ThreadPoolExecutor tpe = new ThreadPoolExecutor(
threadNum,
threadNum,
1000,
TimeUnit.HOURS,
queue,
new ThreadPoolExecutor.CallerRunsPolicy());