0

我正在使用一个ThreadPoolExecutor有 5 个活动线程的任务,任务数量是 20,000 个。
队列几乎立即pool.execute(new WorkingThreadTask())被任务实例填满( ) 。Runnable

每个WorkingThreadTask都有一个HashMap

Map<Integer, HashMap<Integer, String>> themap ;

每个地图最多可以有 2000 个项目,每个子地图有 5 个项目。还有一个共享的BlockingQueue

当进程运行时,我的内存不足。我正在运行:(32bit -Xms1024m -Xmx1024m)

我该如何处理这个问题?我认为我在 hashmap 中没有泄漏......当线程完成时,hashmap 被清理了对吗?

更新:

运行分析器并检查内存后,最大的打击是:

byte[] 2,516,024 hits, 918 MB  

我不知道它是从哪里调用或使用的。

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
4

4 回答 4

1

我不确定内部地图,但我怀疑问题在于您正在创建大量填充内存的任务。您应该使用有界任务队列并限制作业生产者。

看看我的答案:Process Large File for HTTP Calls in Java

总结一下,你应该创建自己的有界队列,然后使用 aRejectedExecutionHandler阻塞生产者,直到队列中有空间。就像是:

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 块来关闭您的连接。

于 2013-09-08T15:39:09.443 回答
0

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 在 executor 的队列中可用时,您可以创建和添加另一个任务。

于 2013-09-08T15:47:50.737 回答
0

您可以通过在正在处理的内容之前不添加太多任务来提高效率。尝试检查队列,仅在条目少于 1000 个时才添加。

您还可以使数据结构更高效。带有 Integer 键的 Map 通常可以简化为某种数组。

最后,这些天 1 GB 并没有那么多。我的手机有2GB。如果您要处理大量数据,我建议您使用 32-64 GB 内存和 64 位 JVM 的机器。

于 2013-09-08T16:05:21.660 回答
0

从大byte[]的s,我怀疑与IO相关的问题(除非你正在处理视频/音频或其他东西)。

要看的东西:

  • DB:你想一次阅读大量的东西吗?您可以例如使用光标不这样做
  • 文件/网络:您是否尝试一次从文件/网络中读取大量内容?您应该将负载“传播”到正在读取的任何内容并调节读取速率。

更新:好的,所以您正在使用游标从数据库中读取。现在您需要确保从光标读取的内容仅在您完成内容时进行(也称为“传播负载”)。为此,请使用这样的线程池:

 BlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(queueSize);
 ThreadPoolExecutor tpe = new ThreadPoolExecutor(
                    threadNum,
                    threadNum,
                    1000,
                    TimeUnit.HOURS,
                    queue,
                    new ThreadPoolExecutor.CallerRunsPolicy());

现在,当您从从数据库读取的代码中发布到此服务时,它将在队列已满时阻塞(调用线程用于运行任务并因此阻塞)。

于 2013-09-09T07:40:46.200 回答