是否可以计算每个线程的内存消耗?假设我把任务分成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中运行应用程序的风险