Oracle几天前发布的JDK 6 update 25里添加的一个新功能非常有趣,可以按照线程来跟踪(GC堆)内存的分配量。这个功能在VM核心、解释器、C1编译器、C2编译器以及GC中都有代码支持,并且通过JMX API暴露出来。 
不过新加的这功能不是加在java.开头的包里,而是加在com.sun.management.ThreadMXBean这个接口上,要用的话还得cast一下。 

当然,这么底层的功能不可能没有代价。添加这个功能后,在GC堆上分配空间的slow-path会比以前稍微慢一些。但希望对整体性能的影响并不大吧。 
Fast-path是在TLAB上分配空间的,而TLAB的分配数据是在TLAB refill的时候才批量更新,所以这个功能对fast-path的执行效率基本上没影响,以稍微放宽数据精准性为代价。 

相关链接: 
​​​Bug ID 7003271: Hotspot should track cumulative Java heap bytes allocated on a per-thread basis​​​ 
​​​changeset​​​ 

JMX中,该功能由ThreadMXBean上新增的几个方法提供。详情可见下面例子。 
ThreadMXBean.getThreadAllocatedBytes(long threadId)的用法基本上可以看成跟System.currentTimeMillis()用于计时的用法一样,在两点上记录并且求差即可。 

不知道为什么JVMTI没得到这个更新,或许是因为更新JVMTI spec涉及committee stuff? 

============================================================== 

直接拿一段Groovy脚本来演示吧: 

先看JDK 6 update 24的情况 

Groovysh代码 

 

1. D:\sdk\groovy-1.7.2\bin>groovysh  
2. Groovy Shell (1.7.2, JVM: 1.6.0_24)
3. Type 'help' or '\h' for help.
4. -------------------------------------------------------------------------------
5. groovy:000> import java.lang.management.*
6. ===> [import java.lang.management.*]
7. groovy:000> tb = ManagementFactory.threadMXBean
8. ===> sun.management.ThreadImpl@8b677f
9. groovy:000> tb.class.methods.name.unique().sort()
10. ===> [dumpAllThreads, equals, findDeadlockedThreads, findMonitorDeadlockedThreads, getAllThreadIds, getClass, getCurrentThreadCpuTime, getCurrentThreadUserTime, getDaemonThreadCount, getPeakThreadCount, getThreadCount, getThreadCpuTime, getThreadInfo, getThreadUserTime, getTotalStartedThreadCount, hashCode, isCurrentThreadCpuTimeSupported, isObjectMonitorUsageSupported, isSynchronizerUsageSupported, isThreadContentionMonitoringEnabled, isThreadContentionMonitoringSupported, isThreadCpuTimeEnabled, isThreadCpuTimeSupported, notify, notifyAll, resetPeakThreadCount, setThreadContentionMonitoringEnabled, setThreadCpuTimeEnabled, toString, wait]
11. groovy:000> tb.class.methods.findAll { it.name =~ /Alloc/}.each { println it }; null
12. ===> null

这个时候ThreadMXBean上还没有跟alloc相关的方法。 
 

Groovysh代码 

1. D:\sdk\groovy-1.7.2\bin>groovysh  
2. Groovy Shell (1.7.2, JVM: 1.6.0_25)
3. Type 'help' or '\h' for help.
4. -------------------------------------------------------------------------------
5. groovy:000> import java.lang.management.*
6. ===> [import java.lang.management.*]
7. groovy:000> tb = ManagementFactory.threadMXBean
8. ===> sun.management.ThreadImpl@9b1670
9. groovy:000> tb.class.methods.name.unique().sort()
10. ===> [dumpAllThreads, equals, findDeadlockedThreads, findMonitorDeadlockedThreads, getAllThreadIds, getClass, getCurrentThreadCpuTime, getCurrentThreadUserTime, getDaemonThreadCount, getPeakThreadCount, getThreadAllocatedBytes, getThreadCount, getThreadCpuTime, getThreadInfo, getThreadUserTime, getTotalStartedThreadCount, hashCode, isCurrentThreadCpuTimeSupported, isObjectMonitorUsageSupported, isSynchronizerUsageSupported, isThreadAllocatedMemoryEnabled, isThreadAllocatedMemorySupported, isThreadContentionMonitoringEnabled, isThreadContentionMonitoringSupported, isThreadCpuTimeEnabled, isThreadCpuTimeSupported, notify, notifyAll, resetPeakThreadCount, setThreadAllocatedMemoryEnabled, setThreadContentionMonitoringEnabled, setThreadCpuTimeEnabled, toString, wait]
11. groovy:000> tb.class.methods.findAll { it.name =~ /Alloc/}.each { println it };
12. null
13. public boolean sun.management.ThreadImpl.isThreadAllocatedMemoryEnabled()
14. public boolean sun.management.ThreadImpl.isThreadAllocatedMemorySupported()
15. public long[] sun.management.ThreadImpl.getThreadAllocatedBytes(long[])
16. public long sun.management.ThreadImpl.getThreadAllocatedBytes(long)
17. public void sun.management.ThreadImpl.setThreadAllocatedMemoryEnabled(boolean)
18. ===> null
19. groovy:000> tb.threadAllocatedMemoryEnabled
20. ===> true
21. groovy:000> tid = Thread.currentThread().id
22. ===> 1
23. groovy:000> tb.getThreadAllocatedBytes(tid)
24. ===> 48106672
25. groovy:000> tb.getThreadAllocatedBytes(tid)
26. ===> 48751520
27. groovy:000> tb.getThreadAllocatedBytes(tid)
28. ===> 49384752
29. groovy:000> tb.getThreadAllocatedBytes(tid)
30. ===> 50086240
31. groovy:000> quit