为什么在线程池中执行大量Runnable时内存消耗增加?

时间:2018-08-23 18:25:54

标签: java multithreading jvm threadpool

我的同事给我这样的代码片段:

public class Main1 {

        public static void main(String[] args) throws Exception {
            ExecutorService executorService = Executors.newFixedThreadPool(3);

            Runnable runnable = () -> {
                try {
                    // business logic
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(LocalDateTime.now().toString() + ":" + Thread.currentThread().getName());
            };

            for (int i = 0; i < 100000; i++) {
                executorService.execute(runnable);
            }

            System.in.read();

        }

    }

在上面的代码中,创建100000可运行实例。当我运行此代码时,我可以看到JVisualVM中的堆空间增加了,但是100000可运行实例的内存大小几乎没有变化。我的JVM选择是-Xms20m -Xmx20m -XX:MaxTenuringThreshold = 1。在macOS High Sierra版本10.13.6上,Java版本是1.8.0_151。线程池中的所有线程都处于睡眠状态,那么为什么堆距离增加了?创建了什么对象?

JVisualVM Visual GC: enter image description here

JVisualVM采样器: enter image description here

2 个答案:

答案 0 :(得分:2)

注意:大多数垃圾是由VisualVM本身监视JVM所创建的。它使用效率很低的Java序列化。减少垃圾产生的最好方法是缩短轮询间隔。 (或者使用JMC或YourKit这样的配置文件)

创建任务使用内存,添加到工作队列的每个节点都使用内存。一种更快捷,更有效的方法是IntStream

public class Main {
    static void doWork(int task) {
        try {
            System.out.println("starting " + task);
            // business logic
            Thread.sleep(10000);
            System.out.println("... finished " + task);
        } catch (InterruptedException e) {
            throw new AssertionError(e);
        }
    }

    public static void main(String[] args) {
        IntStream.range(0, 100_000)
                .parallel()
                .forEach(Main::doWork);
    }
}

通过将任务分解为可用数量的处理器,这将使用相同数量的内存,而不考虑任务的数量(它为您拥有的每个处理器仅创建两个实际任务)

答案 1 :(得分:1)

您正在创建10.000个可运行实例,而由于您的线程池,一次只能执行3个线程。它会创建大量无法运行的Runnable实例。

尝试增加线程池大小,或减少必须执行的线程数。