我使用的ThreadPoolExecutor
有5个活动线程,任务数量是20,000个
1}}几乎立即填充了队列(pool.execute(new WorkingThreadTask())
)。
每个Runnable
都有一个WorkingThreadTask
:
HashMap
每张地图最多可包含2000个项目,每个子地图有5个项目。还有一个共享Map<Integer, HashMap<Integer, String>> themap ;
。
当进程正在运行时,我的内存不足。我正在使用:BlockingQueue
我该如何处理这个问题?我不认为我在hashmap中有泄漏......当线程完成时,hashmap被清理干净了吗?
更新
运行探查器并检查内存后,最大的打击是:
(32bit -Xms1024m -Xmx1024m)
我不知道它被叫或使用的地方。
byte[] 2,516,024 hits, 918 MB
答案 0 :(得分:1)
我不确定内部地图,但我怀疑问题是你正在创建大量填充内存的任务。您应该使用有界任务队列并限制作业生成器。
在这里查看我的答案:Process Large File for HTTP Calls in Java
总结一下,您应该创建自己的有界队列,然后使用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);
}
}
});
修改强>
我不认为我在hashmap中有韭菜...当线程完成时,hashmap被清理好了吗?
在任务完成时,您可能会考虑在工作clear()
和其他集合上积极调用HashMap
。虽然它们最终应该由GC获得,但如果你的内存有限,给GC提供一些帮助可能会解决你的问题。
如果这不起作用,可以使用分析器来帮助您识别内存的保存位置。
修改强>
在查看探查器输出后,byte[]
很有趣。通常,这表示某种序列化或其他IO。您可能还在数据库中存储blob。然而,oracle.jdbc.ttc7.TTCItem
非常非常有趣。这向我表明你没有在某处关闭数据库连接。确保使用正确的try / finally块来关闭连接。
答案 1 :(得分:0)
HashMap在内存使用方面带来了相当多的开销.....每个条目最少占用36个字节,加上键/值本身的大小 - 每个至少32个字节(我认为这是关于32位太阳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在执行程序的队列中可用时,您可以创建并添加另一个任务。
答案 2 :(得分:0)
您可以在处理之前不添加如此多的任务来提高效率。尝试检查队列,只有在少于1000个条目时才添加它。
您还可以提高数据结构的效率。带有整数键的Map通常可以简化为某种数组。
最后,目前1 GB并不是那么多。我的手机有2 GB。如果您要处理大量数据,我建议使用32-64 GB内存和64位JVM。
答案 3 :(得分:0)
从大byte[]
开始,我怀疑与IO有关的问题(除非您正在处理视频/音频或其他事情)。
要看的事情:
更新:好的,所以您正在使用游标从数据库中读取数据。现在你需要确保光标的读数只在你完成的东西(也称为“传播负载”)时进行。为此,请使用如下的线程池:
BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(queueSize);
ThreadPoolExecutor tpe = new ThreadPoolExecutor(
threadNum,
threadNum,
1000,
TimeUnit.HOURS,
queue,
new ThreadPoolExecutor.CallerRunsPolicy());
现在,当您从从DB读取的代码发布到此服务时,它将在队列已满时阻塞(调用线程用于运行任务,因此阻止)。