1
1
package jvm_alloc_rate_meter ;
2
2
3
+ import com .sun .management .ThreadMXBean ;
3
4
import java .util .function .LongConsumer ;
4
5
import java .lang .management .GarbageCollectorMXBean ;
5
6
import java .lang .management .ManagementFactory ;
@@ -22,26 +23,48 @@ public MeterThread(LongConsumer callback, int intervalMs) {
22
23
setDaemon (true );
23
24
}
24
25
26
+ // Basically, we have two ways of measuring the allocation rate.
27
+ // First relies on checking heap usage between two points of time and
28
+ // substracting. This is accurate, but works only if the GC didn't trigger
29
+ // in the meantime.
30
+ // Second works by taking allocation stats by each alive thread. This is
31
+ // more reliable, but potentially less accurate (because threads can go
32
+ // away, and we lose their allocation stats).
33
+ // The idea is to use the first approach if GC didn't happen, and the second
34
+ // one if it did.
35
+
25
36
public void run () {
26
- long lastTime = 0 ;
37
+ long lastTime = 0 , lastHeapUsage = - 1 , lastGcCounts = - 1 , lastThreadAllocated = - 1 ;
27
38
try {
28
- long prevUsage = -1 , prevGcCounts = -1 ;
29
39
while (doRun ) {
30
- long usage = usedHeap ();
40
+ long heapUsage = usedHeap ();
31
41
long gcCounts = gcCounts ();
32
- long ts = System .currentTimeMillis ();
42
+ long threadAllocated = allocatedByAllThreads ();
43
+ long time = System .currentTimeMillis ();
44
+
45
+ double multiplier = 1000.0 / (time - lastTime );
46
+ long deltaUsage = heapUsage - lastHeapUsage ;
47
+ long deltaThreadAllocated = threadAllocated - lastThreadAllocated ;
33
48
34
- if ((gcCounts == prevGcCounts ) && (usage >= prevUsage )) {
35
- long deltaTime = ts - lastTime ;
36
- long rate = Math .round ((usage - prevUsage ) * (1000.0 / deltaTime ));
37
- callback .accept (rate );
49
+ if (lastTime != 0 ) {
50
+ if ((gcCounts == lastGcCounts ) && (deltaUsage >= 0 )) {
51
+ long rate = Math .round (deltaUsage * multiplier );
52
+ callback .accept (rate );
53
+ } else if (deltaThreadAllocated >= 0 ) {
54
+ long rate = Math .round (deltaThreadAllocated * multiplier );
55
+ callback .accept (rate );
56
+ } else {
57
+ // Apparently, neither approach did well, just skip this
58
+ // iteration.
59
+ }
38
60
}
39
61
40
62
Thread .sleep (intervalMs );
41
63
42
- prevUsage = usage ;
43
- prevGcCounts = gcCounts ;
44
- lastTime = ts ;
64
+ lastTime = time ;
65
+ lastHeapUsage = heapUsage ;
66
+ lastGcCounts = gcCounts ;
67
+ lastThreadAllocated = threadAllocated ;
45
68
}
46
69
} catch (InterruptedException e ) {
47
70
System .err .println ("MeterThread terminating..." );
@@ -64,4 +87,16 @@ private static long gcCounts() {
64
87
}
65
88
return total ;
66
89
}
90
+
91
+ private static long allocatedByAllThreads () {
92
+ ThreadMXBean bean = (ThreadMXBean )ManagementFactory .getThreadMXBean ();
93
+ long [] ids = bean .getAllThreadIds ();
94
+ long [] allocatedBytes = bean .getThreadAllocatedBytes (ids );
95
+ long result = 0 ;
96
+ // This is not correct because we will lose allocation data from threads
97
+ // that died. Oh well.
98
+ for (long abytes : allocatedBytes )
99
+ result += abytes ;
100
+ return result ;
101
+ }
67
102
}
0 commit comments