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