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