0

我有一个带有 run 方法的类,并且该类的 main 方法中的计时器正在使用以下代码调用该类:

Timer timer = new Timer();
timer.scheduleAtFixedRate(new LegacySmsSender(), 0, 2*1000);

在运行方法中,我声明了一个 ThreadExecutorPool:

ThreadPoolExecutor packetProcessorThreadPool =
        new ThreadPoolExecutor(4,
                                4,
                                Long.MAX_VALUE,
                                TimeUnit.DAYS,
                                new LinkedBlockingQueue<Runnable>(),
                                new MyThreadFactory("packetProcessorThreadPool")
                                );

我创建了 4 个 PacketProcessor,new PacketProcessor()并在列表中执行 packetProcessorThreadPool.submit并保存它们的Future返回。然后我等待所有这些循环完成:

for(Future<?> f:packetProcessorList)
{
   System.out.println("before f.get() "+new Date());
   f.get();
}

在这些 PacketProcessor 类的 run 方法中,它们声明了一个 ThreadPoolExecutor 并创建和提交数量在 1-5000 之间的线程(通常创建 6-7 个线程),PacketProcessor 中的 threadPoolExecutor 代码是这样的:

ThreadPoolExecutor commonThreadPool =
        new ThreadPoolExecutor(
                        20,
                        20,
                        Long.MAX_VALUE,
                        TimeUnit.DAYS,
                        new LinkedBlockingQueue<Runnable>(),
                        new MyThreadFactory("commonThreadPool"));

最后,我运行了大约 20 分钟,我检查了 VisualVM,我的内存使用量和活动线程数一直在上升。问题是什么 ?

注意:请随时向我询问更多信息或问题

这是带有一些信息的屏幕截图:

在此处输入图像描述

编辑1:

我进行了 270 MB 的堆转储。我找到了 160mb 的 char[] 。我发现像 1000-2000 千个查询字符串。我使用 StringBuilder 构建查询字符串。为什么他们没有获得 GCed ?

4

3 回答 3

2

执行器应该在应用程序启动时声明一次并重用。否则,您还不如按需创建单独的线程。如果您在应用程序执行期间不断创建新的执行程序,它们的线程将继续运行,因此线程数将不断增加。

因此,只需使用 DI 框架创建执行器并将它们注入到您的代码中。或者,如果它是一个小项目,请将它们放在一个静态字段中。

于 2012-09-18T08:17:01.210 回答
1

在运行方法中,我声明了一个 ThreadExecutorPool

如果您在每 2 秒执行一次的 run 方法中声明 ThreadExecutorPool,您将在几分钟内获得许多线程。

于 2012-09-18T08:20:21.770 回答
0

The problem is that you've told all of the ExecutorServices that you create to make threads and then keep them alive forever. This is very plainly obvious in the code:

new ThreadPoolExecutor(
                        20,
                        20,
                        Long.MAX_VALUE,
                        TimeUnit.DAYS,
                        new LinkedBlockingQueue<Runnable>(),
                        new MyThreadFactory("commonThreadPool"));

The third and fourth arguments basically say "keep idle threads alive for (practically) infinity days".

You can solve this in a couple ways:

  1. Call ExecutorService.shutdown on the executor instances when you are done passing them tasks.
  2. The best solution, use a single ScheduledExecutorService and a more reasonable task distribution logic to limit the number of threads and drive up performance by preventing thread context switching.
于 2012-09-18T08:30:27.120 回答