1 /*
2  * Copyright (c) 2015, 2020, 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.
8  *
9  * This code is distributed in the hope that it will be useful, but WITHOUT
10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12  * version 2 for more details (a copy is included in the LICENSE file that
13  * accompanied this code).
14  *
15  * You should have received a copy of the GNU General Public License version
16  * 2 along with this work; if not, write to the Free Software Foundation,
17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18  *
19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20  * or visit www.oracle.com if you need additional information or have any
21  * questions.
22  */
23 
24 package gc.arguments;
25 
26 /*
27  * @test TestNewSizeFlags
28  * @bug 8025166
29  * @summary Verify that young gen size conforms values specified by NewSize, MaxNewSize and Xmn options
30  * @requires vm.gc != "Z" & vm.gc != "Shenandoah"
31  * @library /test/lib
32  * @library /
33  * @modules java.base/jdk.internal.misc
34  *          java.management
35  * @build sun.hotspot.WhiteBox
36  * @run driver ClassFileInstaller sun.hotspot.WhiteBox
37  * @run driver/timeout=240  gc.arguments.TestNewSizeFlags
38  */
39 
40 import java.io.IOException;
41 import java.lang.management.MemoryUsage;
42 import java.util.Arrays;
43 import java.util.Collections;
44 import java.util.LinkedList;
45 import jdk.test.lib.process.OutputAnalyzer;
46 import jdk.test.lib.process.ProcessTools;
47 import jdk.test.lib.Utils;
48 import sun.hotspot.WhiteBox;
49 
50 public class TestNewSizeFlags {
51 
52     public static final long M = 1024 * 1024;
53 
main(String args[])54     public static void main(String args[]) throws Exception {
55         LinkedList<String> options = new LinkedList<>(
56                 Arrays.asList(Utils.getFilteredTestJavaOpts("(-Xm[nsx][^ ]+)|"
57                                 + "(-XX:(Max)?((New)|"
58                                 + "(Heap))((Size)|"
59                                 + "(Ratio))=[^ ]+)"))
60         );
61 
62         // Test NewSize and MaxNewSize
63         testNewSizeFlags(20 * M, 10 * M, 30 * M, 40 * M, options, false);
64         testNewSizeFlags(10 * M, 20 * M, 30 * M, 80 * M, options, false);
65         testNewSizeFlags(-1, 20 * M, 30 * M, 40 * M, options, false);
66         testNewSizeFlags(10 * M, -1, 30 * M, 40 * M, options, false);
67         testNewSizeFlags(20 * M, 20 * M, 30 * M, 40 * M, options, false);
68         testNewSizeFlags(20 * M, 30 * M, 40 * M, 50 * M, options, false);
69         testNewSizeFlags(30 * M, 100 * M, 150 * M, 200 * M, options, false);
70         testNewSizeFlags(20 * M, 30 * M, 128 * M, 128 * M, options, false);
71 
72         // Test -Xmn
73         testXmnFlags(0, 30 * M, 40 * M, options, true);
74         testXmnFlags(20 * M, 30 * M, 40 * M, options, false);
75         testXmnFlags(50 * M, 70 * M, 100 * M, options, false);
76     }
77 
78     /**
79      * Verify that NewSize and MaxNewSize flags affect young gen size.
80      *
81      * @param newSize value of NewSize option, omitted if negative
82      * @param maxNewSize value of MaxNewSize option, omitted if negative
83      * @param heapSize value of HeapSize option
84      * @param maxHeapSize value of MaxHeapSize option
85      * @param options additional options for JVM
86      * @param failureExpected true if JVM should fail with passed heap size options
87      */
testNewSizeFlags(long newSize, long maxNewSize, long heapSize, long maxHeapSize, LinkedList<String> options, boolean failureExpected)88     public static void testNewSizeFlags(long newSize, long maxNewSize,
89             long heapSize, long maxHeapSize,
90             LinkedList<String> options,
91             boolean failureExpected) throws Exception {
92         long expectedNewSize = newSize;
93         long expectedMaxNewSize = (maxNewSize >= 0 ? Math.max(maxNewSize, newSize) : maxNewSize);
94         testVMOptions(newSize, maxNewSize,
95                 heapSize, maxHeapSize,
96                 expectedNewSize, expectedMaxNewSize,
97                 options, failureExpected);
98     }
99 
100     /**
101      * Verify that -Xmn flag affect young gen size.
102      *
103      * @param mnValue value of -Xmn option
104      * @param heapSize value of HeapSize option
105      * @param maxHeapSize value of MaxHeapSize option
106      * @param options additional options for JVM
107      * @param failureExpected true if JVM should fail with passed heap size options
108      */
testXmnFlags(long mnValue, long heapSize, long maxHeapSize, LinkedList<String> options, boolean failureExpected)109     public static void testXmnFlags(long mnValue,
110             long heapSize, long maxHeapSize,
111             LinkedList<String> options,
112             boolean failureExpected) throws Exception {
113         LinkedList<String> newOptions = new LinkedList<>(options);
114         newOptions.add("-Xmn" + mnValue);
115         testVMOptions(-1, -1,
116                 heapSize, maxHeapSize,
117                 mnValue, mnValue,
118                 newOptions, failureExpected);
119     }
120 
121     /**
122      * Verify that NewSize and MaxNewSize flags affect young gen size.
123      *
124      * @param newSize value of NewSize option, omitted if negative
125      * @param maxNewSize value of MaxNewSize option, omitted if negative
126      * @param heapSize value of HeapSize option
127      * @param maxHeapSize value of MaxHeapSize option
128      * @param expectedNewSize expected initial young gen size
129      * @param expectedMaxNewSize expected max young gen size
130      * @param options additional options for JVM
131      * @param failureExpected true if JVM should fail with passed heap size options
132      */
testVMOptions(long newSize, long maxNewSize, long heapSize, long maxHeapSize, long expectedNewSize, long expectedMaxNewSize, LinkedList<String> options, boolean failureExpected)133     public static void testVMOptions(long newSize, long maxNewSize,
134             long heapSize, long maxHeapSize,
135             long expectedNewSize, long expectedMaxNewSize,
136             LinkedList<String> options, boolean failureExpected) throws Exception {
137         OutputAnalyzer analyzer = startVM(options, newSize, maxNewSize, heapSize, maxHeapSize, expectedNewSize, expectedMaxNewSize);
138 
139         if (failureExpected) {
140             analyzer.shouldHaveExitValue(1);
141             analyzer.shouldMatch("(Error occurred during initialization of VM)|"
142                     + "(Error: Could not create the Java Virtual Machine.)");
143         } else {
144             analyzer.shouldHaveExitValue(0);
145         }
146     }
147 
startVM(LinkedList<String> options, long newSize, long maxNewSize, long heapSize, long maxHeapSize, long expectedNewSize, long expectedMaxNewSize)148     private static OutputAnalyzer startVM(LinkedList<String> options,
149             long newSize, long maxNewSize,
150             long heapSize, long maxHeapSize,
151             long expectedNewSize, long expectedMaxNewSize) throws Exception, IOException {
152         LinkedList<String> vmOptions = new LinkedList<>(options);
153         Collections.addAll(vmOptions,
154                 "-Xbootclasspath/a:.",
155                 "-XX:+UnlockDiagnosticVMOptions",
156                 "-XX:+WhiteBoxAPI",
157                 (newSize >= 0 ? "-XX:NewSize=" + newSize : ""),
158                 (maxNewSize >= 0 ? "-XX:MaxNewSize=" + maxNewSize : ""),
159                 "-Xmx" + maxHeapSize,
160                 "-Xms" + heapSize,
161                 "-XX:GCLockerEdenExpansionPercent=0",
162                 "-XX:-UseLargePages",
163                 NewSizeVerifier.class.getName(),
164                 Long.toString(expectedNewSize),
165                 Long.toString(expectedMaxNewSize),
166                 Long.toString(heapSize),
167                 Long.toString(maxHeapSize)
168         );
169         vmOptions.removeIf(String::isEmpty);
170         ProcessBuilder procBuilder = GCArguments.createJavaProcessBuilder(vmOptions);
171         OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
172         return analyzer;
173     }
174 
175     /**
176      * NewSizeVerifier checks that initial young gen size is equal to expected
177      * regardful to alignment and that young gen size will not be greater than
178      * expected max size.
179      * In order to verify that young gen size will not be greater then expected
180      * max size, NewSizeVerifier do some object allocation to force garbage
181      * collection and heap expansion.
182      */
183     public static class NewSizeVerifier {
184 
185         private static final WhiteBox WB = WhiteBox.getWhiteBox();
186         private static final GCTypes.YoungGCType YOUNG_GC_TYPE = GCTypes.YoungGCType.getYoungGCType();
187         private static final long HEAP_SPACE_ALIGNMENT = WB.getHeapSpaceAlignment();
188         private static final long HEAP_ALIGNMENT = WB.getHeapAlignment();
189         private static final long PS_VIRTUAL_SPACE_ALIGNMENT =
190                 (YOUNG_GC_TYPE == GCTypes.YoungGCType.PSNew) ? WB.psVirtualSpaceAlignment() : 0;
191 
192         public static final int ARRAY_LENGTH = 100;
193         public static final int CHUNK_SIZE = 1024;
194         public static final int MAX_ITERATIONS = 10;
195         public static byte garbage[][] = new byte[ARRAY_LENGTH][];
196 
main(String args[])197         public static void main(String args[]) throws Exception {
198             if (args.length != 4) {
199                 throw new IllegalArgumentException("Expected 4 args: <expectedNewSize> <expectedMaxNewSize> <initialHeapSize> <maxHeapSize>");
200             }
201             final long newSize = Long.valueOf(args[0]);
202             final long maxNewSize = Long.valueOf(args[1]);
203             final long initialHeapSize = Long.valueOf(args[2]);
204             final long maxHeapSize = Long.valueOf(args[3]);
205 
206             // verify initial size
207             verifyNewSize(newSize, maxNewSize, initialHeapSize, maxHeapSize);
208 
209             // force GC and verify that size is still correct
210             AllocationHelper allocator = new AllocationHelper(MAX_ITERATIONS, ARRAY_LENGTH, CHUNK_SIZE, () -> (verifyNewSize(newSize, maxNewSize, initialHeapSize, maxHeapSize)));
211             allocator.allocateMemoryAndVerifyNoOOME();
212         }
213 
214         /**
215          * Verify that actual young gen size conforms NewSize and MaxNewSize values.
216          */
verifyNewSize(long newSize, long maxNewSize, long initialHeapSize, long maxHeapSize)217         public static Void verifyNewSize(long newSize, long maxNewSize,
218                 long initialHeapSize, long maxHeapSize) {
219             long alignedNewSize = alignGenSize(newSize);
220             long alignedMaxNewSize = alignGenSize(maxNewSize);
221             long alignedXms = alignHeapSize(initialHeapSize);
222             long alignedXmx = alignHeapSize(maxHeapSize);
223 
224             MemoryUsage youngGenUsage = getYoungGenUsage();
225             long initSize = youngGenUsage.getInit();
226             long commitedSize = youngGenUsage.getCommitted();
227             long maxSize = youngGenUsage.getMax();
228 
229             if (newSize != -1) {
230                 if (initSize < alignedNewSize) {
231                     throw new RuntimeException("initial new size < NewSize value: "
232                             + initSize + " < " + alignedNewSize);
233                 }
234 
235                 if (commitedSize < alignedNewSize) {
236                     throw new RuntimeException("actual new size < NewSize value: "
237                             + commitedSize + " < " + alignedNewSize);
238                 }
239 
240                 // for G1 max new size == committed new size
241                 if (YOUNG_GC_TYPE != GCTypes.YoungGCType.G1
242                         && maxSize < alignedNewSize) {
243                     throw new RuntimeException("max new size < NewSize value: "
244                             + maxSize + " < " + alignedNewSize);
245                 }
246             }
247 
248             if (maxNewSize != -1) {
249                 if (initSize > alignedMaxNewSize) {
250                     throw new RuntimeException("initial new size > MaxNewSize value: "
251                             + initSize + " > " + alignedMaxNewSize);
252                 }
253 
254                 if (commitedSize > alignedMaxNewSize) {
255                     throw new RuntimeException("actual new size > MaxNewSize value: "
256                             + commitedSize + " > " + alignedMaxNewSize);
257                 }
258 
259                 if (alignedXms != alignedXmx) {
260                     if (YOUNG_GC_TYPE != GCTypes.YoungGCType.G1
261                             && maxSize != alignedMaxNewSize) {
262                         throw new RuntimeException("max new size != MaxNewSize value: "
263                                 + maxSize + " != " + alignedMaxNewSize);
264                     }
265                 } else {
266                     if (YOUNG_GC_TYPE != GCTypes.YoungGCType.G1
267                             && maxSize != alignedNewSize) {
268                         throw new RuntimeException("max new size != NewSize for case InitialHeapSize == MaxHeapSize value: "
269                                 + maxSize + " != " + alignedNewSize + " HeapSize == " + alignedXms);
270                     }
271                 }
272             }
273             return null;
274         }
275 
276         /**
277          *  Get young gen memory usage.
278          *
279          *  For G1 it is EdenUsage + SurvivorUsage,
280          *  for other GCs it is EdenUsage + 2 * SurvivorUsage.
281          *  For G1 max value is just LONG_MAX.
282          *  For all GCs used value is 0.
283          */
getYoungGenUsage()284         private static MemoryUsage getYoungGenUsage() {
285             MemoryUsage edenUsage = HeapRegionUsageTool.getEdenUsage();
286             MemoryUsage survivorUsage = HeapRegionUsageTool.getSurvivorUsage();
287             long edenUsageInit = edenUsage.getInit();
288             long edenUsageCommited = edenUsage.getCommitted();
289             long survivorUsageInit = survivorUsage.getInit();
290             long survivorUsageCommited = survivorUsage.getCommitted();
291 
292             if (YOUNG_GC_TYPE == GCTypes.YoungGCType.G1) {
293                 return new MemoryUsage(edenUsageInit + survivorUsageInit, 0,
294                         edenUsageCommited + survivorUsageCommited, Long.MAX_VALUE);
295             } else {
296                 return new MemoryUsage(edenUsageInit + survivorUsageInit * 2, 0,
297                         edenUsageCommited + survivorUsageCommited * 2,
298                         edenUsage.getMax() + survivorUsage.getMax() * 2);
299             }
300         }
301 
302         /**
303          * Align generation size regardful to used young GC.
304          */
alignGenSize(long value)305         public static long alignGenSize(long value) {
306             switch (YOUNG_GC_TYPE) {
307                 case DefNew:
308                     return HeapRegionUsageTool.alignDown(value, HEAP_SPACE_ALIGNMENT);
309                 case PSNew:
310                     return HeapRegionUsageTool.alignUp(HeapRegionUsageTool.alignDown(value,
311                             HEAP_SPACE_ALIGNMENT),
312                             PS_VIRTUAL_SPACE_ALIGNMENT);
313                 case G1:
314                     return HeapRegionUsageTool.alignUp(value, WB.g1RegionSize());
315                 default:
316                     throw new RuntimeException("Unexpected young GC type");
317             }
318         }
319 
320         /**
321          * Align heap size.
322          */
alignHeapSize(long value)323         public static long alignHeapSize(long value){
324             return HeapRegionUsageTool.alignUp(value,HEAP_ALIGNMENT);
325         }
326     }
327 }
328