1. 概述
线程在 Java 中是并发的基本单位。在大多数情况下,当创建多个线程来并行执行任务时,应用程序的吞吐量会随之增加。
然而,吞吐量总会存在一个饱和点。毕竟,应用程序的吞吐量取决于 CPU 和内存资源。当线程数量达到一定程度后,增加线程数反而会导致内存占用过高、频繁线程上下文切换等。
因此,对 Java 应用程序中的高内存占用率问题进行故障排除的一个很好的方式是监视线程数。在本文中,我们将了解一些可以检查 Java 进程创建的线程数的方法。
2. 图形化 Java 监控工具
查看 Java 中线程数的最简单方法是使用图形工具,如Java VisualVM。除了应用程序线程之外,Java VisualVM 还列出了 GC 和应用程序使用的任何其它线程,如 JMX 线程。
此外,它还显示诸如线程状态及其持续时间之类的统计信息:
监控线程数是 Java VisualVM 最基本的功能。一般来说,图形工具功能非常丰富,甚至可以实时监控应用程序。例如,Java VisualVM 允许我们对 CPU 堆栈进行跟踪采样,从而找到可能导致 CPU 瓶颈的代码类或方法。
在Windows系统中,Java VisualVM 会随着 JDK 一起安装到 Windows 机器上。 对于部署在 Linux 上的应用程序,我们需要远程连接到应用程序所在的机器,而这需要JMX VM 参数。
因此,如果应用程序已经在不支持这些 JMX VM参数的系统中运行,则此类工具将无法工作。在后面的部分中,我们将看到如何使用命令行工具获取线程数。
3. Java API
在某些场景中,我们可能希望在应用代码内感知线程数。例如,在监控仪表板上显示或将信息记录在日志中。
在这种场景下,我们需要依靠 Java API 来获取线程数。幸运的是,Thread 类中有一个activeCount() API :
public class FindNumberofThreads { public static void main(String[] args) { System.out.println("Number of threads " + Thread.activeCount()); } }
它的输出如下:
Number of threads 2
值得注意的是,我们在 Java VisualVM 中看到的线程数,比用上述 API 中看到的线程数要多。这是因为activeCount()只返回同一线程组中的线程数。 在 Java 中,所有线程会被分组以便于管理。
在这个例子中,我们只能看到 父线程组,即主线程 main:
public static void main(String[] args) { System.out.println("Current Thread Group - " + Thread.currentThread().getThreadGroup().getName()); }
Current Thread Group - main
如果 Java 应用程序中有许多线程组, activeCount()将不会给出正确的线程数。例如,它不会返回 GC 线程的数量。
在这种情况下,我们可以使用 JMX API:
public static void main(String[] args) { System.out.println("Total Number of threads " + ManagementFactory.getThreadMXBean().getThreadCount()); }
该 API 返回来自所有线程组、GC、JMX 等的线程总数:
Total Number of threads 6
实际上,像 Java VisualVM 这样的 JMX 图形工具正是使用 JMX API 对其数据进行计算统计的。
4. 命令行工具
我们前面讨论了 Java VisualVM,这是一种用于实时分析应用程序中的线程的图形工具。尽管它是线程实时可视化的绝佳工具,但它会对应用程序的性能产生影响,因此并不推荐用于生产环境。
此外,正如我们所讨论的,在 Linux 中要使用 Java VisualVM 的话需要远程连接。事实上,在某些情况下,它需要额外的配置。例如,在 Docker 或 Kubernetes 中运行的应用程序需要额外的服务和端口配置。
在这种情况下,我们必须依靠宿主环境中的命令行工具来获取线程数。
幸运的是,Java 提供了一些命令来进行线程转储。我们可以将线程转储为文本文件后进行分析,或使用线程转储分析工具来检查线程数量及其状态。
阿里巴巴 Arthas是另一个很棒的命令行工具,不需要远程连接或任何特殊配置。
此外,我们还可以从一些 Linux 命令中获取有关线程的信息。例如,我们可以使用top命令来显示任何 Java 应用程序的所有线程:
top -H -p 1
这里, -H 是一个命令行选项,用于显示 Java 进程中的每个线程。如果没有这个标志, top命令将显示进程中所有线程的组合统计信息。而 -p 选项的作用是输出特定进程ID的应用程序线程:
如上所示,它显示了线程 id(PID) 和每个线程的 CPU 、内存利用率。与 Java VisualVM 类似,top 命令将列出所有线程,包括 GC、JMX 或任何其他子进程。
要查找进程 ID,我们可以使用 ps 命令:
ps -ef | grep java
事实上,我们也可以使用 ps 命令来列出线程:
ps -e -T | grep 1
该 -T 选项的作用是输出某一进程下的所有线程信息:
在这里,第一列是进程ID,第二列显示的是线程ID。
5.总结
在本文中,我们看到有多种方法可以找到 Java 应用程序中的线程数。在大多数情况下,首选方法是使用top或 ps 命令这样的命令行选项。
然而,在某些情况下,我们还是需要像 Java VisualVM 这样的图形工具。
全部评论
(1) 回帖