1 /*
  2  * Copyright (c) 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 static java.lang.System.gc;
 29 import static java.lang.Thread.sleep;
 30 import static java.util.Set.of;
 31 import static java.util.stream.Collectors.joining;
 32 import static java.util.stream.Collectors.toList;
 33 import static java.util.stream.Collectors.toSet;
 34 import static java.util.stream.IntStream.range;
 35 import static jdk.jfr.event.gc.collection.Provoker.provokeMixedGC;
 36 import static jdk.test.lib.Asserts.assertEquals;
 37 import static jdk.test.lib.Asserts.assertTrue;
 38 import static jdk.test.lib.jfr.Events.fromRecording;
 39 import static sun.hotspot.WhiteBox.getWhiteBox;
 40 
 41 import java.io.IOException;
 42 import java.lang.ref.WeakReference;
 43 import java.math.BigDecimal;
 44 import java.util.ArrayList;
 45 import java.util.Collection;
 46 import java.util.List;
 47 import java.util.Set;
 48 
 49 import jdk.jfr.Recording;
 50 import jdk.test.lib.Asserts;
 51 import jdk.test.lib.jfr.EventNames;
 52 import sun.hotspot.WhiteBox;
 53 
 54 /**
 55  * @test
 56  * @key jfr
 57  * @requires vm.hasJFR
 58  * @requires vm.gc == "G1" | vm.gc == null
 59  * @library /test/lib /test/jdk
 60  * @build sun.hotspot.WhiteBox
 61  * @run main ClassFileInstaller sun.hotspot.WhiteBox
 62  * @run main/othervm -XX:+UnlockExperimentalVMOptions -XX:MaxTenuringThreshold=1 -Xms20M -Xmx20M
 63  *      -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0 -XX:G1HeapRegionSize=1m
 64  *      -XX:+UseG1GC -XX:+UseStringDeduplication
 65  *      -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI
 66  *      jdk.jfr.event.gc.collection.TestG1ParallelPhases
 67  */
 68 
 69 public class TestG1ParallelPhases {
 70     public static List<WeakReference<byte[]>> weakRefs;
 71 
 72     public static void main(String[] args) throws IOException {
 73         Recording recording = new Recording();
 74         recording.enable(EventNames.GCPhaseParallel);
 75         recording.start();
 76 
 77         // create more weak garbage than can fit in this heap (-Xmx20m), will force collection of weak references
 78         weakRefs = range(1, 100)
 79             .mapToObj(n -> new WeakReference<>(new byte[1_000_000]))
 80             .collect(toList()); // force evaluation of lazy stream (all weak refs must be created)
 81 
 82         final var MEG = 1024 * 1024;
 83         provokeMixedGC(1 * MEG);
 84         recording.stop();
 85 
 86         Set<String> usedPhases = fromRecording(recording).stream()
 87             .map(e -> e.getValue("name").toString())
 88             .collect(toSet());
 89 
 90         Set<String> allPhases = of(
 91             "ExtRootScan",
 92             "ThreadRoots",
 93             "StringTableRoots",
 94             "UniverseRoots",
 95             "JNIRoots",
 96             "ObjectSynchronizerRoots",
 97             "ManagementRoots",
 98             "SystemDictionaryRoots",
 99             "CLDGRoots",
100             "JVMTIRoots",
101             "CMRefRoots",
102             "WaitForStrongCLD",
103             "WeakCLDRoots",
104             "SATBFiltering",
105             "UpdateRS",
106             "ScanHCC",
107             "ScanRS",
108             "CodeRoots",
109             "ObjCopy",
110             "Termination",
111             "StringDedupQueueFixup",
112             "StringDedupTableFixup",
113             "RedirtyCards",
114        //     "PreserveCMReferents",
115             "NonYoungFreeCSet",
116             "YoungFreeCSet"
117         );
118 
119         assertTrue(usedPhases.equals(allPhases), "Compare events expected and received"
120             + ", Not found phases: " + allPhases.stream().filter(p -> !usedPhases.contains(p)).collect(joining(", "))
121             + ", Not expected phases: " + usedPhases.stream().filter(p -> !allPhases.contains(p)).collect(joining(", ")));
122     }
123 }
124 
125 /**
126  * Utility class to guarantee a mixed GC. The class allocates several arrays and
127  * promotes them to the oldgen. After that it tries to provoke mixed GC by
128  * allocating new objects.
129  */
130 class Provoker {
131     private static void allocateOldObjects(
132             List<byte[]> liveOldObjects,
133             int g1HeapRegionSize,
134             int arraySize) {
135 
136         var toUnreachable = new ArrayList<byte[]>();
137 
138         // Allocates buffer and promotes it to the old gen. Mix live and dead old objects.
139         // allocate about two regions of old memory. At least one full old region will guarantee
140         // mixed collection in the future
141         range(0, g1HeapRegionSize/arraySize).forEach(n -> {
142             liveOldObjects.add(new byte[arraySize]);
143             toUnreachable.add(new byte[arraySize]);
144         });
145 
146         // Do two young collections, MaxTenuringThreshold=1 will force promotion.
147         getWhiteBox().youngGC();
148         getWhiteBox().youngGC();
149 
150         // Check it is promoted & keep alive
151         Asserts.assertTrue(getWhiteBox().isObjectInOldGen(liveOldObjects), "List of the objects is suppose to be in OldGen");
152         Asserts.assertTrue(getWhiteBox().isObjectInOldGen(toUnreachable), "List of the objects is suppose to be in OldGen");
153     }
154 
155     private static void waitTillCMCFinished(int sleepTime) {
156         while (getWhiteBox().g1InConcurrentMark()) {
157               try {sleep(sleepTime);} catch (Exception e) {}
158         }
159     }
160 
161     /**
162     * The necessary condition for guaranteed mixed GC is running in VM with the following flags:
163     * -XX:+UnlockExperimentalVMOptions -XX:MaxTenuringThreshold=1 -Xms{HEAP_SIZE}M
164     * -Xmx{HEAP_SIZE}M -XX:G1MixedGCLiveThresholdPercent=100 -XX:G1HeapWastePercent=0
165     * -XX:G1HeapRegionSize={REGION_SIZE}m
166     *
167     * @param provokeSize The size to allocate to provoke the start of a mixed gc (half heap size?)
168     * @param g1HeapRegionSize The size of your regions in bytes
169     */
170     public static void provokeMixedGC(int g1HeapRegionSize) {
171         final var arraySize = 20_000;
172         var liveOldObjects = new ArrayList<byte[]>();
173         allocateOldObjects(liveOldObjects, g1HeapRegionSize, arraySize);
174         waitTillCMCFinished(10);
175         getWhiteBox().g1StartConcMarkCycle();
176         waitTillCMCFinished(10);
177         getWhiteBox().youngGC();
178         getWhiteBox().youngGC();
179 
180         // check that liveOldObjects still alive
181         assertTrue(getWhiteBox().isObjectInOldGen(liveOldObjects), "List of the objects is suppose to be in OldGen");
182     }
183 }