50113
|
1 |
/*
|
|
2 |
* Copyright (c) 2013, 2018, Oracle and/or its affiliates. All rights reserved.
|
|
3 |
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
4 |
*
|
|
5 |
* This code is free software; you can redistribute it and/or modify it
|
|
6 |
* under the terms of the GNU General Public License version 2 only, as
|
|
7 |
* published by the Free Software Foundation. Oracle designates this
|
|
8 |
* particular file as subject to the "Classpath" exception as provided
|
|
9 |
* by Oracle in the LICENSE file that accompanied this code.
|
|
10 |
*
|
|
11 |
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
12 |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
13 |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
14 |
* version 2 for more details (a copy is included in the LICENSE file that
|
|
15 |
* accompanied this code).
|
|
16 |
*
|
|
17 |
* You should have received a copy of the GNU General Public License version
|
|
18 |
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
19 |
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
20 |
*
|
|
21 |
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
22 |
* or visit www.oracle.com if you need additional information or have any
|
|
23 |
* questions.
|
|
24 |
*/
|
|
25 |
|
|
26 |
package jdk.jfr.event.gc.collection;
|
|
27 |
|
|
28 |
import java.lang.management.ManagementFactory;
|
|
29 |
import java.time.Duration;
|
|
30 |
import java.time.Instant;
|
|
31 |
import java.util.ArrayList;
|
|
32 |
import java.util.Arrays;
|
|
33 |
import java.util.HashSet;
|
|
34 |
import java.util.List;
|
|
35 |
import java.util.Random;
|
|
36 |
import java.util.Set;
|
|
37 |
import java.util.stream.Collectors;
|
|
38 |
|
|
39 |
import jdk.jfr.EventType;
|
|
40 |
import jdk.jfr.FlightRecorder;
|
|
41 |
import jdk.jfr.Recording;
|
|
42 |
import jdk.jfr.consumer.RecordedEvent;
|
|
43 |
import jdk.test.lib.Asserts;
|
|
44 |
import jdk.test.lib.jfr.EventNames;
|
|
45 |
import jdk.test.lib.jfr.Events;
|
|
46 |
import jdk.test.lib.jfr.GCHelper;
|
|
47 |
|
|
48 |
/**
|
|
49 |
* Tests for event garbage_collection.
|
|
50 |
* The test function is called from TestGCEvent*.java, with different worker threads.
|
|
51 |
* Groups all events belonging to the same garbage collection (the same gcId).
|
|
52 |
* The group of events belonging to the same GC is called a batch.
|
|
53 |
*
|
|
54 |
* This class contains the verifications done and the worker threads used to generate GCs.
|
|
55 |
* The helper logic are in class GCHelper.
|
|
56 |
*
|
|
57 |
* Summary of verifications:
|
|
58 |
* All gcIds in garbage_collection event are unique.
|
|
59 |
*
|
|
60 |
* All events in batch has the same gcId.
|
|
61 |
*
|
|
62 |
* Number of garbage_collection events == GarbageCollectionMXBean.getCollectionCount()
|
|
63 |
*
|
|
64 |
* garbage_collection.sum_pause_time approximately equals GarbageCollectionMXBean.getCollectionTime()
|
|
65 |
*
|
|
66 |
* Batch contains expected events depending on garbage_collection.name
|
|
67 |
*
|
|
68 |
* garbage_collection_start.timestamp == garbage_collection.startTime.
|
|
69 |
*
|
|
70 |
* garbage_collection.timestamp >= timestamp for all other events in batch.
|
|
71 |
*
|
|
72 |
* The start_garbage_collection and garbage_collection events must be synchronized.
|
|
73 |
* This means that there may be multiple start_garbage_collection before a garbage_collection,
|
|
74 |
* but garbage_collection.gcId must be equal to latest start_garbage_collection.gcId.
|
|
75 |
*
|
|
76 |
* start_garbage_collection must be the first event in the batch,
|
|
77 |
* that means no event with same gcId before garbage_collection_start event.
|
|
78 |
*
|
|
79 |
* garbage_collection.name matches what is expected by the collectors specified in initial_configuration.
|
|
80 |
*
|
|
81 |
* Duration for event "vm/gc/phases/pause" >= 1. Duration for phase level events >= 0.
|
|
82 |
*
|
|
83 |
*
|
|
84 |
*/
|
|
85 |
public class GCEventAll {
|
|
86 |
private String youngCollector = null;
|
|
87 |
private String oldCollector = null;
|
|
88 |
|
|
89 |
/**
|
|
90 |
* Trigger GC events by generating garbage and calling System.gc() concurrently.
|
|
91 |
*/
|
|
92 |
public static void doTest() throws Throwable {
|
|
93 |
// Trigger GC events by generating garbage and calling System.gc() concurrently.
|
|
94 |
Thread[] workerThreads = new Thread[] {
|
|
95 |
new Thread(GCEventAll.GarbageRunner.create(10)),
|
|
96 |
new Thread(GCEventAll.SystemGcWaitRunner.create(10, 2, 1000))};
|
|
97 |
GCEventAll test = new GCEventAll();
|
|
98 |
test.doSingleTest(workerThreads);
|
|
99 |
}
|
|
100 |
|
|
101 |
/**
|
|
102 |
* Runs the test once with given worker threads.
|
|
103 |
* @param workerThreads Threads that generates GCs.
|
|
104 |
* @param gcIds Set of all used gcIds
|
|
105 |
* @throws Exception
|
|
106 |
*/
|
|
107 |
private void doSingleTest(Thread[] workerThreads) throws Throwable {
|
|
108 |
Recording recording = new Recording();
|
|
109 |
enableAllGcEvents(recording);
|
|
110 |
|
|
111 |
// Start with a full GC to minimize risk of getting extra GC between
|
|
112 |
// getBeanCollectionCount() and recording.start().
|
|
113 |
doSystemGc();
|
|
114 |
GCHelper.CollectionSummary startBeanCount = GCHelper.CollectionSummary.createFromMxBeans();
|
|
115 |
recording.start();
|
|
116 |
|
|
117 |
for (Thread t : workerThreads) {
|
|
118 |
t.start();
|
|
119 |
}
|
|
120 |
for (Thread t : workerThreads) {
|
|
121 |
t.join();
|
|
122 |
}
|
|
123 |
|
|
124 |
// End with a full GC to minimize risk of getting extra GC between
|
|
125 |
// recording.stop and getBeanCollectionCount().
|
|
126 |
doSystemGc();
|
|
127 |
// Add an extra System.gc() to make sure we get at least one full garbage_collection batch at
|
|
128 |
// the end of the test. This extra System.gc() is only necessary when using "UseConcMarkSweepGC" and "+ExplicitGCInvokesConcurrent".
|
|
129 |
doSystemGc();
|
|
130 |
|
|
131 |
recording.stop();
|
|
132 |
GCHelper.CollectionSummary deltaBeanCount = GCHelper.CollectionSummary.createFromMxBeans();
|
|
133 |
deltaBeanCount = deltaBeanCount.calcDelta(startBeanCount);
|
|
134 |
|
|
135 |
List<RecordedEvent> events = Events.fromRecording(recording).stream()
|
|
136 |
.filter(evt -> EventNames.isGcEvent(evt.getEventType()))
|
|
137 |
.collect(Collectors.toList());
|
|
138 |
RecordedEvent configEvent = GCHelper.getConfigEvent(events);
|
|
139 |
youngCollector = Events.assertField(configEvent, "youngCollector").notEmpty().getValue();
|
|
140 |
oldCollector = Events.assertField(configEvent, "oldCollector").notEmpty().getValue();
|
|
141 |
verify(events, deltaBeanCount);
|
|
142 |
}
|
|
143 |
|
|
144 |
private void enableAllGcEvents(Recording recording) {
|
|
145 |
FlightRecorder flightrecorder = FlightRecorder.getFlightRecorder();
|
|
146 |
for (EventType et : flightrecorder.getEventTypes()) {
|
|
147 |
if (EventNames.isGcEvent(et)) {
|
|
148 |
recording.enable(et.getName());
|
|
149 |
System.out.println("Enabled GC event: " + et.getName());
|
|
150 |
}
|
|
151 |
}
|
|
152 |
System.out.println("All GC events enabled");
|
|
153 |
}
|
|
154 |
|
|
155 |
private static synchronized void doSystemGc() {
|
|
156 |
System.gc();
|
|
157 |
}
|
|
158 |
|
|
159 |
/**
|
|
160 |
* Does all verifications of the received events.
|
|
161 |
*
|
|
162 |
* @param events All flight recorder events.
|
|
163 |
* @param beanCounts Number of collections and sum pause time reported by GarbageCollectionMXBeans.
|
|
164 |
* @param gcIds All used gcIds. Must be unique.
|
|
165 |
* @throws Exception
|
|
166 |
*/
|
|
167 |
private void verify(List<RecordedEvent> events, GCHelper.CollectionSummary beanCounts) throws Throwable {
|
|
168 |
List<GCHelper.GcBatch> gcBatches = null;
|
|
169 |
GCHelper.CollectionSummary eventCounts = null;
|
|
170 |
|
|
171 |
// For some GC configurations, the JFR recording may have stopped before we received the last gc event.
|
|
172 |
try {
|
|
173 |
events = filterIncompleteGcBatch(events);
|
|
174 |
gcBatches = GCHelper.GcBatch.createFromEvents(events);
|
|
175 |
eventCounts = GCHelper.CollectionSummary.createFromEvents(gcBatches);
|
|
176 |
|
|
177 |
verifyUniqueIds(gcBatches);
|
|
178 |
verifyCollectorNames(gcBatches);
|
|
179 |
verifyCollectionCause(gcBatches);
|
|
180 |
verifyCollectionCount(eventCounts, beanCounts);
|
|
181 |
verifyPhaseEvents(gcBatches);
|
|
182 |
verifySingleGcBatch(gcBatches);
|
|
183 |
} catch (Throwable t) {
|
|
184 |
log(events, gcBatches, eventCounts, beanCounts);
|
|
185 |
if (gcBatches != null) {
|
|
186 |
for (GCHelper.GcBatch batch : gcBatches) {
|
|
187 |
System.out.println(String.format("Batch:%n%s", batch.getLog()));
|
|
188 |
}
|
|
189 |
}
|
|
190 |
throw t;
|
|
191 |
}
|
|
192 |
}
|
|
193 |
|
|
194 |
/**
|
|
195 |
* When using collector ConcurrentMarkSweep with -XX:+ExplicitGCInvokesConcurrent, the JFR recording may
|
|
196 |
* stop before we have received the last garbage_collection event.
|
|
197 |
*
|
|
198 |
* This function does 3 things:
|
|
199 |
* 1. Check if the last batch is incomplete.
|
|
200 |
* 2. If it is incomplete, then asserts that incomplete batches are allowed for this configuration.
|
|
201 |
* 3. If incomplete batches are allowed, then the incomplete batch is removed.
|
|
202 |
*
|
|
203 |
* @param events All events
|
|
204 |
* @return All events with any incomplete batch removed.
|
|
205 |
* @throws Throwable
|
|
206 |
*/
|
|
207 |
private List<RecordedEvent> filterIncompleteGcBatch(List<RecordedEvent> events) throws Throwable {
|
|
208 |
List<RecordedEvent> returnEvents = new ArrayList<RecordedEvent>(events);
|
|
209 |
int lastGcId = getLastGcId(events);
|
|
210 |
List<RecordedEvent> lastBatchEvents = getEventsWithGcId(events, lastGcId);
|
|
211 |
String[] endEvents = {GCHelper.event_garbage_collection, GCHelper.event_old_garbage_collection, GCHelper.event_young_garbage_collection};
|
|
212 |
boolean isComplete = containsAnyPath(lastBatchEvents, endEvents);
|
|
213 |
if (!isComplete) {
|
|
214 |
// The last GC batch does not contain an end event. The batch is incomplete.
|
|
215 |
// This is only allowed if we are using old_collector="ConcurrentMarkSweep" and "-XX:+ExplicitGCInvokesConcurrent"
|
|
216 |
boolean isExplicitGCInvokesConcurrent = hasInputArgument("-XX:+ExplicitGCInvokesConcurrent");
|
|
217 |
boolean isConcurrentMarkSweep = GCHelper.gcConcurrentMarkSweep.equals(oldCollector);
|
|
218 |
String msg = String.format(
|
|
219 |
"Incomplete batch only allowed for '%s' with -XX:+ExplicitGCInvokesConcurrent",
|
|
220 |
GCHelper.gcConcurrentMarkSweep);
|
|
221 |
Asserts.assertTrue(isConcurrentMarkSweep && isExplicitGCInvokesConcurrent, msg);
|
|
222 |
|
|
223 |
// Incomplete batch is allowed with the current settings. Remove incomplete batch.
|
|
224 |
returnEvents.removeAll(lastBatchEvents);
|
|
225 |
}
|
|
226 |
return returnEvents;
|
|
227 |
}
|
|
228 |
|
|
229 |
private boolean hasInputArgument(String arg) {
|
|
230 |
return ManagementFactory.getRuntimeMXBean().getInputArguments().contains(arg);
|
|
231 |
}
|
|
232 |
|
|
233 |
private List<RecordedEvent> getEventsWithGcId(List<RecordedEvent> events, int gcId) {
|
|
234 |
List<RecordedEvent> batchEvents = new ArrayList<>();
|
|
235 |
for (RecordedEvent event : events) {
|
|
236 |
if (GCHelper.isGcEvent(event) && GCHelper.getGcId(event) == gcId) {
|
|
237 |
batchEvents.add(event);
|
|
238 |
}
|
|
239 |
}
|
|
240 |
return batchEvents;
|
|
241 |
}
|
|
242 |
|
|
243 |
private boolean containsAnyPath(List<RecordedEvent> events, String[] paths) {
|
|
244 |
List<String> pathList = Arrays.asList(paths);
|
|
245 |
for (RecordedEvent event : events) {
|
|
246 |
if (pathList.contains(event.getEventType().getName())) {
|
|
247 |
return true;
|
|
248 |
}
|
|
249 |
}
|
|
250 |
return false;
|
|
251 |
}
|
|
252 |
|
|
253 |
private int getLastGcId(List<RecordedEvent> events) {
|
|
254 |
int lastGcId = -1;
|
|
255 |
for (RecordedEvent event : events) {
|
|
256 |
if (GCHelper.isGcEvent(event)) {
|
|
257 |
int gcId = GCHelper.getGcId(event);
|
|
258 |
if (gcId > lastGcId) {
|
|
259 |
lastGcId = gcId;
|
|
260 |
}
|
|
261 |
}
|
|
262 |
}
|
|
263 |
Asserts.assertTrue(lastGcId != -1, "No gcId found");
|
|
264 |
return lastGcId;
|
|
265 |
}
|
|
266 |
|
|
267 |
/**
|
|
268 |
* Verifies collection count reported by flight recorder events against the values
|
|
269 |
* reported by GarbageCollectionMXBean.
|
|
270 |
* Number of collections should match exactly.
|
|
271 |
* Sum pause time are allowed some margin of error because of rounding errors in measurements.
|
|
272 |
*/
|
|
273 |
private void verifyCollectionCount(GCHelper.CollectionSummary eventCounts, GCHelper.CollectionSummary beanCounts) {
|
|
274 |
verifyCollectionCount(youngCollector, eventCounts.collectionCountYoung, beanCounts.collectionCountYoung);
|
|
275 |
verifyCollectionCount(oldCollector, eventCounts.collectionCountOld, beanCounts.collectionCountOld);
|
|
276 |
}
|
|
277 |
|
|
278 |
private void verifyCollectionCount(String collector, long eventCounts, long beanCounts) {
|
|
279 |
if (GCHelper.gcConcurrentMarkSweep.equals(collector) || GCHelper.gcG1Old.equals(oldCollector)) {
|
|
280 |
// ConcurrentMarkSweep mixes old and new collections. Not same values as in MXBean.
|
|
281 |
// MXBean does not report old collections for G1Old, so we have nothing to compare with.
|
|
282 |
return;
|
|
283 |
}
|
|
284 |
// JFR events and GarbageCollectorMXBean events are not updated at the same time.
|
|
285 |
// This means that number of collections may diff.
|
|
286 |
// We allow a diff of +- 1 collection count.
|
|
287 |
long minCount = Math.max(0, beanCounts - 1);
|
|
288 |
long maxCount = beanCounts + 1;
|
|
289 |
Asserts.assertGreaterThanOrEqual(eventCounts, minCount, "Too few event counts for collector " + collector);
|
|
290 |
Asserts.assertLessThanOrEqual(eventCounts, maxCount, "Too many event counts for collector " + collector);
|
|
291 |
}
|
|
292 |
|
|
293 |
/**
|
|
294 |
* Verifies that all events belonging to a single GC are ok.
|
|
295 |
* A GcBatch contains all flight recorder events that belong to a single GC.
|
|
296 |
*/
|
|
297 |
private void verifySingleGcBatch(List<GCHelper.GcBatch> batches) {
|
|
298 |
for (GCHelper.GcBatch batch : batches) {
|
|
299 |
//System.out.println("batch:\r\n" + batch.getLog());
|
|
300 |
try {
|
|
301 |
RecordedEvent endEvent = batch.getEndEvent();
|
|
302 |
Asserts.assertNotNull(endEvent, "No end event in batch.");
|
|
303 |
Asserts.assertNotNull(batch.getName(), "No method name in end event.");
|
|
304 |
long longestPause = Events.assertField(endEvent, "longestPause").atLeast(0L).getValue();
|
|
305 |
Events.assertField(endEvent, "sumOfPauses").atLeast(longestPause).getValue();
|
|
306 |
Instant batchStartTime = endEvent.getStartTime();
|
|
307 |
Instant batchEndTime = endEvent.getEndTime();
|
|
308 |
for (RecordedEvent event : batch.getEvents()) {
|
|
309 |
if (event.getEventType().getName().contains("AllocationRequiringGC")) {
|
|
310 |
// Unlike other events, these are sent *before* a GC.
|
|
311 |
Asserts.assertLessThanOrEqual(event.getStartTime(), batchStartTime, "Timestamp in event after start event, should be sent before GC start");
|
|
312 |
} else {
|
|
313 |
Asserts.assertGreaterThanOrEqual(event.getStartTime(), batchStartTime, "startTime in event before batch start event, should be sent after GC start");
|
|
314 |
}
|
|
315 |
Asserts.assertLessThanOrEqual(event.getEndTime(), batchEndTime, "endTime in event after batch end event, should be sent before GC end");
|
|
316 |
}
|
|
317 |
|
|
318 |
// Verify that all required events has been received.
|
|
319 |
String[] requiredEvents = GCHelper.requiredEvents.get(batch.getName());
|
|
320 |
Asserts.assertNotNull(requiredEvents, "No required events specified for " + batch.getName());
|
|
321 |
for (String requiredEvent : requiredEvents) {
|
|
322 |
boolean b = batch.containsEvent(requiredEvent);
|
|
323 |
Asserts.assertTrue(b, String.format("%s does not contain event %s", batch, requiredEvent));
|
|
324 |
}
|
|
325 |
|
|
326 |
// Verify that we have exactly one heap_summary "Before GC" and one "After GC".
|
|
327 |
int countBeforeGc = 0;
|
|
328 |
int countAfterGc = 0;
|
|
329 |
for (RecordedEvent event : batch.getEvents()) {
|
|
330 |
if (GCHelper.event_heap_summary.equals(event.getEventType().getName())) {
|
|
331 |
String when = Events.assertField(event, "when").notEmpty().getValue();
|
|
332 |
if ("Before GC".equals(when)) {
|
|
333 |
countBeforeGc++;
|
|
334 |
} else if ("After GC".equals(when)) {
|
|
335 |
countAfterGc++;
|
|
336 |
} else {
|
|
337 |
Asserts.fail("Unknown value for heap_summary.when: '" + when + "'");
|
|
338 |
}
|
|
339 |
}
|
|
340 |
}
|
|
341 |
if (!GCHelper.gcConcurrentMarkSweep.equals(batch.getName())) {
|
|
342 |
// We do not get heap_summary events for ConcurrentMarkSweep
|
|
343 |
Asserts.assertEquals(1, countBeforeGc, "Unexpected number of heap_summary.before_gc");
|
|
344 |
Asserts.assertEquals(1, countAfterGc, "Unexpected number of heap_summary.after_gc");
|
|
345 |
}
|
|
346 |
} catch (Throwable e) {
|
|
347 |
GCHelper.log("verifySingleGcBatch failed for gcEvent:");
|
|
348 |
GCHelper.log(batch.getLog());
|
|
349 |
throw e;
|
|
350 |
}
|
|
351 |
}
|
|
352 |
}
|
|
353 |
|
|
354 |
private Set<Integer> verifyUniqueIds(List<GCHelper.GcBatch> batches) {
|
|
355 |
Set<Integer> gcIds = new HashSet<>();
|
|
356 |
for (GCHelper.GcBatch batch : batches) {
|
|
357 |
Integer gcId = new Integer(batch.getGcId());
|
|
358 |
Asserts.assertFalse(gcIds.contains(gcId), "Duplicate gcId: " + gcId);
|
|
359 |
gcIds.add(gcId);
|
|
360 |
}
|
|
361 |
return gcIds;
|
|
362 |
}
|
|
363 |
|
|
364 |
private void verifyPhaseEvents(List<GCHelper.GcBatch> batches) {
|
|
365 |
for (GCHelper.GcBatch batch : batches) {
|
|
366 |
for(RecordedEvent event : batch.getEvents()) {
|
|
367 |
if (event.getEventType().getName().contains(GCHelper.pauseLevelEvent)) {
|
|
368 |
Instant batchStartTime = batch.getEndEvent().getStartTime();
|
|
369 |
Asserts.assertGreaterThanOrEqual(
|
|
370 |
event.getStartTime(), batchStartTime, "Phase startTime >= batch startTime. Event:" + event);
|
|
371 |
|
|
372 |
// Duration for event "vm/gc/phases/pause" must be >= 1. Other phase event durations must be >= 0.
|
|
373 |
Duration minDuration = Duration.ofNanos(GCHelper.event_phases_pause.equals(event.getEventType().getName()) ? 1 : 0);
|
|
374 |
Duration duration = event.getDuration();
|
|
375 |
Asserts.assertGreaterThanOrEqual(duration, minDuration, "Wrong duration. Event:" + event);
|
|
376 |
}
|
|
377 |
}
|
|
378 |
}
|
|
379 |
}
|
|
380 |
|
|
381 |
/**
|
|
382 |
* Verifies that the collector name in initial configuration matches the name in garbage configuration event.
|
|
383 |
* If the names are not equal, then we check if this is an expected collector override.
|
|
384 |
* For example, if old collector in initial config is "G1Old" we allow both event "G1Old" and "SerialOld".
|
|
385 |
*/
|
|
386 |
private void verifyCollectorNames(List<GCHelper.GcBatch> batches) {
|
|
387 |
for (GCHelper.GcBatch batch : batches) {
|
|
388 |
String name = batch.getName();
|
|
389 |
Asserts.assertNotNull(name, "garbage_collection.name was null");
|
|
390 |
boolean isYoung = batch.isYoungCollection();
|
|
391 |
String expectedName = isYoung ? youngCollector : oldCollector;
|
|
392 |
if (!expectedName.equals(name)) {
|
|
393 |
// Collector names not equal. Check if the collector has been overridden by an expected collector.
|
|
394 |
String overrideKey = expectedName + "." + name;
|
|
395 |
boolean isOverride = GCHelper.collectorOverrides.contains(overrideKey);
|
|
396 |
Asserts.assertTrue(isOverride, String.format("Unexpected event name(%s) for collectors(%s, %s)", name, youngCollector, oldCollector));
|
|
397 |
}
|
|
398 |
}
|
|
399 |
}
|
|
400 |
|
|
401 |
/**
|
|
402 |
* Verifies field "cause" in garbage_collection event.
|
|
403 |
* Only check that at cause is not null and that at least 1 cause is "System.gc()"
|
|
404 |
* We might want to check more cause reasons later.
|
|
405 |
*/
|
|
406 |
private void verifyCollectionCause(List<GCHelper.GcBatch> batches) {
|
|
407 |
int systemGcCount = 0;
|
|
408 |
for (GCHelper.GcBatch batch : batches) {
|
|
409 |
RecordedEvent endEvent = batch.getEndEvent();
|
|
410 |
String cause = Events.assertField(endEvent, "cause").notEmpty().getValue();
|
|
411 |
// A System.GC() can be consolidated into a GCLocker GC
|
|
412 |
if (cause.equals("System.gc()") || cause.equals("GCLocker Initiated GC")) {
|
|
413 |
systemGcCount++;
|
|
414 |
}
|
|
415 |
Asserts.assertNotNull(batch.getName(), "garbage_collection.name was null");
|
|
416 |
}
|
|
417 |
final String msg = "No event with cause=System.gc(), collectors(%s, %s)";
|
|
418 |
Asserts.assertTrue(systemGcCount > 0, String.format(msg, youngCollector, oldCollector));
|
|
419 |
}
|
|
420 |
|
|
421 |
private void log(List<RecordedEvent> events, List<GCHelper.GcBatch> batches,
|
|
422 |
GCHelper.CollectionSummary eventCounts, GCHelper.CollectionSummary beanCounts) {
|
|
423 |
GCHelper.log("EventCounts:");
|
|
424 |
if (eventCounts != null) {
|
|
425 |
GCHelper.log(eventCounts.toString());
|
|
426 |
}
|
|
427 |
GCHelper.log("BeanCounts:");
|
|
428 |
if (beanCounts != null) {
|
|
429 |
GCHelper.log(beanCounts.toString());
|
|
430 |
}
|
|
431 |
}
|
|
432 |
|
|
433 |
/**
|
|
434 |
* Thread that does a number of System.gc().
|
|
435 |
*/
|
|
436 |
public static class SystemGcRunner implements Runnable {
|
|
437 |
private final int totalCollections;
|
|
438 |
|
|
439 |
public SystemGcRunner(int totalCollections) {
|
|
440 |
this.totalCollections = totalCollections;
|
|
441 |
}
|
|
442 |
|
|
443 |
public static SystemGcRunner create(int totalCollections) {
|
|
444 |
return new SystemGcRunner(totalCollections);
|
|
445 |
}
|
|
446 |
|
|
447 |
public void run() {
|
|
448 |
for (int i = 0; i < totalCollections; i++) {
|
|
449 |
GCEventAll.doSystemGc();
|
|
450 |
}
|
|
451 |
}
|
|
452 |
}
|
|
453 |
|
|
454 |
/**
|
|
455 |
* Thread that creates garbage until a certain number of GCs has been run.
|
|
456 |
*/
|
|
457 |
public static class GarbageRunner implements Runnable {
|
|
458 |
private final int totalCollections;
|
|
459 |
public byte[] dummyBuffer = null;
|
|
460 |
|
|
461 |
public GarbageRunner(int totalCollections) {
|
|
462 |
this.totalCollections = totalCollections;
|
|
463 |
}
|
|
464 |
|
|
465 |
public static GarbageRunner create(int totalCollections) {
|
|
466 |
return new GarbageRunner(totalCollections);
|
|
467 |
}
|
|
468 |
|
|
469 |
public void run() {
|
|
470 |
long currCollections = GCHelper.CollectionSummary.createFromMxBeans().sum();
|
|
471 |
long endCollections = totalCollections + currCollections;
|
|
472 |
Random r = new Random(0);
|
|
473 |
while (true) {
|
|
474 |
for (int i = 0; i < 1000; i++) {
|
|
475 |
dummyBuffer = new byte[r.nextInt(10000)];
|
|
476 |
}
|
|
477 |
if (GCHelper.CollectionSummary.createFromMxBeans().sum() >= endCollections) {
|
|
478 |
break;
|
|
479 |
}
|
|
480 |
}
|
|
481 |
}
|
|
482 |
}
|
|
483 |
|
|
484 |
/**
|
|
485 |
* Thread that runs System.gc() and then wait for a number of GCs or a maximum time.
|
|
486 |
*/
|
|
487 |
public static class SystemGcWaitRunner implements Runnable {
|
|
488 |
private final int totalCollections;
|
|
489 |
private final int minWaitCollections;
|
|
490 |
private final long maxWaitMillis;
|
|
491 |
|
|
492 |
public SystemGcWaitRunner(int totalCollections, int minWaitCollections, long maxWaitMillis) {
|
|
493 |
this.totalCollections = totalCollections;
|
|
494 |
this.minWaitCollections = minWaitCollections;
|
|
495 |
this.maxWaitMillis = maxWaitMillis;
|
|
496 |
}
|
|
497 |
|
|
498 |
public static SystemGcWaitRunner create(int deltaCollections, int minWaitCollections, long maxWaitMillis) {
|
|
499 |
return new SystemGcWaitRunner(deltaCollections, minWaitCollections, maxWaitMillis);
|
|
500 |
}
|
|
501 |
|
|
502 |
public void run() {
|
|
503 |
long currCount = GCHelper.CollectionSummary.createFromMxBeans().sum();
|
|
504 |
long endCount = totalCollections + currCount;
|
|
505 |
long nextSystemGcCount = currCount + minWaitCollections;
|
|
506 |
long now = System.currentTimeMillis();
|
|
507 |
long nextSystemGcMillis = now + maxWaitMillis;
|
|
508 |
|
|
509 |
while (true) {
|
|
510 |
if (currCount >= nextSystemGcCount || System.currentTimeMillis() > nextSystemGcMillis) {
|
|
511 |
GCEventAll.doSystemGc();
|
|
512 |
currCount = GCHelper.CollectionSummary.createFromMxBeans().sum();
|
|
513 |
nextSystemGcCount = currCount + minWaitCollections;
|
|
514 |
} else {
|
|
515 |
try {
|
|
516 |
Thread.sleep(20);
|
|
517 |
} catch (InterruptedException e) {
|
|
518 |
e.printStackTrace();
|
|
519 |
break;
|
|
520 |
}
|
|
521 |
}
|
|
522 |
currCount = GCHelper.CollectionSummary.createFromMxBeans().sum();
|
|
523 |
if (currCount >= endCount) {
|
|
524 |
break;
|
|
525 |
}
|
|
526 |
}
|
|
527 |
}
|
|
528 |
}
|
|
529 |
|
|
530 |
}
|