8048556: Unnecessary GCLocker-initiated young GCs

Wed, 31 Jul 2019 14:28:51 -0400

author
kbarrett
date
Wed, 31 Jul 2019 14:28:51 -0400
changeset 9787
9f28a4cac6d9
parent 9784
775e2bf92114
child 9788
44ef77ad417c

8048556: Unnecessary GCLocker-initiated young GCs
Summary: Fixed recognition of unnecessary GCLocker collections.
Reviewed-by: pliden, tschatzl
Contributed-by: johnc@azul.com

src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/shared/vmGCOperations.cpp file | annotate | diff | comparison | revisions
src/share/vm/gc_implementation/shared/vmGCOperations.hpp file | annotate | diff | comparison | revisions
src/share/vm/memory/gcLocker.cpp file | annotate | diff | comparison | revisions
src/share/vm/memory/gcLocker.hpp file | annotate | diff | comparison | revisions
src/share/vm/memory/genCollectedHeap.cpp file | annotate | diff | comparison | revisions
test/gc/stress/gclocker/TestExcessGCLockerCollections.java file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Wed Aug 07 17:00:19 2019 +0800
     1.2 +++ b/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp	Wed Jul 31 14:28:51 2019 -0400
     1.3 @@ -2520,6 +2520,12 @@
     1.4            }
     1.5          }
     1.6        }
     1.7 +    } else if (GC_locker::should_discard(cause, gc_count_before)) {
     1.8 +      // Return to be consistent with VMOp failure due to another
     1.9 +      // collection slipping in after our gc_count but before our
    1.10 +      // request is processed.  _gc_locker collections upgraded by
    1.11 +      // GCLockerInvokesConcurrent are handled above and never discarded.
    1.12 +      return;
    1.13      } else {
    1.14        if (cause == GCCause::_gc_locker || cause == GCCause::_wb_young_gc
    1.15            DEBUG_ONLY(|| cause == GCCause::_scavenge_alot)) {
     2.1 --- a/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp	Wed Aug 07 17:00:19 2019 +0800
     2.2 +++ b/src/share/vm/gc_implementation/parallelScavenge/parallelScavengeHeap.cpp	Wed Jul 31 14:28:51 2019 -0400
     2.3 @@ -530,6 +530,10 @@
     2.4      full_gc_count = Universe::heap()->total_full_collections();
     2.5    }
     2.6  
     2.7 +  if (GC_locker::should_discard(cause, gc_count)) {
     2.8 +    return;
     2.9 +  }
    2.10 +
    2.11    VM_ParallelGCSystemGC op(gc_count, full_gc_count, cause);
    2.12    VMThread::execute(&op);
    2.13  }
     3.1 --- a/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.cpp	Wed Aug 07 17:00:19 2019 +0800
     3.2 +++ b/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.cpp	Wed Jul 31 14:28:51 2019 -0400
     3.3 @@ -52,11 +52,16 @@
     3.4    }
     3.5  }
     3.6  
     3.7 +static bool is_cause_full(GCCause::Cause cause) {
     3.8 +  return (cause != GCCause::_gc_locker) && (cause != GCCause::_wb_young_gc)
     3.9 +         DEBUG_ONLY(&& (cause != GCCause::_scavenge_alot));
    3.10 +}
    3.11 +
    3.12  // Only used for System.gc() calls
    3.13  VM_ParallelGCSystemGC::VM_ParallelGCSystemGC(uint gc_count,
    3.14                                               uint full_gc_count,
    3.15                                               GCCause::Cause gc_cause) :
    3.16 -  VM_GC_Operation(gc_count, gc_cause, full_gc_count, true /* full */)
    3.17 +  VM_GC_Operation(gc_count, gc_cause, full_gc_count, is_cause_full(gc_cause))
    3.18  {
    3.19  }
    3.20  
    3.21 @@ -68,8 +73,7 @@
    3.22      "must be a ParallelScavengeHeap");
    3.23  
    3.24    GCCauseSetter gccs(heap, _gc_cause);
    3.25 -  if (_gc_cause == GCCause::_gc_locker || _gc_cause == GCCause::_wb_young_gc
    3.26 -      DEBUG_ONLY(|| _gc_cause == GCCause::_scavenge_alot)) {
    3.27 +  if (!_full) {
    3.28      // If (and only if) the scavenge fails, this will invoke a full gc.
    3.29      heap->invoke_scavenge();
    3.30    } else {
     4.1 --- a/src/share/vm/gc_implementation/shared/vmGCOperations.cpp	Wed Aug 07 17:00:19 2019 +0800
     4.2 +++ b/src/share/vm/gc_implementation/shared/vmGCOperations.cpp	Wed Jul 31 14:28:51 2019 -0400
     4.3 @@ -201,6 +201,19 @@
     4.4    }
     4.5  }
     4.6  
     4.7 +static bool is_full_gc(int max_level) {
     4.8 +  // Return true if max_level is all generations
     4.9 +  return (max_level == (GenCollectedHeap::heap()->n_gens() - 1));
    4.10 +}
    4.11 +
    4.12 +VM_GenCollectFull::VM_GenCollectFull(uint gc_count_before,
    4.13 +                                     uint full_gc_count_before,
    4.14 +                                     GCCause::Cause gc_cause,
    4.15 +                                     int max_level) :
    4.16 +  VM_GC_Operation(gc_count_before, gc_cause, full_gc_count_before,
    4.17 +                  is_full_gc(max_level) /* full */),
    4.18 +  _max_level(max_level) { }
    4.19 +
    4.20  void VM_GenCollectFull::doit() {
    4.21    SvcGCMarker sgcm(SvcGCMarker::FULL);
    4.22  
     5.1 --- a/src/share/vm/gc_implementation/shared/vmGCOperations.hpp	Wed Aug 07 17:00:19 2019 +0800
     5.2 +++ b/src/share/vm/gc_implementation/shared/vmGCOperations.hpp	Wed Jul 31 14:28:51 2019 -0400
     5.3 @@ -201,9 +201,7 @@
     5.4    VM_GenCollectFull(uint gc_count_before,
     5.5                      uint full_gc_count_before,
     5.6                      GCCause::Cause gc_cause,
     5.7 -                    int max_level)
     5.8 -    : VM_GC_Operation(gc_count_before, gc_cause, full_gc_count_before, true /* full */),
     5.9 -      _max_level(max_level) { }
    5.10 +                    int max_level);
    5.11    ~VM_GenCollectFull() {}
    5.12    virtual VMOp_Type type() const { return VMOp_GenCollectFull; }
    5.13    virtual void doit();
     6.1 --- a/src/share/vm/memory/gcLocker.cpp	Wed Aug 07 17:00:19 2019 +0800
     6.2 +++ b/src/share/vm/memory/gcLocker.cpp	Wed Jul 31 14:28:51 2019 -0400
     6.3 @@ -31,6 +31,7 @@
     6.4  volatile jint GC_locker::_jni_lock_count = 0;
     6.5  volatile bool GC_locker::_needs_gc       = false;
     6.6  volatile bool GC_locker::_doing_gc       = false;
     6.7 +unsigned int  GC_locker::_total_collections = 0;
     6.8  
     6.9  #ifdef ASSERT
    6.10  volatile jint GC_locker::_debug_jni_lock_count = 0;
    6.11 @@ -94,6 +95,11 @@
    6.12    }
    6.13  }
    6.14  
    6.15 +bool GC_locker::should_discard(GCCause::Cause cause, uint total_collections) {
    6.16 +  return (cause == GCCause::_gc_locker) &&
    6.17 +         (_total_collections != total_collections);
    6.18 +}
    6.19 +
    6.20  void GC_locker::jni_lock(JavaThread* thread) {
    6.21    assert(!thread->in_critical(), "shouldn't currently be in a critical region");
    6.22    MutexLocker mu(JNICritical_lock);
    6.23 @@ -117,7 +123,13 @@
    6.24    decrement_debug_jni_lock_count();
    6.25    thread->exit_critical();
    6.26    if (needs_gc() && !is_active_internal()) {
    6.27 -    // We're the last thread out. Cause a GC to occur.
    6.28 +    // We're the last thread out. Request a GC.
    6.29 +    // Capture the current total collections, to allow detection of
    6.30 +    // other collections that make this one unnecessary.  The value of
    6.31 +    // total_collections() is only changed at a safepoint, so there
    6.32 +    // must not be a safepoint between the lock becoming inactive and
    6.33 +    // getting the count, else there may be unnecessary GCLocker GCs.
    6.34 +    _total_collections = Universe::heap()->total_collections();
    6.35      _doing_gc = true;
    6.36      {
    6.37        // Must give up the lock while at a safepoint
     7.1 --- a/src/share/vm/memory/gcLocker.hpp	Wed Aug 07 17:00:19 2019 +0800
     7.2 +++ b/src/share/vm/memory/gcLocker.hpp	Wed Jul 31 14:28:51 2019 -0400
     7.3 @@ -26,6 +26,7 @@
     7.4  #define SHARE_VM_MEMORY_GCLOCKER_HPP
     7.5  
     7.6  #include "gc_interface/collectedHeap.hpp"
     7.7 +#include "gc_interface/gcCause.hpp"
     7.8  #include "memory/genCollectedHeap.hpp"
     7.9  #include "memory/universe.hpp"
    7.10  #include "oops/oop.hpp"
    7.11 @@ -57,6 +58,7 @@
    7.12    static volatile bool _needs_gc;        // heap is filling, we need a GC
    7.13                                           // note: bool is typedef'd as jint
    7.14    static volatile bool _doing_gc;        // unlock_critical() is doing a GC
    7.15 +  static uint _total_collections;        // value for _gc_locker collection
    7.16  
    7.17  #ifdef ASSERT
    7.18    // This lock count is updated for all operations and is used to
    7.19 @@ -116,6 +118,12 @@
    7.20    // Sets _needs_gc if is_active() is true. Returns is_active().
    7.21    static bool check_active_before_gc();
    7.22  
    7.23 +  // Return true if the designated collection is a GCLocker request
    7.24 +  // that should be discarded.  Returns true if cause == GCCause::_gc_locker
    7.25 +  // and the given total collection value indicates a collection has been
    7.26 +  // done since the GCLocker request was made.
    7.27 +  static bool should_discard(GCCause::Cause cause, uint total_collections);
    7.28 +
    7.29    // Stalls the caller (who should not be in a jni critical section)
    7.30    // until needs_gc() clears. Note however that needs_gc() may be
    7.31    // set at a subsequent safepoint and/or cleared under the
     8.1 --- a/src/share/vm/memory/genCollectedHeap.cpp	Wed Aug 07 17:00:19 2019 +0800
     8.2 +++ b/src/share/vm/memory/genCollectedHeap.cpp	Wed Jul 31 14:28:51 2019 -0400
     8.3 @@ -796,8 +796,11 @@
     8.4  #else  // INCLUDE_ALL_GCS
     8.5      ShouldNotReachHere();
     8.6  #endif // INCLUDE_ALL_GCS
     8.7 -  } else if (cause == GCCause::_wb_young_gc) {
     8.8 -    // minor collection for WhiteBox API
     8.9 +  } else if ((cause == GCCause::_wb_young_gc) ||
    8.10 +             (cause == GCCause::_gc_locker)) {
    8.11 +    // minor collection for WhiteBox or GCLocker.
    8.12 +    // _gc_locker collections upgraded by GCLockerInvokesConcurrent
    8.13 +    // are handled above and never discarded.
    8.14      collect(cause, 0);
    8.15    } else {
    8.16  #ifdef ASSERT
    8.17 @@ -835,6 +838,11 @@
    8.18    // Read the GC count while holding the Heap_lock
    8.19    unsigned int gc_count_before      = total_collections();
    8.20    unsigned int full_gc_count_before = total_full_collections();
    8.21 +
    8.22 +  if (GC_locker::should_discard(cause, gc_count_before)) {
    8.23 +    return;
    8.24 +  }
    8.25 +
    8.26    {
    8.27      MutexUnlocker mu(Heap_lock);  // give up heap lock, execute gets it back
    8.28      VM_GenCollectFull op(gc_count_before, full_gc_count_before,
    8.29 @@ -887,24 +895,16 @@
    8.30  
    8.31  void GenCollectedHeap::do_full_collection(bool clear_all_soft_refs,
    8.32                                            int max_level) {
    8.33 -  int local_max_level;
    8.34 -  if (!incremental_collection_will_fail(false /* don't consult_young */) &&
    8.35 -      gc_cause() == GCCause::_gc_locker) {
    8.36 -    local_max_level = 0;
    8.37 -  } else {
    8.38 -    local_max_level = max_level;
    8.39 -  }
    8.40  
    8.41    do_collection(true                 /* full */,
    8.42                  clear_all_soft_refs  /* clear_all_soft_refs */,
    8.43                  0                    /* size */,
    8.44                  false                /* is_tlab */,
    8.45 -                local_max_level      /* max_level */);
    8.46 +                max_level            /* max_level */);
    8.47    // Hack XXX FIX ME !!!
    8.48    // A scavenge may not have been attempted, or may have
    8.49    // been attempted and failed, because the old gen was too full
    8.50 -  if (local_max_level == 0 && gc_cause() == GCCause::_gc_locker &&
    8.51 -      incremental_collection_will_fail(false /* don't consult_young */)) {
    8.52 +  if (gc_cause() == GCCause::_gc_locker && incremental_collection_failed()) {
    8.53      if (PrintGCDetails) {
    8.54        gclog_or_tty->print_cr("GC locker: Trying a full collection "
    8.55                               "because scavenge failed");
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/test/gc/stress/gclocker/TestExcessGCLockerCollections.java	Wed Jul 31 14:28:51 2019 -0400
     9.3 @@ -0,0 +1,285 @@
     9.4 +/*
     9.5 + * Copyright (c) 2019, Oracle and/or its affiliates. All rights reserved.
     9.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     9.7 + *
     9.8 + * This code is free software; you can redistribute it and/or modify it
     9.9 + * under the terms of the GNU General Public License version 2 only, as
    9.10 + * published by the Free Software Foundation.
    9.11 + *
    9.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
    9.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    9.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    9.15 + * version 2 for more details (a copy is included in the LICENSE file that
    9.16 + * accompanied this code).
    9.17 + *
    9.18 + * You should have received a copy of the GNU General Public License version
    9.19 + * 2 along with this work; if not, write to the Free Software Foundation,
    9.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    9.21 + *
    9.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    9.23 + * or visit www.oracle.com if you need additional information or have any
    9.24 + * questions.
    9.25 + */
    9.26 +
    9.27 +package gc.stress.gclocker;
    9.28 +
    9.29 +// Based on Kim Barrett;s test for JDK-8048556
    9.30 +
    9.31 +/*
    9.32 + * @test TestExcessGCLockerCollections
    9.33 + * @key gc
    9.34 + * @bug 8048556
    9.35 + * @summary Check for GC Locker initiated GCs that immediately follow another
    9.36 + * GC and so have very little needing to be collected.
    9.37 + * @library /testlibrary
    9.38 + * @run driver/timeout=1000 gc.stress.gclocker.TestExcessGCLockerCollections 300 4 2
    9.39 + */
    9.40 +
    9.41 +import java.util.HashMap;
    9.42 +import java.util.Map;
    9.43 +
    9.44 +import java.util.zip.Deflater;
    9.45 +
    9.46 +import java.util.ArrayList;
    9.47 +import java.util.Arrays;
    9.48 +
    9.49 +import javax.management.MBeanServer;
    9.50 +import javax.management.Notification;
    9.51 +import javax.management.NotificationListener;
    9.52 +import javax.management.openmbean.CompositeData;
    9.53 +import java.lang.management.ManagementFactory;
    9.54 +import java.lang.management.GarbageCollectorMXBean;
    9.55 +import java.lang.management.MemoryUsage;
    9.56 +import java.util.List;
    9.57 +import com.sun.management.GarbageCollectionNotificationInfo;
    9.58 +import com.sun.management.GcInfo;
    9.59 +
    9.60 +import com.oracle.java.testlibrary.Asserts;
    9.61 +import com.oracle.java.testlibrary.ProcessTools;
    9.62 +import com.oracle.java.testlibrary.OutputAnalyzer;
    9.63 +
    9.64 +class TestExcessGCLockerCollectionsStringConstants {
    9.65 +    // Some constant strings used in both GC logging and error detection
    9.66 +    static public final String GCLOCKER_CAUSE = "GCLocker Initiated GC";
    9.67 +    static public final String USED_TOO_LOW = "TOO LOW";
    9.68 +    static public final String USED_OK = "OK";
    9.69 +}
    9.70 +
    9.71 +class TestExcessGCLockerCollectionsAux {
    9.72 +    static private final int LARGE_MAP_SIZE = 64 * 1024;
    9.73 +
    9.74 +    static private final int MAP_ARRAY_LENGTH = 4;
    9.75 +    static private final int MAP_SIZE = 1024;
    9.76 +
    9.77 +    static private final int BYTE_ARRAY_LENGTH = 128 * 1024;
    9.78 +
    9.79 +    static private void println(String str) { System.out.println(str); }
    9.80 +    static private void println()           { System.out.println();    }
    9.81 +
    9.82 +    static private volatile boolean keepRunning = true;
    9.83 +
    9.84 +    static Map<Integer,String> populateMap(int size) {
    9.85 +        Map<Integer,String> map = new HashMap<Integer,String>();
    9.86 +        for (int i = 0; i < size; i += 1) {
    9.87 +            Integer keyInt = Integer.valueOf(i);
    9.88 +            String valStr = "value is [" + i + "]";
    9.89 +            map.put(keyInt,valStr);
    9.90 +        }
    9.91 +        return map;
    9.92 +    }
    9.93 +
    9.94 +    static private class AllocatingWorker implements Runnable {
    9.95 +        private final Object[] array = new Object[MAP_ARRAY_LENGTH];
    9.96 +        private int arrayIndex = 0;
    9.97 +
    9.98 +        private void doStep() {
    9.99 +            Map<Integer,String> map = populateMap(MAP_SIZE);
   9.100 +            array[arrayIndex] = map;
   9.101 +            arrayIndex = (arrayIndex + 1) % MAP_ARRAY_LENGTH;
   9.102 +        }
   9.103 +
   9.104 +        public void run() {
   9.105 +            while (keepRunning) {
   9.106 +                doStep();
   9.107 +            }
   9.108 +        }
   9.109 +    }
   9.110 +
   9.111 +    static private class JNICriticalWorker implements Runnable {
   9.112 +        private int count;
   9.113 +
   9.114 +        private void doStep() {
   9.115 +            byte[] inputArray = new byte[BYTE_ARRAY_LENGTH];
   9.116 +            for (int i = 0; i < inputArray.length; i += 1) {
   9.117 +                inputArray[i] = (byte) (count + i);
   9.118 +            }
   9.119 +
   9.120 +            Deflater deflater = new Deflater();
   9.121 +            deflater.setInput(inputArray);
   9.122 +            deflater.finish();
   9.123 +
   9.124 +            byte[] outputArray = new byte[2 * inputArray.length];
   9.125 +            deflater.deflate(outputArray);
   9.126 +
   9.127 +            count += 1;
   9.128 +        }
   9.129 +
   9.130 +        public void run() {
   9.131 +            while (keepRunning) {
   9.132 +                doStep();
   9.133 +            }
   9.134 +        }
   9.135 +    }
   9.136 +
   9.137 +    static class GCNotificationListener implements NotificationListener {
   9.138 +        static private final double MIN_USED_PERCENT = 40.0;
   9.139 +
   9.140 +        static private final List<String> newGenPoolNames = Arrays.asList(
   9.141 +                "G1 Eden Space",           // OpenJDK G1GC: -XX:+UseG1GC
   9.142 +                "PS Eden Space",           // OpenJDK ParallelGC: -XX:+ParallelGC
   9.143 +                "Par Eden Space",          // OpenJDK ConcMarkSweepGC: -XX:+ConcMarkSweepGC
   9.144 +                "Eden Space"               // OpenJDK SerialGC: -XX:+UseSerialGC
   9.145 +                                           // OpenJDK ConcMarkSweepGC: -XX:+ConcMarkSweepGC -XX:-UseParNewGC
   9.146 +        );
   9.147 +
   9.148 +        @Override
   9.149 +        public void handleNotification(Notification notification, Object handback) {
   9.150 +            try {
   9.151 +                if (notification.getType().equals(GarbageCollectionNotificationInfo.GARBAGE_COLLECTION_NOTIFICATION)) {
   9.152 +                    GarbageCollectionNotificationInfo info =
   9.153 +                            GarbageCollectionNotificationInfo.from((CompositeData) notification.getUserData());
   9.154 +
   9.155 +                    String gc_cause = info.getGcCause();
   9.156 +
   9.157 +                    if (gc_cause.equals(TestExcessGCLockerCollectionsStringConstants.GCLOCKER_CAUSE)) {
   9.158 +                        Map<String, MemoryUsage> memory_before_gc = info.getGcInfo().getMemoryUsageBeforeGc();
   9.159 +
   9.160 +                        for (String newGenPoolName : newGenPoolNames) {
   9.161 +                            MemoryUsage usage = memory_before_gc.get(newGenPoolName);
   9.162 +                            if (usage == null) continue;
   9.163 +
   9.164 +                            double startTime = ((double) info.getGcInfo().getStartTime()) / 1000.0;
   9.165 +                            long used = usage.getUsed();
   9.166 +                            long committed = usage.getCommitted();
   9.167 +                            long max = usage.getMax();
   9.168 +                            double used_percent = (((double) used) / Math.max(committed, max)) * 100.0;
   9.169 +
   9.170 +                            System.out.printf("%6.3f: (%s) %d/%d/%d, %8.4f%% (%s)\n",
   9.171 +                                              startTime, gc_cause, used, committed, max, used_percent,
   9.172 +                                              ((used_percent < MIN_USED_PERCENT) ? TestExcessGCLockerCollectionsStringConstants.USED_TOO_LOW
   9.173 +                                                                                 : TestExcessGCLockerCollectionsStringConstants.USED_OK));
   9.174 +                        }
   9.175 +                    }
   9.176 +                }
   9.177 +            } catch (RuntimeException ex) {
   9.178 +                System.err.println("Exception during notification processing:" + ex);
   9.179 +                ex.printStackTrace();
   9.180 +            }
   9.181 +        }
   9.182 +
   9.183 +        public static boolean register() {
   9.184 +            try {
   9.185 +                MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
   9.186 +
   9.187 +                // Get the list of MX
   9.188 +                List<GarbageCollectorMXBean> gc_mxbeans = ManagementFactory.getGarbageCollectorMXBeans();
   9.189 +
   9.190 +                // Create the notification listener
   9.191 +                GCNotificationListener gcNotificationListener = new GCNotificationListener();
   9.192 +
   9.193 +                for (GarbageCollectorMXBean gcbean : gc_mxbeans) {
   9.194 +                  // Add notification listener for the MXBean
   9.195 +                  mbeanServer.addNotificationListener(gcbean.getObjectName(), gcNotificationListener, null, null);
   9.196 +                }
   9.197 +            } catch (Exception ex) {
   9.198 +                System.err.println("Exception during mbean registration:" + ex);
   9.199 +                ex.printStackTrace();
   9.200 +                // We've failed to set up, terminate
   9.201 +                return false;
   9.202 +            }
   9.203 +
   9.204 +            return true;
   9.205 +        }
   9.206 +    }
   9.207 +
   9.208 +    static public Map<Integer,String> largeMap;
   9.209 +
   9.210 +    static public void main(String args[]) {
   9.211 +        long durationSec = Long.parseLong(args[0]);
   9.212 +        int allocThreadNum = Integer.parseInt(args[1]);
   9.213 +        int jniCriticalThreadNum = Integer.parseInt(args[2]);
   9.214 +
   9.215 +        println("Running for " + durationSec + " secs");
   9.216 +
   9.217 +        if (!GCNotificationListener.register()) {
   9.218 +          println("failed to register GC notification listener");
   9.219 +          System.exit(-1);
   9.220 +        }
   9.221 +
   9.222 +        largeMap = populateMap(LARGE_MAP_SIZE);
   9.223 +
   9.224 +        println("Starting " + allocThreadNum + " allocating threads");
   9.225 +        for (int i = 0; i < allocThreadNum; i += 1) {
   9.226 +            new Thread(new AllocatingWorker()).start();
   9.227 +        }
   9.228 +
   9.229 +        println("Starting " + jniCriticalThreadNum + " jni critical threads");
   9.230 +        for (int i = 0; i < jniCriticalThreadNum; i += 1) {
   9.231 +            new Thread(new JNICriticalWorker()).start();
   9.232 +        }
   9.233 +
   9.234 +        long durationMS = (long) (1000 * durationSec);
   9.235 +        long start = System.currentTimeMillis();
   9.236 +        long now = start;
   9.237 +        long soFar = now - start;
   9.238 +        while (soFar < durationMS) {
   9.239 +            try {
   9.240 +                Thread.sleep(durationMS - soFar);
   9.241 +            } catch (Exception e) {
   9.242 +            }
   9.243 +            now = System.currentTimeMillis();
   9.244 +            soFar = now - start;
   9.245 +        }
   9.246 +        println("Done.");
   9.247 +        keepRunning = false;
   9.248 +    }
   9.249 +}
   9.250 +
   9.251 +public class TestExcessGCLockerCollections {
   9.252 +    private static final String USED_OK_LINE =
   9.253 +        "\\(" + TestExcessGCLockerCollectionsStringConstants.GCLOCKER_CAUSE + "\\)"
   9.254 +              + " .* " +
   9.255 +        "\\(" + TestExcessGCLockerCollectionsStringConstants.USED_OK + "\\)";
   9.256 +    private static final String USED_TOO_LOW_LINE =
   9.257 +        "\\(" + TestExcessGCLockerCollectionsStringConstants.GCLOCKER_CAUSE + "\\)"
   9.258 +              + " .* " +
   9.259 +        "\\(" + TestExcessGCLockerCollectionsStringConstants.USED_TOO_LOW + "\\)";
   9.260 +
   9.261 +    private static final String[] COMMON_OPTIONS = new String[] {
   9.262 +        "-Xmx1G", "-Xms1G", "-Xmn256M" };
   9.263 +
   9.264 +    public static void main(String args[]) throws Exception {
   9.265 +        if (args.length < 3) {
   9.266 +            System.out.println("usage: TestExcessGCLockerCollections" +
   9.267 +                               " <duration sec> <alloc threads>" +
   9.268 +                               " <jni critical threads>");
   9.269 +            throw new RuntimeException("Invalid arguments");
   9.270 +        }
   9.271 +
   9.272 +        ArrayList<String> finalArgs = new ArrayList<String>();
   9.273 +        finalArgs.addAll(Arrays.asList(COMMON_OPTIONS));
   9.274 +        finalArgs.add(TestExcessGCLockerCollectionsAux.class.getName());
   9.275 +        finalArgs.addAll(Arrays.asList(args));
   9.276 +
   9.277 +        // GC and other options obtained from test framework.
   9.278 +        ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(
   9.279 +            true, finalArgs.toArray(new String[0]));
   9.280 +        OutputAnalyzer output = new OutputAnalyzer(pb.start());
   9.281 +        output.shouldHaveExitValue(0);
   9.282 +        //System.out.println("------------- begin stdout ----------------");
   9.283 +        //System.out.println(output.getStdout());
   9.284 +        //System.out.println("------------- end stdout ----------------");
   9.285 +        output.stdoutShouldMatch(USED_OK_LINE);
   9.286 +        output.stdoutShouldNotMatch(USED_TOO_LOW_LINE);
   9.287 +    }
   9.288 +}

mercurial