1. 概要

スレッドは、Javaの同時実行性の基本単位です。 ほとんどの場合、タスクを並行して実行するために複数のスレッドが作成されると、アプリケーションのスループットが向上します。

ただし、常に飽和点があります。 結局のところ、アプリケーションのスループットはCPUとメモリリソースに依存します。 一定の制限を超えると、スレッドの数を増やすと、メモリが多くなったり、スレッドのコンテキストが切り替わったりする可能性があります。 

したがって、Javaアプリケーションの高メモリの問題をトラブルシューティングするための良い出発点は、スレッドの数を監視することです。 このチュートリアルでは、Javaプロセスによって作成されたスレッドの数を確認するいくつかの方法を見ていきます。

2. グラフィカルJava監視ツール

Javaのスレッド数を確認する最も簡単な方法は、 JavaVisualVMなどのグラフィカルツールを使用することです。 アプリケーションスレッドとは別に、Java VisualVMは、GCまたはJMXスレッドなどのアプリケーションで使用されるその他のスレッドも一覧表示します。

さらに、スレッドの状態などの統計情報とその期間も表示されます。

スレッド数の監視は、JavaVisualVMの最も基本的な機能です。 一般的に、グラフィカルツールはより高度であり、アプリケーションのライブモニタリングを可能にします。 たとえば、Java VisualVMを使用すると、CPUスタックトレースをサンプリングして、CPUのボトルネックを引き起こす可能性のあるクラスまたはメソッドを見つけることができます。

Java VisualVMは、WindowsマシンへのJDKインストールとともに配布されます。  Linuxにデプロイされたアプリケーションの場合、アプリケーションにリモートで接続する必要があります。 これには、JMXVM引数が必要です。

したがって、これらのパラメータなしでアプリケーションがすでに実行されている場合、このようなツールは機能しません。 後のセクションでは、コマンドラインツールを使用してスレッド数を取得する方法を説明します。

3. Java API

場合によっては、アプリケーション自体のスレッド数を調べたいことがあります。 たとえば、監視ダッシュボードに表示したり、ログに公開したりします。

このような場合、スレッド数を取得するためにJavaAPIに依存します。 ありがたいことに、スレッドクラスには 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のスレッド数を確認すると、同じアプリケーションのスレッド数が増えます。 これは、activeCount()が同じスレッドの数のみを返すためです。 ThreadGroup。 Javaは、管理を容易にするためにすべてのスレッドをグループに分割します。

この例では、親 ThreadGroup、、つまり main:のみがあります。

public static void main(String[] args) {
    System.out.println("Current Thread Group - " + Thread.currentThread().getThreadGroup().getName());
}
Current Thread Group - main

Javaアプリケーションに多数のスレッドグループがある場合、 activeCount()は正しい出力を提供しません。 たとえば、GCスレッドの数は返されません。

このようなシナリオでは、JMXAPIを使用できます。

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グラフィカルツールは、データに同じAPIを使用します。

4. コマンドラインツール

以前、アプリケーションのライブスレッドを分析するためのグラフィカルツールであるJavaVisualVMについて説明しました。 スレッドをライブで視覚化するための優れたツールですが、アプリケーションのパフォーマンスにはわずかな影響しかありません。 したがって、本番環境にはお勧めしません

さらに、前述したように、JavaVisualVMにはLinuxでのリモート接続が必要です。 実際、場合によっては、追加の構成が必要になります。 たとえば、DockerまたはKubernetes内で実行されているアプリケーションには、追加のサービスとポートの構成が必要になります。

このような場合、スレッド数を取得するには、ホスト環境のコマンドラインツールに依存する必要があります。

幸いなことに、Javaはスレッドダンプを取得するためのいくつかのコマンドを提供します。 スレッドダンプをテキストファイルとして分析するか、スレッドダンプアナライザツールを使用してスレッドの数とその状態を確認できます。

Alibaba Arthas は、リモート接続や特別な構成を必要としないもう1つの優れたコマンドラインツールです。

さらに、いくつかのLinuxコマンドからスレッドに関する情報を取得することもできます。 たとえば、topコマンドを使用して、任意のJavaアプリケーションのすべてのスレッドを表示できます

top -H -p 1

ここで、 -H は、Javaプロセスのすべてのスレッドを表示するためのコマンドラインオプションです。 このフラグがない場合、 top コマンドは、プロセス内のすべてのスレッドの結合された統計を表示します。 -p オプションは、ターゲットアプリケーションのプロセスIDで出力をフィルタリングします。

top - 15:59:44 up 7 days, 19:23,  0 users,  load average: 0.52, 0.41, 0.36
Threads:  37 total,   0 running,  37 sleeping,   0 stopped,   0 zombie
%Cpu(s):  3.2 us,  2.2 sy,  0.0 ni, 93.4 id,  0.8 wa,  0.0 hi,  0.3 si,  0.0 st
MiB Mem :   1989.2 total,    110.2 free,   1183.1 used,    695.8 buff/cache
MiB Swap:   1024.0 total,    993.0 free,     31.0 used.    838.8 avail Mem

  PID USER      PR  NI    VIRT    RES    SHR S  %CPU  %MEM     TIME+ COMMAND
   1 flink     20   0 2612160 304084  29784 S   0.0  14.9   0:00.07 java
  275 flink     20   0 2612160 304084  29784 S   0.0  14.9   0:02.87 java
  276 flink     20   0 2612160 304084  29784 S   0.0  14.9   0:00.37 VM Thread
  277 flink     20   0 2612160 304084  29784 S   0.0  14.9   0:00.00 Reference Handl
  278 flink     20   0 2612160 304084  29784 S   0.0  14.9   0:00.00 Finalizer
  279 flink     20   0 2612160 304084  29784 S   0.0  14.9   0:00.00 Signal Dispatch

上記のように、スレッドID、つまりPIDとスレッドごとのCPUおよびメモリ使用率が表示されます。 Java VisualVMと同様に、topコマンドは、GC、JMX、またはその他のサブプロセスを含むすべてのスレッドを一覧表示します。

上記のコマンドで引数として使用したプロセスIDを見つけるには、psコマンドを使用できます。

ps -ef | grep java

実際のところ、 ps コマンドを使用して、スレッドを一覧表示することもできます。

ps -e -T | grep 1

-T オプションは、 ps コマンドに、アプリケーションによって開始されたすべてのスレッドを一覧表示するように指示します。

1     1 ?        00:00:00 java
1   275 ?        00:00:02 java
1   276 ?        00:00:00 VM Thread
1   277 ?        00:00:00 Reference Handl
1   278 ?        00:00:00 Finalizer
1   279 ?        00:00:00 Signal Dispatch
1   280 ?        00:00:03 C2 CompilerThre
1   281 ?        00:00:01 C1 CompilerThre

ここで、最初の列はPIDであり、2番目の列は各スレッドのLinuxスレッドIDを示しています。

5. 結論

この記事では、Javaアプリケーションのスレッド数を見つける方法がいくつかあることを確認しました。 ほとんどの場合、topコマンドやpsコマンドなどのコマンドラインオプションを使用するのが一般的な方法です

ただし、状況によっては、JavaVisualVMなどのグラフィカルツールも必要になる場合があります。 すべてのコードサンプルは、GitHubから入手できます。