为什么在线程池中执行大量 Runnable 时内存占用增加?
Why memory away increasing when execute huge number Runnable in thread pool?
我的同事给了我这样的代码片段:
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 个 Runnable 实例。当我 运行 这段代码时,我可以看到 JVisualVM 中的堆在增加,但是 100000 个 Runnable 实例的内存大小几乎没有变化。我的 JVM 选择是 -Xms20m -Xmx20m -XX:MaxTenuringThreshold=1。 Java 版本为 1.8.0_151,在 macOS High Sierra 版本 10.13.6 上。线程池中的所有线程都在休眠,那么为什么堆会增加呢?创建了什么对象?
JVisualVM 视觉 GC:
JVisualVM 采样器:
您正在创建 10.000 个 Runnable 实例,但由于您的线程池,一次只能执行 3 个线程。它创建了一个大型的 Runnable 实例队列,这些实例还不能被执行。
尝试增加线程池大小,或减少必须执行的线程数。
注意:大部分垃圾是由 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);
}
}
通过将任务分解为可用数量的处理器(它只为您拥有的每个处理器创建两个实际任务),无论任务数量如何,它都使用相同数量的内存
我的同事给了我这样的代码片段:
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 个 Runnable 实例。当我 运行 这段代码时,我可以看到 JVisualVM 中的堆在增加,但是 100000 个 Runnable 实例的内存大小几乎没有变化。我的 JVM 选择是 -Xms20m -Xmx20m -XX:MaxTenuringThreshold=1。 Java 版本为 1.8.0_151,在 macOS High Sierra 版本 10.13.6 上。线程池中的所有线程都在休眠,那么为什么堆会增加呢?创建了什么对象?
JVisualVM 视觉 GC:
JVisualVM 采样器:
您正在创建 10.000 个 Runnable 实例,但由于您的线程池,一次只能执行 3 个线程。它创建了一个大型的 Runnable 实例队列,这些实例还不能被执行。
尝试增加线程池大小,或减少必须执行的线程数。
注意:大部分垃圾是由 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);
}
}
通过将任务分解为可用数量的处理器(它只为您拥有的每个处理器创建两个实际任务),无论任务数量如何,它都使用相同数量的内存