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 TestSurvivorRatioFlag
26 * @key gc
27 * @summary Verify that actual survivor ratio is equal to specified SurvivorRatio value
28 * @library /test/lib
29 * @modules java.base/jdk.internal.misc
30 *          java.management
31 * @build sun.hotspot.WhiteBox
32 * @run main ClassFileInstaller sun.hotspot.WhiteBox
33 * @run driver TestSurvivorRatioFlag
34 */
35
36import java.lang.management.MemoryUsage;
37import java.util.Arrays;
38import java.util.Collections;
39import java.util.LinkedList;
40import jdk.test.lib.process.OutputAnalyzer;
41import jdk.test.lib.process.ProcessTools;
42import jdk.test.lib.Utils;
43import sun.hotspot.WhiteBox;
44
45public class TestSurvivorRatioFlag {
46
47    public static final long M = 1024 * 1024;
48    public static final long HEAP_SIZE = 200 * M;
49    public static final long NEW_SIZE = 100 * M;
50
51    public static void main(String args[]) throws Exception {
52        LinkedList<String> options = new LinkedList<>(
53                Arrays.asList(Utils.getFilteredTestJavaOpts("-XX:[^ ]*SurvivorRatio=[^ ]+"))
54        );
55
56        testSurvivorRatio(3, options);
57        testSurvivorRatio(6, options);
58        testSurvivorRatio(10, options);
59        testSurvivorRatio(15, options);
60        testSurvivorRatio(20, options);
61    }
62
63    /**
64     * Verify that actual survivor ratio equal to specified.
65     *
66     * @param ratio survivor ratio that be verified
67     * @param options additional options to JVM
68     */
69    public static void testSurvivorRatio(int ratio, LinkedList<String> options) throws Exception {
70
71        LinkedList<String> vmOptions = new LinkedList<>(options);
72
73        Collections.addAll(vmOptions,
74                "-Xbootclasspath/a:.",
75                "--add-exports=java.base/jdk.internal.misc=ALL-UNNAMED",
76                "-XX:+UnlockDiagnosticVMOptions",
77                "-XX:+WhiteBoxAPI",
78                "-XX:GCLockerEdenExpansionPercent=0",
79                "-XX:MaxNewSize=" + NEW_SIZE,
80                "-XX:NewSize=" + NEW_SIZE,
81                "-Xmx" + HEAP_SIZE,
82                "-Xms" + HEAP_SIZE,
83                "-XX:SurvivorRatio=" + ratio,
84                SurvivorRatioVerifier.class.getName(),
85                Integer.toString(ratio)
86        );
87
88        ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()]));
89        OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
90        analyzer.shouldHaveExitValue(0);
91    }
92
93    /**
94     * Class that verifies survivor ratio.
95     */
96    public static class SurvivorRatioVerifier {
97
98        static WhiteBox wb = WhiteBox.getWhiteBox();
99
100        public static final int MAX_ITERATIONS = 10;
101        public static final int ARRAY_LENGTH = 10000;
102        public static final int CHUNK_SIZE = 10000;
103
104        public static void main(String args[]) throws Exception {
105            if (args.length != 1) {
106                throw new IllegalArgumentException("Expected 1 arg: <ratio>");
107            }
108            final int ratio = Integer.valueOf(args[0]);
109
110            AllocationHelper allocator = new AllocationHelper(MAX_ITERATIONS, ARRAY_LENGTH, CHUNK_SIZE, () -> (verifySurvivorRatio(ratio)));
111            allocator.allocateMemoryAndVerify();
112        }
113
114        /**
115         * Verify that actual survivor ratio is equal to expected.
116         * Depending on selected young GC we verify that:
117         * - for DefNew and ParNew: eden_size / survivor_size is close to expectedRatio;
118         * - for PSNew:             survivor_size equal to young_gen_size / expectedRatio;
119         * - for G1:                survivor_regions <= young_list_length / expectedRatio.
120         */
121        public static Void verifySurvivorRatio(int expectedRatio) {
122            GCTypes.YoungGCType type = GCTypes.YoungGCType.getYoungGCType();
123            switch (type) {
124                case DefNew:
125                case ParNew:
126                    verifyDefNewSurvivorRatio(expectedRatio);
127                    break;
128                case PSNew:
129                    verifyPSSurvivorRatio(expectedRatio);
130                    break;
131                case G1:
132                    verifyG1SurvivorRatio(expectedRatio);
133                    break;
134                default:
135                    throw new RuntimeException("Unexpected young GC type");
136            }
137            return null;
138        }
139
140        private static void verifyDefNewSurvivorRatio(int expectedRatio) {
141            MemoryUsage edenUsage = HeapRegionUsageTool.getEdenUsage();
142            MemoryUsage survivorUsage = HeapRegionUsageTool.getSurvivorUsage();
143
144            int actualRatio = (int) (edenUsage.getCommitted() / survivorUsage.getCommitted());
145            if (Math.abs(actualRatio - expectedRatio) > 1) {
146                throw new RuntimeException("Expected survivor ratio is: " + expectedRatio
147                        + ", but observed ratio is: " + actualRatio);
148            }
149        }
150
151        private static void verifyPSSurvivorRatio(int expectedRatio) {
152            MemoryUsage edenUsage = HeapRegionUsageTool.getEdenUsage();
153            MemoryUsage survivorUsage = HeapRegionUsageTool.getSurvivorUsage();
154
155            long youngGenSize = edenUsage.getMax() + 2 * survivorUsage.getMax();
156            // for Paralle GC Min/InitialSurvivorRatio = SurvivorRatio + 2
157            long expectedSize = HeapRegionUsageTool.alignDown(youngGenSize / (expectedRatio + 2),
158                    wb.psHeapGenerationAlignment());
159
160            if (expectedSize != survivorUsage.getCommitted()) {
161                throw new RuntimeException("Expected survivor size is: " + expectedSize
162                        + ", but observed size is: " + survivorUsage.getCommitted());
163            }
164        }
165
166        private static void verifyG1SurvivorRatio(int expectedRatio) {
167            MemoryUsage survivorUsage = HeapRegionUsageTool.getSurvivorUsage();
168
169            int regionSize = wb.g1RegionSize();
170            int youngListLength = (int) Math.max(NEW_SIZE / regionSize, 1);
171            int expectedSurvivorRegions = (int) Math.ceil(youngListLength / (double) expectedRatio);
172            int observedSurvivorRegions = (int) (survivorUsage.getCommitted() / regionSize);
173
174            if (expectedSurvivorRegions < observedSurvivorRegions) {
175                throw new RuntimeException("Expected amount of G1 survivor regions is "
176                        + expectedSurvivorRegions + ", but observed "
177                        + observedSurvivorRegions);
178            }
179        }
180    }
181}
182