Java线程内存计算



是否可以计算每个线程的内存消耗?假设我把任务分成4个线程,那么我想知道每个线程消耗了多少内存?我需要它从我的线程中了解平均和峰值内存使用情况。

正如其他人所指出的,大多数对象都生活在堆中。堆内存在线程之间共享。因此,无法确定哪些线程对堆的大小负责。

但是线程确实有自己的内存块:堆栈。

堆栈大小

正如我从甲骨文的Ron Pressler在2020年的演讲中回忆的那样…

常规螺纹

为每个线程的堆栈分配一定数量的内存。由于当前基于OpenJDK的Java实现中的线程被一对一地映射到主机OS的线程,因此堆栈大小被任意设置为类似于meg的大小。如果需要,可以分配更多的内存,但不会减少。

虚拟线程

该图随着Project Loom中提出的虚拟线(纤维(而变厚。

Project Loom为Java并发设施添加了新功能。作为其中的一部分,虚拟线程被映射到主机操作系统线程(也称为平台/内核线程(。JVM将管理这些虚拟线程,而不是操作系统,在虚拟线程的代码块时"停放"虚拟线程,以便通过分配给"真实"平台/内核线程的执行时间为另一个虚拟线程留出运行时间。"真实"平台/内核线程在CPU内核上实际完成工作的调度由主机操作系统控制,无论是否使用Project Loom(至少在基于OpenJDK的Java实现中(。

➥作为虚拟线程JVM管理的一部分,每个虚拟线程的堆栈一开始会小得多。每个堆栈都会根据需要增长和收缩(!(。

由于这种对CPU和内存的有效使用,虚拟线程被显著地"占用";更便宜";。所以我们可以运行更多的它们。甚至在普通硬件上也可以有数百万个虚拟线程。

总结我的评论,线程使用共享内存。因此,除了保留的堆栈内存(在jvm启动期间设置(之外,没有线程拥有自己的任何数据。

由于您关注的是线程在运行jvm时消耗的确切堆大小,因此您可以简单地使用像visualvm这样的内存探查器来查看类&线程创建的对象,并假定消耗大小。

也可以使用ThreadLocal变量来定义属于特定线程的对象。这也可以帮助您在每个线程的基础上获得确切的内存消耗。

您还可以查看ThreadMXBean,但是它在最新的jvm中不再可用。

您可以使用Native Memory Tracking来获取有关线程的一般信息,但我不知道如何单独获取有关每个线程的信息。

启动应用程序时,将-XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics添加到vm选项中:

java -XX:+UnlockDiagnosticVMOptions -XX:NativeMemoryTracking=summary -XX:+PrintNMTStatistics -jar myjar.jar

现在,如果你运行:

$jps -l

在您的机器上查找java进程,然后查找

$jcmd <PID> VM.native_memory

你会看到如下内容:

Native Memory Tracking:
Total: reserved=2344374KB +4399KB, committed=567362KB +19659KB
...
- Thread (reserved=87894KB, committed=19126KB +756KB)
(thread #159)
(stack: reserved=87132KB, committed=18364KB +756KB)
(malloc=577KB #956)
(arena=184KB #316)
...

有了这些信息,您可以执行19126KB/159=120KB来获得每个线程的平均提交内存大小。问题是,这不会单独告诉你每个线程,有些线程会更大,另一些线程会更小。

您也可以使用:

$jcmd <PID> VM.native_memory baseline
$jcmd <PID> VM.native_memory summary.diff

这将告诉你基线时刻和高峰流量期间可以进行的跑步之间的差异。

有一点需要注意:启用NMT会带来一些额外的开销。您可能需要权衡在启用NMT的情况下在PROD中运行应用程序的风险

最新更新