Skip to content

Commit 4adf847

Browse files
author
Lillian Zhang
committed
Report a few more GC stats in GC.stat
1 parent 02f5685 commit 4adf847

File tree

2 files changed

+138
-9
lines changed

2 files changed

+138
-9
lines changed

src/main/java/org/truffleruby/core/GCNodes.java

Lines changed: 91 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
import java.lang.management.GarbageCollectorMXBean;
1313
import java.lang.management.ManagementFactory;
14+
import java.lang.management.MemoryPoolMXBean;
15+
import java.lang.management.MemoryUsage;
1416
import java.time.Duration;
1517

1618
import org.truffleruby.SuppressFBWarnings;
@@ -23,6 +25,7 @@
2325
import org.truffleruby.builtins.Primitive;
2426
import org.truffleruby.builtins.PrimitiveArrayArgumentsNode;
2527
import org.truffleruby.collections.WeakValueCache;
28+
import org.truffleruby.core.array.RubyArray;
2629
import org.truffleruby.language.SafepointManager;
2730
import org.truffleruby.language.control.RaiseException;
2831

@@ -103,10 +106,6 @@ public abstract static class CountNode extends CoreMethodArrayArgumentsNode {
103106
@TruffleBoundary
104107
@Specialization
105108
protected int count() {
106-
return getCollectionCount();
107-
}
108-
109-
public static int getCollectionCount() {
110109
int count = 0;
111110
for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) {
112111
count += bean.getCollectionCount();
@@ -122,15 +121,100 @@ public abstract static class TimeNode extends CoreMethodArrayArgumentsNode {
122121
@TruffleBoundary
123122
@Specialization
124123
protected long time() {
125-
return getCollectionTime();
124+
long time = 0;
125+
for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) {
126+
time += bean.getCollectionTime();
127+
}
128+
return time;
126129
}
127130

128-
public static long getCollectionTime() {
131+
}
132+
133+
@Primitive(name = "gc_stat")
134+
public static abstract class GCStatPrimitiveNode extends PrimitiveArrayArgumentsNode {
135+
136+
@TruffleBoundary
137+
@Specialization
138+
protected RubyArray stat() {
129139
long time = 0;
140+
int count = 0;
141+
int minorCount = 0;
142+
int majorCount = 0;
143+
int unknownCount = 0;
144+
String[] memoryPoolNames = new String[0];
145+
Object[] memoryPools;
146+
147+
// Get GC time and counts from GarbageCollectorMXBean
130148
for (GarbageCollectorMXBean bean : ManagementFactory.getGarbageCollectorMXBeans()) {
149+
// Get MemoryPoolName relevant to GC
150+
if (bean.getMemoryPoolNames().length > memoryPoolNames.length) {
151+
// Since old generation memory pools are a superset of young generation memory pools,
152+
// it suffices to check that we have the longer list of memory pools
153+
memoryPoolNames = bean.getMemoryPoolNames();
154+
}
131155
time += bean.getCollectionTime();
156+
count += bean.getCollectionCount();
157+
switch (bean.getName()) {
158+
case "G1 Young Generation": // during 'jvm' and 'jvm-ce'
159+
case "PS Scavenge": // during 'jvm --vm.XX:+UseParallelGC'
160+
case "young generation scavenger": // during 'native'
161+
minorCount += bean.getCollectionCount();
162+
break;
163+
case "G1 Old Generation": // during 'jvm' and 'jvm-ce'
164+
case "PS MarkSweep": // during 'jvm --vm.XX:+UseParallelGC'
165+
case "complete scavenger": // during 'native'
166+
majorCount += bean.getCollectionCount();
167+
break;
168+
default:
169+
unknownCount += bean.getCollectionCount();
170+
break;
171+
}
132172
}
133-
return time;
173+
174+
// Get memory usage values from relevant memory pools (2-3 / ~8 are relevant)
175+
memoryPools = new Object[memoryPoolNames.length];
176+
for (MemoryPoolMXBean bean : ManagementFactory.getMemoryPoolMXBeans()) {
177+
if (bean.getName().equals(memoryPoolNames[0])) {
178+
memoryPools[0] = beanToArray(bean);
179+
} else if (bean.getName().equals(memoryPoolNames[1])) {
180+
memoryPools[1] = beanToArray(bean);
181+
} else if (memoryPoolNames.length == 3 && bean.getName().equals(memoryPoolNames[2])) {
182+
memoryPools[2] = beanToArray(bean);
183+
}
184+
}
185+
186+
MemoryUsage usage = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
187+
188+
// Use an object array instead because otherwise ArrayHelpers.java line 51 complains
189+
Object[] memoryPoolNamesCast = new Object[memoryPoolNames.length];
190+
for (int i = 0; i < memoryPoolNames.length; i++) {
191+
memoryPoolNamesCast[i] = memoryPoolNames[i];
192+
}
193+
194+
195+
return createArray(
196+
new Object[]{
197+
time,
198+
count,
199+
minorCount,
200+
majorCount,
201+
unknownCount,
202+
usage.getCommitted(),
203+
usage.getUsed(),
204+
createArray(memoryPoolNamesCast),
205+
createArray(memoryPools) });
206+
}
207+
208+
protected RubyArray usageToArray(MemoryUsage usage) {
209+
return createArray(new Object[]{ usage.getCommitted(), usage.getInit(), usage.getMax(), usage.getUsed() });
210+
}
211+
212+
protected RubyArray beanToArray(MemoryPoolMXBean bean) {
213+
return createArray(
214+
new Object[]{
215+
usageToArray(bean.getUsage()),
216+
usageToArray(bean.getPeakUsage()),
217+
usageToArray(bean.getCollectionUsage()) });
134218
}
135219

136220
}

src/main/ruby/truffleruby/core/gc.rb

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,10 +76,55 @@ def garbage_collect
7676
end
7777

7878
def self.stat(option = nil)
79+
time, count, minor_count, major_count, unknown_count, commited, used, memory_pool_names, memory_pool_info = Primitive.gc_stat()
80+
81+
# Initialize stat for statistics that come from memory pools, and populate it with some final stats
7982
stat = {
80-
count: GC.count,
81-
time: GC.time,
83+
:count => count,
84+
:time => time,
85+
:minor_gc_count => minor_count,
86+
:major_gc_count => major_count,
87+
:unknown_count => unknown_count, # if nonzero, major or minor count needs to be updated for this GC case
88+
:committed => 0.0,
89+
:init => 0.0,
90+
:max => 0.0,
91+
:used => 0.0,
92+
:peak_committed => 0.0,
93+
:peak_init => 0.0,
94+
:peak_max => 0.0,
95+
:peak_used => 0.0,
96+
:last_committed => 0.0,
97+
:last_init => 0.0,
98+
:last_max => 0.0,
99+
:last_used => 0.0,
100+
:heap_available_slots => commited, # should be the same as the calculated commited
101+
:heap_live_slots => used, # should be the same as the calculated used
102+
:heap_free_slots => commited - used,
82103
}
104+
105+
(0...memory_pool_names.length).each do |i|
106+
# Populate memory pool specific stats
107+
stat[Truffle::Interop.to_string(memory_pool_names[i])] = {
108+
:committed => memory_pool_info[i][0][0],
109+
:init => memory_pool_info[i][0][1],
110+
:max => memory_pool_info[i][0][2],
111+
:used => memory_pool_info[i][0][3],
112+
:peak_committed => memory_pool_info[i][1][0],
113+
:peak_init => memory_pool_info[i][1][1],
114+
:peak_max => memory_pool_info[i][1][2],
115+
:peak_used => memory_pool_info[i][1][3],
116+
:last_committed => memory_pool_info[i][2][0],
117+
:last_init => memory_pool_info[i][2][1],
118+
:last_max => memory_pool_info[i][2][2],
119+
:last_used => memory_pool_info[i][2][3],
120+
}
121+
122+
# Calculate stats across memory pools
123+
stat[Truffle::Interop.to_string(memory_pool_names[i])].each_pair do |key, value|
124+
stat[key] += value
125+
end
126+
end
127+
83128
return stat unless option
84129

85130
if stat[option]

0 commit comments

Comments
 (0)