test/jdk/jdk/jfr/event/gc/collection/GCEventAll.java
changeset 50113 caf115bb98ad
child 59053 ba6c248cae19
equal deleted inserted replaced
50112:7a2a740815b7 50113:caf115bb98ad
       
     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 }