在 Java 中实时监控总线程堆栈大小
Live monitoring of total thread stack size in Java
据我所知,在 Java 中,线程堆栈大小取决于 JVM 和 OS 体系结构,默认情况下(除非设置了 -Xss)在 256k 和 1m 之间变化。有没有一种方法或工具允许查看运行时所有当前 运行 线程消耗的总堆栈大小,就像我可以使用 JDK 包中的 JVisualVM 查看堆大小或元空间大小一样?我知道这个值可以计算为线程堆栈大小 * 当前 运行 线程的数量但是在运行时监视这个值会很好。
你可以试试JProfiler。
如果您想在自己尝试之前看到它的实际效果,请查看 this
此外,您关于线程堆栈大小在 256k 和 1m 之间变化的想法是绝对正确的。
在JDK8中,HotSpot安装带有一个名为Native Memory Tracking的功能(默认:禁用)。要启用它,请使用:
-XX:NativeMemoryTracking=[off|detail|summary]
启用 NMT 后,您可以使用以下方法检查线程或线程堆栈占用的内存:
jcmd <pid> VM.native_memory [summary | detail | baseline | summary.diff | detail.diff | shutdown] [scale= KB | MB | GB]
首先,默认堆栈大小是特定于平台的,但这并不意味着特定于平台的默认值“不同”。我的回忆是默认设置(对于任何给定的“体系结构”)已经很长时间没有改变了。 (可能从 Java 5 开始,如果不是更早的话。但不要引用我的话!)
您也无法使用默认值来准确确定实际分配了多少堆栈内存,因为:
- 可以通过
-xss
命令行选项覆盖平台默认设置。如您所述。
- 创建每个
Thread
对象时可以指定非默认堆栈大小。
- 只有在
Thread
启动时才真正分配堆栈。 (并且它在 Thread
终止时被释放。)
因此,如果您想测量实际分配的堆栈内存,您需要迭代所有线程并找到它们的实际堆栈大小。第一个应该相对直接:遍历 ThreadGroup
树。 (我不认为你可以保证你会在遍历中看到所有线程,但这应该无关紧要。)第二个更难,因为私有 Thread.stackSize
字段没有 getter ...并且该字段仅记录应用程序提供给 Thread
构造函数的参数。
但是,由于典型的应用程序仅使用默认堆栈大小,因此计算线程数并乘以默认大小通常可以很好地估计线程堆栈的总使用量。
也可以通过使用 Andrei Pangin 的 stackmem 脚本方法检查进程内存段来推断 JVM 分配的堆栈内存。 (请注意,这是 Linux 特定的,并且它依赖于 JVM 从 OS 为线程堆栈请求单独的内存段。)
另一方面,如果您想知道当前使用(不仅仅是分配)的堆栈数量space,那将很难从内部获得应用程序。如果您想通过代理获取它,我怀疑您需要先冻结 JVM。这对于常规监控来说是不可接受的。
但归根结底,获取信息将(至少!)非常重要,而且 (IMO) 可能不值得付出努力。您可以通过查看线程数和乘法来准确测量分配的堆栈 space 来做1 很多事情...
... it would be great to monitor this value in runtime.
不相信:-)
1 - 想知道使用了多少堆栈内存有两个可能的原因:需要优化或好奇心。在前一种情况下,知道使用了多少堆栈内存并不能直接告诉您应该使用多少。要确定后者,您实际上需要确定是否有太多线程,或者这些线程的堆栈是否需要与当前一样大。 “原则上”或因为有人说这是“最佳实践”而减少堆栈内存使用量可能会给您带来麻烦。
据我所知,在 Java 中,线程堆栈大小取决于 JVM 和 OS 体系结构,默认情况下(除非设置了 -Xss)在 256k 和 1m 之间变化。有没有一种方法或工具允许查看运行时所有当前 运行 线程消耗的总堆栈大小,就像我可以使用 JDK 包中的 JVisualVM 查看堆大小或元空间大小一样?我知道这个值可以计算为线程堆栈大小 * 当前 运行 线程的数量但是在运行时监视这个值会很好。
你可以试试JProfiler。 如果您想在自己尝试之前看到它的实际效果,请查看 this
此外,您关于线程堆栈大小在 256k 和 1m 之间变化的想法是绝对正确的。
在JDK8中,HotSpot安装带有一个名为Native Memory Tracking的功能(默认:禁用)。要启用它,请使用:
-XX:NativeMemoryTracking=[off|detail|summary]
启用 NMT 后,您可以使用以下方法检查线程或线程堆栈占用的内存:
jcmd <pid> VM.native_memory [summary | detail | baseline | summary.diff | detail.diff | shutdown] [scale= KB | MB | GB]
首先,默认堆栈大小是特定于平台的,但这并不意味着特定于平台的默认值“不同”。我的回忆是默认设置(对于任何给定的“体系结构”)已经很长时间没有改变了。 (可能从 Java 5 开始,如果不是更早的话。但不要引用我的话!)
您也无法使用默认值来准确确定实际分配了多少堆栈内存,因为:
- 可以通过
-xss
命令行选项覆盖平台默认设置。如您所述。 - 创建每个
Thread
对象时可以指定非默认堆栈大小。 - 只有在
Thread
启动时才真正分配堆栈。 (并且它在Thread
终止时被释放。)
因此,如果您想测量实际分配的堆栈内存,您需要迭代所有线程并找到它们的实际堆栈大小。第一个应该相对直接:遍历 ThreadGroup
树。 (我不认为你可以保证你会在遍历中看到所有线程,但这应该无关紧要。)第二个更难,因为私有 Thread.stackSize
字段没有 getter ...并且该字段仅记录应用程序提供给 Thread
构造函数的参数。
但是,由于典型的应用程序仅使用默认堆栈大小,因此计算线程数并乘以默认大小通常可以很好地估计线程堆栈的总使用量。
也可以通过使用 Andrei Pangin 的 stackmem 脚本方法检查进程内存段来推断 JVM 分配的堆栈内存。 (请注意,这是 Linux 特定的,并且它依赖于 JVM 从 OS 为线程堆栈请求单独的内存段。)
另一方面,如果您想知道当前使用(不仅仅是分配)的堆栈数量space,那将很难从内部获得应用程序。如果您想通过代理获取它,我怀疑您需要先冻结 JVM。这对于常规监控来说是不可接受的。
但归根结底,获取信息将(至少!)非常重要,而且 (IMO) 可能不值得付出努力。您可以通过查看线程数和乘法来准确测量分配的堆栈 space 来做1 很多事情...
... it would be great to monitor this value in runtime.
不相信:-)
1 - 想知道使用了多少堆栈内存有两个可能的原因:需要优化或好奇心。在前一种情况下,知道使用了多少堆栈内存并不能直接告诉您应该使用多少。要确定后者,您实际上需要确定是否有太多线程,或者这些线程的堆栈是否需要与当前一样大。 “原则上”或因为有人说这是“最佳实践”而减少堆栈内存使用量可能会给您带来麻烦。