我有一个繁忙的旅行应用程序,使用ExecutorService连接到像这样运行的后端服务
ExecutorService taskExecutor = Executors.newFixedThreadPool(10);
List<Future<MyResponse>> futures = new ArrayList<Future<MyResponse>>();
futures.add(taskExecutor.submit(myTask1));
futures.add(taskExecutor.submit(myTask2));
futures.add(taskExecutor.submit(myTask3));
for (int i=0; i<futures.size(); i++) {
future = futures.get(i);
// do something with "future"
}
taskExecutor.shutdownNow();
我发现它每小时都会崩溃,线程数达到峰值,如下图所示。上面的代码有问题吗?
这是崩溃时的摘要日志,它会留下一个名为hs_err_pid92053.log的文件
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Native memory allocation (mmap) failed to map 12288 bytes for committing reserved memory.
# Possible reasons:
# The system is out of physical RAM or swap space
# In 32 bit mode, the process size limit was hit
# Possible solutions:
# Reduce memory load on the system
# Increase physical memory or swap space
# Check if swap backing store is full
# Use 64 bit Java on a 64 bit OS
# Decrease Java heap size (-Xmx/-Xms)
# Decrease number of Java threads
# Decrease Java thread stack sizes (-Xss)
# Set larger code cache with -XX:ReservedCodeCacheSize=
# This output file may be truncated or incomplete.
#
# Out of Memory Error (os_linux.cpp:2627), pid=92053, tid=0x00007f273e0ef700
#
# JRE version: Java(TM) SE Runtime Environment (8.0_121-b13) (build 1.8.0_121-b13)
# Java VM: Java HotSpot(TM) 64-Bit Server VM (25.121-b13 mixed mode linux-amd64 )
# Failed to write core dump. Core dumps have been disabled. To enable core dumping, try "ulimit -c unlimited" before starting Java again
#
--------------- S Y S T E M ---------------
OS:CentOS Linux release 7.4.1708 (Core)
uname:Linux 3.10.0-693.5.2.el7.x86_64 #1 SMP Fri Oct 20 20:32:50 UTC 2017 x86_64
libc:glibc 2.17 NPTL 2.17
rlimit: STACK 8192k, CORE 0k, NPROC 160181, NOFILE 131072, AS infinity
load average:2.02 2.21 2.43
/proc/meminfo:
MemTotal: 41038016 kB
MemFree: 3875544 kB
MemAvailable: 26661144 kB
Buffers: 57724 kB
Cached: 22408624 kB
SwapCached: 1468 kB
Active: 23965100 kB
Inactive: 10993212 kB
Active(anon): 10868052 kB
Inactive(anon): 1649944 kB
Active(file): 13097048 kB
Inactive(file): 9343268 kB
Unevictable: 0 kB
Mlocked: 0 kB
SwapTotal: 8257532 kB
SwapFree: 8253832 kB
Dirty: 9564 kB
Writeback: 0 kB
AnonPages: 12490804 kB
Mapped: 171152 kB
Shmem: 25844 kB
Slab: 1053464 kB
SReclaimable: 780424 kB
SUnreclaim: 273040 kB
KernelStack: 529280 kB
PageTables: 101952 kB
NFS_Unstable: 0 kB
Bounce: 0 kB
WritebackTmp: 0 kB
CommitLimit: 28776540 kB
Committed_AS: 42016176 kB
VmallocTotal: 34359738367 kB
VmallocUsed: 259648 kB
VmallocChunk: 34359341052 kB
HardwareCorrupted: 0 kB
AnonHugePages: 6057984 kB
HugePages_Total: 0
HugePages_Free: 0
HugePages_Rsvd: 0
HugePages_Surp: 0
Hugepagesize: 2048 kB
DirectMap4k: 247744 kB
DirectMap2M: 41695232 kB
答案 0 :(得分:0)
您使用Executors.newFixedThreadPool(10)
获取ExecutorService
来运行某些任务。获得的特定服务将拥有10个用于任务服务的线程,也许还有一个用于管理。您的代码还等待它提交的所有任务完成,然后关闭ExecutorService
。因此,如果此代码确实对您提供的线程数负责,那么主要有两种可能性:
我对后者的评价更有可能。在这种情况下,如果繁忙应用程序上的负载超过主机的容量,那么这可能确实表现为活动线程的累积,因为创建的新线程的速率超过了完成和清理的线程的速率。 / p>
您的特定代码对此没有帮助,因为,至少在示例中,您创建的ExecutorService
线程数多于实际需要的数量。这将放大您观察到的效果,但额外的开销不足以导致它。您显然也会迅速创建并销毁ExecutorService
。这也涉及不必要的开销。
我不建议每次都设置和删除ExecutorService
,而是建议在程序启动时设置合适的ExecutorService
,并在程序的持续时间内重复使用相同的服务。作为直接的好处,这提供了对使用的线程总数进行更严格控制的可能性(如果继续使用固定大小的池)。它还应该减少你的线程管理开销。
如果提交的任务不执行I / O,那么在您的(一个)池中拥有比拥有CPU内核更多的线程可能没有用,但如果它们确实执行了I / O,那么您可能会获得更高的利用率多一点。但不是数千人。
还要考虑您是否只需要扩展主机。例如,通过负载平衡群集提供请求,这样任何单个主机都不会比清除请求更快地接收请求。
答案 1 :(得分:0)
最后,我想出了发生了什么。它是导致问题的JVM最大线程数的限制。
我在Linux中设置了以下内容
ulimit -u unlimited
echo 999999 > /proc/sys/kernel/threads-max
echo 999999 > /proc/sys/kernel/pid_max
echo 1999999 > /proc/sys/vm/max_map_count
然后使用
减小堆栈的大小-Xss512k
答案 2 :(得分:-1)
我经常使用FixedThreadPool并且没有遇到泄漏(尽管考虑到各种JVM版本,操作系统和一般编码方案,总会有惊喜)。我想到的第一个注意事项仍然是:
最重要的是:你的代码是否总是达到'shutdownNow',或者它可能会被困在“做一些未来的事情”(我假设涉及等待)?例如。它是否会被卡在某些I / O或条件等待上,无限期超时?
您是否检查了其他可能的线程来源(其他 人民代码,第三方等。)