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