8081317: [NEWTEST] documented GC ratio tuning and new size options should be covered by regression tests

Reviewed-by: iignatyev, dfazunen
This commit is contained in:
Michail Chernov 2015-09-01 21:38:07 +03:00
parent 187c7c2b58
commit 6814a6121a
10 changed files with 1842 additions and 0 deletions

View file

@ -55,6 +55,7 @@
#include "gc/g1/g1CollectedHeap.inline.hpp" #include "gc/g1/g1CollectedHeap.inline.hpp"
#include "gc/g1/heapRegionRemSet.hpp" #include "gc/g1/heapRegionRemSet.hpp"
#include "gc/parallel/parallelScavengeHeap.inline.hpp" #include "gc/parallel/parallelScavengeHeap.inline.hpp"
#include "gc/parallel/adjoiningGenerations.hpp"
#endif // INCLUDE_ALL_GCS #endif // INCLUDE_ALL_GCS
#if INCLUDE_NMT #if INCLUDE_NMT
#include "services/mallocSiteTable.hpp" #include "services/mallocSiteTable.hpp"
@ -296,6 +297,11 @@ WB_ENTRY(jlong, WB_GetObjectSize(JNIEnv* env, jobject o, jobject obj))
return p->size() * HeapWordSize; return p->size() * HeapWordSize;
WB_END WB_END
WB_ENTRY(jlong, WB_GetHeapSpaceAlignment(JNIEnv* env, jobject o))
size_t alignment = Universe::heap()->collector_policy()->space_alignment();
return (jlong)alignment;
WB_END
#if INCLUDE_ALL_GCS #if INCLUDE_ALL_GCS
WB_ENTRY(jboolean, WB_G1IsHumongous(JNIEnv* env, jobject o, jobject obj)) WB_ENTRY(jboolean, WB_G1IsHumongous(JNIEnv* env, jobject o, jobject obj))
G1CollectedHeap* g1 = G1CollectedHeap::heap(); G1CollectedHeap* g1 = G1CollectedHeap::heap();
@ -334,6 +340,17 @@ WB_ENTRY(jint, WB_G1RegionSize(JNIEnv* env, jobject o))
return (jint)HeapRegion::GrainBytes; return (jint)HeapRegion::GrainBytes;
WB_END WB_END
WB_ENTRY(jlong, WB_PSVirtualSpaceAlignment(JNIEnv* env, jobject o))
ParallelScavengeHeap* ps = ParallelScavengeHeap::heap();
size_t alignment = ps->gens()->virtual_spaces()->alignment();
return (jlong)alignment;
WB_END
WB_ENTRY(jlong, WB_PSHeapGenerationAlignment(JNIEnv* env, jobject o))
size_t alignment = ParallelScavengeHeap::heap()->generation_alignment();
return (jlong)alignment;
WB_END
WB_ENTRY(jobject, WB_G1AuxiliaryMemoryUsage(JNIEnv* env)) WB_ENTRY(jobject, WB_G1AuxiliaryMemoryUsage(JNIEnv* env))
ResourceMark rm(THREAD); ResourceMark rm(THREAD);
G1CollectedHeap* g1h = G1CollectedHeap::heap(); G1CollectedHeap* g1h = G1CollectedHeap::heap();
@ -1332,6 +1349,7 @@ static JNINativeMethod methods[] = {
{CC"getVMPageSize", CC"()I", (void*)&WB_GetVMPageSize }, {CC"getVMPageSize", CC"()I", (void*)&WB_GetVMPageSize },
{CC"getVMAllocationGranularity", CC"()J", (void*)&WB_GetVMAllocationGranularity }, {CC"getVMAllocationGranularity", CC"()J", (void*)&WB_GetVMAllocationGranularity },
{CC"getVMLargePageSize", CC"()J", (void*)&WB_GetVMLargePageSize}, {CC"getVMLargePageSize", CC"()J", (void*)&WB_GetVMLargePageSize},
{CC"getHeapSpaceAlignment", CC"()J", (void*)&WB_GetHeapSpaceAlignment},
{CC"isClassAlive0", CC"(Ljava/lang/String;)Z", (void*)&WB_IsClassAlive }, {CC"isClassAlive0", CC"(Ljava/lang/String;)Z", (void*)&WB_IsClassAlive },
{CC"parseCommandLine0", {CC"parseCommandLine0",
CC"(Ljava/lang/String;C[Lsun/hotspot/parser/DiagnosticCommand;)[Ljava/lang/Object;", CC"(Ljava/lang/String;C[Lsun/hotspot/parser/DiagnosticCommand;)[Ljava/lang/Object;",
@ -1356,6 +1374,8 @@ static JNINativeMethod methods[] = {
{CC"g1StartConcMarkCycle", CC"()Z", (void*)&WB_G1StartMarkCycle }, {CC"g1StartConcMarkCycle", CC"()Z", (void*)&WB_G1StartMarkCycle },
{CC"g1AuxiliaryMemoryUsage", CC"()Ljava/lang/management/MemoryUsage;", {CC"g1AuxiliaryMemoryUsage", CC"()Ljava/lang/management/MemoryUsage;",
(void*)&WB_G1AuxiliaryMemoryUsage }, (void*)&WB_G1AuxiliaryMemoryUsage },
{CC"psVirtualSpaceAlignment",CC"()J", (void*)&WB_PSVirtualSpaceAlignment},
{CC"psHeapGenerationAlignment",CC"()J", (void*)&WB_PSHeapGenerationAlignment},
#endif // INCLUDE_ALL_GCS #endif // INCLUDE_ALL_GCS
#if INCLUDE_NMT #if INCLUDE_NMT
{CC"NMTMalloc", CC"(J)J", (void*)&WB_NMTMalloc }, {CC"NMTMalloc", CC"(J)J", (void*)&WB_NMTMalloc },

View file

@ -0,0 +1,115 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.Arrays;
import java.util.Objects;
/**
* Helper class with enum representation of GC types.
*/
public final class GCTypes {
private static <T extends GCType> T getCurrentGCType(Class<T> type) {
return ManagementFactory.getGarbageCollectorMXBeans().stream()
.map(bean -> getGCTypeByName(type, bean.getName()))
.filter(Objects::nonNull)
.findFirst()
.orElse(null);
}
private static <T extends GCType> T getGCTypeByName(Class<T> type, String name) {
return Arrays.stream(type.getEnumConstants())
.filter(e -> e.getGCName().equals(name))
.findFirst()
.orElse(null);
}
private static <T extends GCType> GarbageCollectorMXBean getGCBeanByType(Class<T> type) {
return ManagementFactory.getGarbageCollectorMXBeans().stream()
.filter(bean -> Arrays.stream(type.getEnumConstants())
.filter(enumName -> enumName.getGCName().equals(bean.getName()))
.findFirst()
.isPresent()
)
.findFirst()
.orElse(null);
}
/**
* Helper interface used by GCTypes static methods
* to get gcTypeName field of *GCType classes.
*/
private interface GCType {
String getGCName();
}
public static enum YoungGCType implements GCType {
DefNew("Copy"),
ParNew("ParNew"),
PSNew("PS Scavenge"),
G1("G1 Young Generation");
@Override
public String getGCName() {
return gcTypeName;
}
private final String gcTypeName;
private YoungGCType(String name) {
gcTypeName = name;
}
public static YoungGCType getYoungGCType() {
return GCTypes.getCurrentGCType(YoungGCType.class);
}
public static GarbageCollectorMXBean getYoungGCBean() {
return GCTypes.getGCBeanByType(YoungGCType.class);
}
}
public static enum OldGCType implements GCType {
Serial("MarkSweepCompact"),
CMS("ConcurrentMarkSweep"),
PSOld("PS MarkSweep"),
G1("G1 Old Generation");
private final String gcTypeName;
private OldGCType(String name) {
gcTypeName = name;
}
public static OldGCType getOldGCType() {
return GCTypes.getCurrentGCType(OldGCType.class);
}
@Override
public String getGCName() {
return gcTypeName;
}
}
}

View file

@ -0,0 +1,299 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test TestMaxMinHeapFreeRatioFlags
* @key gc
* @summary Verify that heap size changes according to max and min heap free ratios.
* @library /testlibrary
* @modules java.base/sun.misc
* java.management
* @build TestMaxMinHeapFreeRatioFlags
* @run driver/timeout=240 TestMaxMinHeapFreeRatioFlags
*/
import java.util.LinkedList;
import java.util.Arrays;
import java.util.Collections;
import jdk.test.lib.OutputAnalyzer;
import jdk.test.lib.ProcessTools;
import jdk.test.lib.Utils;
import jdk.test.lib.HeapRegionUsageTool;
import sun.misc.Unsafe;
public class TestMaxMinHeapFreeRatioFlags {
public static final long M = 1024 * 1024;
public static final long MAX_HEAP_SIZE = 200 * M;
public static final long HEAP_SIZE = 10 * M;
public static final long MAX_NEW_SIZE = 20 * M;
public static final long NEW_SIZE = 5 * M;
public static void main(String args[]) throws Exception {
LinkedList<String> options = new LinkedList<>(
Arrays.asList(Utils.getFilteredTestJavaOpts("-XX:[^ ]*HeapFreeRatio","-XX:\\+ExplicitGCInvokesConcurrent"))
);
negativeTest(20, false, 10, true, options);
negativeTest(100, true, 0, false, options);
negativeTest(101, false, 50, false, options);
negativeTest(49, true, 102, true, options);
negativeTest(-1, false, 50, false, options);
negativeTest(50, true, -1, true, options);
positiveTest(10, false, 90, false, options);
positiveTest(10, true, 80, false, options);
positiveTest(20, false, 70, true, options);
positiveTest(25, true, 65, true, options);
positiveTest(40, false, 50, false, options);
}
/**
* Verify that heap size will be changed to conform
* min and max heap free ratios.
*
* @param minRatio value of MinHeapFreeRatio option
* @param useXminf used Xminf option instead of MinHeapFreeRatio
* @param maxRatio value of MaxHeapFreeRatio option
* @param useXmaxf used Xmaxf option instead of MaxHeapFreeRatio
* @param options additional options for JVM
*/
public static void positiveTest(int minRatio, boolean useXminf,
int maxRatio, boolean useXmaxf,
LinkedList<String> options) throws Exception {
LinkedList<String> vmOptions = new LinkedList<>(options);
Collections.addAll(vmOptions,
(useXminf ? "-Xminf" + minRatio / 100.0 : "-XX:MinHeapFreeRatio=" + minRatio),
(useXmaxf ? "-Xmaxf" + maxRatio / 100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio),
"-Xmx" + MAX_HEAP_SIZE,
"-Xms" + HEAP_SIZE,
"-XX:NewSize=" + NEW_SIZE,
"-XX:MaxNewSize=" + MAX_NEW_SIZE,
RatioVerifier.class.getName(),
Integer.toString(minRatio),
Integer.toString(maxRatio)
);
ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()]));
OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
analyzer.shouldHaveExitValue(0);
}
/**
* Verify that VM will fail to start with specified ratios.
*
* @param minRatio value of MinHeapFreeRatio option
* @param useXminf used Xminf option instead of MinHeapFreeRatio
* @param maxRatio value of MaxHeapFreeRatio option
* @param useXmaxf used Xmaxf option instead of MaxHeapFreeRatio
* @param options additional options for JVM
*/
public static void negativeTest(int minRatio, boolean useXminf,
int maxRatio, boolean useXmaxf,
LinkedList<String> options) throws Exception {
LinkedList<String> vmOptions = new LinkedList<>(options);
Collections.addAll(vmOptions,
(useXminf ? "-Xminf" + minRatio / 100.0 : "-XX:MinHeapFreeRatio=" + minRatio),
(useXmaxf ? "-Xmaxf" + maxRatio / 100.0 : "-XX:MaxHeapFreeRatio=" + maxRatio),
"-version"
);
ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()]));
OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
analyzer.shouldHaveExitValue(1);
analyzer.shouldContain("Error: Could not create the Java Virtual Machine.");
}
/**
* RatioVerifier will be executed in the tested VM.
* It will check that real heap usage after collection lies between MinHeapFreeRatio and MaxHeapFreeRatio.
*/
public static class RatioVerifier {
private static final Unsafe unsafe = Utils.getUnsafe();
// Size of byte array that will be allocated
public static final int CHUNK_SIZE = 1024;
// Length of byte array, that will be added to "garbage" list.
public static final int ARRAY_LENGTH = CHUNK_SIZE - Unsafe.ARRAY_BYTE_BASE_OFFSET;
// Amount of tries to force heap shrinking/expansion using GC
public static final int GC_TRIES = 10;
// Value that will be added/substracted from expected min/max heap free ratio
// during memory allocation to make sure that specified limit will be exceeded.
public static final double OVERLOAD = 0.05;
// Acceptable heap free ratio limit exceedance: verification will fail if
// actual ratio is lower than expected min heap free ratio - VARIANCE or
// higher than expected max heap free ratio + VARIANCE.
public static final double VARIANCE = 0.025;
public static LinkedList<Object> garbage = new LinkedList<>();
public static void main(String args[]) throws Exception {
if (args.length != 2) {
throw new IllegalArgumentException("Expected 2 args: <minRatio> <maxRatio>");
}
if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.PSOld) {
System.out.println("Test is not applicable to parallel GC");
return;
}
double minRatio = Integer.valueOf(args[0]) / 100.0;
double maxRatio = Integer.valueOf(args[1]) / 100.0;
long maxHeapSize = getMax();
// commit 0.5 of total heap size to have enough space
// to both shink and expand
while (getCommitted() < maxHeapSize / 2) {
garbage.add(new byte[ARRAY_LENGTH]);
}
forceGC();
// Verify that current heap free ratio lies between specified limits
verifyRatio(minRatio, maxRatio);
// Estimate how much memory we have to allocate to force expansion
long memoryToFill = (long) (getCommitted() * (1 - minRatio + OVERLOAD))
- getUsed();
long previouslyCommitted = getCommitted();
while (memoryToFill > 0) {
garbage.add(new byte[CHUNK_SIZE]);
memoryToFill -= CHUNK_SIZE;
}
forceGC();
// Verify that after memory allocation heap free ratio is still conforming specified limits
verifyRatio(minRatio, maxRatio);
// Verify that heap was actually expanded
if (previouslyCommitted >= getCommitted()) {
throw new RuntimeException("Heap was not expanded.");
}
// Estimate how much memory we have to free to force shrinking
long memoryToFree = getUsed()
- (long) (getCommitted() * (1 - maxRatio - OVERLOAD));
previouslyCommitted = getCommitted();
while (memoryToFree > 0 && garbage.size() > 0) {
garbage.remove(garbage.size() - 1);
memoryToFree -= CHUNK_SIZE;
}
forceGC();
// Verify that heap free ratio is still conforming specified limits
verifyRatio(minRatio, maxRatio);
// Verify that heap was actually shrinked
if (previouslyCommitted <= getCommitted()) {
throw new RuntimeException("Heap was not shrinked.");
}
}
public static void forceGC() {
for (int i = 0; i < GC_TRIES; i++) {
System.gc();
try {
Thread.sleep(10);
} catch (InterruptedException ie) {
}
}
}
/**
* Verify that heap free ratio is conforming specified limits.
* Actual heap free ratio may be very close to one of specified limits,
* but exceed for more then VARIANCE.
* Verification will also pass if actual ratio is not conforming limits,
* but it is not possible to shrink/expand heap.
*/
public static void verifyRatio(double minRatio, double maxRatio) {
double ratio = getHeapFreeRatio();
System.out.println(minRatio + " " + ratio + " " + maxRatio);
if (minRatio - ratio > VARIANCE
&& getCommitted() < getMax()) {
throw new RuntimeException("Current heap free ratio is lower than "
+ "MinHeapFreeRatio (" + ratio + " vs " + minRatio + ").");
}
if (ratio - maxRatio > VARIANCE
&& getUsed() > getInit()) {
throw new RuntimeException("Current heap free ratio is higher than "
+ "MaxHeapFreeRatio (" + ratio + " vs " + maxRatio + ").");
}
}
/*
* Obtain information about heap size.
*
* For G1 information summed up for all type of regions,
* because tested options affect overall heap sizing.
*
* For all other GCs return information only for old gen.
*/
public static long getMax() {
return HeapRegionUsageTool.getOldUsage().getMax();
}
public static long getInit() {
if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) {
return HeapRegionUsageTool.getEdenUsage().getInit()
+ HeapRegionUsageTool.getSurvivorUsage().getInit()
+ HeapRegionUsageTool.getOldUsage().getInit();
} else {
return HeapRegionUsageTool.getOldUsage().getInit();
}
}
public static long getUsed() {
if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) {
return HeapRegionUsageTool.getEdenUsage().getUsed()
+ HeapRegionUsageTool.getSurvivorUsage().getUsed()
+ HeapRegionUsageTool.getOldUsage().getUsed();
} else {
return HeapRegionUsageTool.getOldUsage().getUsed();
}
}
public static long getCommitted() {
if (GCTypes.OldGCType.getOldGCType() == GCTypes.OldGCType.G1) {
return HeapRegionUsageTool.getEdenUsage().getCommitted()
+ HeapRegionUsageTool.getSurvivorUsage().getCommitted()
+ HeapRegionUsageTool.getOldUsage().getCommitted();
} else {
return HeapRegionUsageTool.getOldUsage().getCommitted();
}
}
public static long getFree() {
return getCommitted() - getUsed();
}
public static double getHeapFreeRatio() {
return getFree() / (double) getCommitted();
}
}
}

View file

@ -0,0 +1,206 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test TestMinAndInitialSurvivorRatioFlags
* @key gc
* @summary Verify that MinSurvivorRatio and InitialSurvivorRatio flags work
* @library /testlibrary /../../test/lib
* @modules java.base/sun.misc
* java.management
* @build TestMinAndInitialSurvivorRatioFlags
* @run main ClassFileInstaller sun.hotspot.WhiteBox
* @run driver TestMinAndInitialSurvivorRatioFlags
*/
import jdk.test.lib.AllocationHelper;
import java.lang.management.MemoryUsage;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import jdk.test.lib.HeapRegionUsageTool;
import jdk.test.lib.OutputAnalyzer;
import jdk.test.lib.ProcessTools;
import jdk.test.lib.Utils;
import sun.hotspot.WhiteBox;
/* Test verifies that VM can start with any GC when MinSurvivorRatio and
* InitialSurvivorRatio flags passed and for Parallel GC it verifies that
* after start up survivor ratio equal to InitialSurvivorRatio value and
* that actual survivor ratio will never be less than MinSurvivorRatio.
*/
public class TestMinAndInitialSurvivorRatioFlags {
public static final long M = 1024 * 1024;
public static final long HEAP_SIZE = 200 * M;
public static final long NEW_SIZE = 100 * M;
public static void main(String args[]) throws Exception {
LinkedList<String> options = new LinkedList<>(
Arrays.asList(Utils.getFilteredTestJavaOpts("-XX:[^ ]*SurvivorRatio=[^ ]+"))
);
testSurvivorRatio(5, -1, -1, options, true);
testSurvivorRatio(10, -1, -1, options, true);
testSurvivorRatio(-1, 5, 3, options, true);
testSurvivorRatio(-1, 15, 3, options, true);
testSurvivorRatio(-1, 15, 3, options, false);
testSurvivorRatio(-1, 10, 10, options, true);
testSurvivorRatio(-1, 3, 15, options, true);
testSurvivorRatio(-1, 3, 15, options, false);
}
/**
* Test that MinSurvivorRatio and InitialSurvivorRatio flags work.
*
* @param survivorRatio value for -XX:SurvivorRatio option, omitted if negative
* @param initRatio value for -XX:InitialSurvivorRatio option, omitted if negative
* @param minRatio value for -XX:MinSurvivorRatio option, omitted if negative
* @param options additional options for VM
* @param useAdaptiveSizePolicy turn on or off UseAdaptiveSizePolicy option
*/
public static void testSurvivorRatio(int survivorRatio,
int initRatio,
int minRatio,
LinkedList<String> options,
boolean useAdaptiveSizePolicy) throws Exception {
LinkedList<String> vmOptions = new LinkedList<>(options);
Collections.addAll(vmOptions,
"-Xbootclasspath/a:.",
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
"-XX:MaxNewSize=" + NEW_SIZE, "-XX:NewSize=" + NEW_SIZE,
"-Xmx" + HEAP_SIZE, "-Xms" + HEAP_SIZE,
(survivorRatio >= 0 ? "-XX:SurvivorRatio=" + survivorRatio : ""),
(initRatio >= 0 ? "-XX:InitialSurvivorRatio=" + initRatio : ""),
(minRatio >= 0 ? "-XX:MinSurvivorRatio=" + minRatio : ""),
(useAdaptiveSizePolicy ? "-XX:+UseAdaptiveSizePolicy" : "-XX:-UseAdaptiveSizePolicy"),
SurvivorRatioVerifier.class.getName(),
Integer.toString(survivorRatio),
Integer.toString(initRatio),
Integer.toString(minRatio),
Boolean.toString(useAdaptiveSizePolicy)
);
vmOptions.removeIf((String p) -> p.isEmpty());
ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()]));
OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
analyzer.shouldHaveExitValue(0);
}
/**
* Class that verifies survivor ratio.
* Will be executed in tested VM. Checks initial size of eden and survivor paces with alignment.
*/
public static class SurvivorRatioVerifier {
public static WhiteBox wb = WhiteBox.getWhiteBox();
public static final int MAX_ITERATIONS = 10;
public static final int ARRAY_LENGTH = 10000;
public static final int CHUNK_SIZE = 10000;
public static byte garbage[][] = new byte[ARRAY_LENGTH][];
public static void main(String args[]) throws Exception {
if (args.length != 4) {
throw new IllegalArgumentException("Expected 4 args: <survivorRatio> <initRatio> <minRatio> <useAdaptiveSizePolicy>");
}
final int survivorRatio = Integer.valueOf(args[0]);
final int initRatio = Integer.valueOf(args[1]);
final int minRatio = Integer.valueOf(args[2]);
final boolean useAdaptiveSizePolicy = Boolean.valueOf(args[3]);
// we stop testing only here to ensure that JVM will accept
// both MinSurvivorRatio and InitialSurvivorRatio regardles to GC
if (GCTypes.YoungGCType.getYoungGCType() != GCTypes.YoungGCType.PSNew) {
System.out.println("Test is only applicable to Parallel GC");
return;
}
// verify initial survivor ratio
verifySurvivorRatio(survivorRatio, initRatio, minRatio, useAdaptiveSizePolicy, true);
// force GC
AllocationHelper allocator = new AllocationHelper(MAX_ITERATIONS, ARRAY_LENGTH, CHUNK_SIZE,
() -> (verifySurvivorRatio(survivorRatio, initRatio, minRatio, useAdaptiveSizePolicy, false)));
allocator.allocateMemoryAndVerify();
}
/**
* Verify actual survivor ratio.
*
* @param survivorRatio value of SurvivorRatio option, omitted if negative
* @param initRatio value of InitialSurvivorRatio option, omitted if negative
* @param minRatio value of MinSurvivorRatio option, omitted if negative
* @param useAdaptiveSizePolicy value of UseAdaptiveSizePolicy option
* @param verifyInitialRatio true if we are going to verify initial ratio
*/
public static Void verifySurvivorRatio(int survivorRatio,
int initRatio,
int minRatio,
boolean useAdaptiveSizePolicy,
boolean verifyInitialRatio) {
MemoryUsage edenUsage = HeapRegionUsageTool.getEdenUsage();
MemoryUsage survivorUsage = HeapRegionUsageTool.getSurvivorUsage();
long alignedNewSize = edenUsage.getMax() + 2 * survivorUsage.getMax();
long generationAlignment = wb.psHeapGenerationAlignment();
if (survivorRatio >= 0) {
// -XX:SurvivorRatio was passed to JVM, actual ratio should be SurvivorRatio + 2
long expectedSize = HeapRegionUsageTool.alignDown(alignedNewSize / (survivorRatio + 2),
generationAlignment);
if (survivorUsage.getCommitted() != expectedSize) {
throw new RuntimeException("Expected survivor size is: " + expectedSize
+ ", but observed size is: " + survivorUsage.getCommitted());
}
} else if (verifyInitialRatio || !useAdaptiveSizePolicy) {
// In case of initial ratio verification or disabled adaptive size policy
// ratio should be equal to InitialSurvivorRatio value
long expectedSize = HeapRegionUsageTool.alignDown(alignedNewSize / initRatio,
generationAlignment);
if (survivorUsage.getCommitted() != expectedSize) {
throw new RuntimeException("Expected survivor size is: " + expectedSize
+ ", but observed size is: " + survivorUsage.getCommitted());
}
} else {
// In any other case actual survivor ratio should not be lower than MinSurvivorRatio
// or is should be equal to InitialSurvivorRatio
long expectedMinSize = HeapRegionUsageTool.alignDown(alignedNewSize / minRatio,
generationAlignment);
long expectedInitSize = HeapRegionUsageTool.alignDown(alignedNewSize / initRatio,
generationAlignment);
if (survivorUsage.getCommitted() != expectedInitSize
&& survivorUsage.getCommitted() < expectedMinSize) {
throw new RuntimeException("Expected survivor size should be " + expectedMinSize
+ " or should be greater then " + expectedMinSize
+ ", but observer survivor size is " + survivorUsage.getCommitted());
}
}
return null;
}
}
}

View file

@ -0,0 +1,182 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test TestNewRatioFlag
* @key gc
* @bug 8025166
* @summary Verify that heap devided among generations according to NewRatio
* @library /testlibrary /../../test/lib
* @modules java.base/sun.misc
* java.management
* @build TestNewRatioFlag
* @run main ClassFileInstaller sun.hotspot.WhiteBox
* @run driver TestNewRatioFlag
*/
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import jdk.test.lib.HeapRegionUsageTool;
import jdk.test.lib.OutputAnalyzer;
import jdk.test.lib.ProcessTools;
import jdk.test.lib.Utils;
import sun.hotspot.WhiteBox;
public class TestNewRatioFlag {
public static final long M = 1024 * 1024;
public static final long HEAP_SIZE = 100 * M;
public static void main(String args[]) throws Exception {
LinkedList<String> options = new LinkedList<>(
Arrays.asList(Utils.getFilteredTestJavaOpts("(-XX:[^ ]*NewSize=[^ ]+)|(-Xm[ns][^ ]+)"))
);
testNewRatio(4, options);
testNewRatio(6, options);
testNewRatio(10, options);
testNewRatio(15, options);
testNewRatio(20, options);
}
/**
* Verify that actual size of young gen conforms specified NewRatio
*
* @param ratio value of NewRatio option
* @param options additional options for VM
*/
public static void testNewRatio(int ratio, LinkedList<String> options) throws Exception {
LinkedList<String> vmOptions = new LinkedList<>(options);
Collections.addAll(vmOptions,
"-Xbootclasspath/a:.",
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
"-XX:GCLockerEdenExpansionPercent=0",
"-Xmx" + HEAP_SIZE,
"-Xms" + HEAP_SIZE,
"-XX:NewRatio=" + ratio,
"-XX:-UseLargePages",
NewRatioVerifier.class.getName(),
Integer.toString(ratio)
);
ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()]));
OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
analyzer.shouldHaveExitValue(0);
System.out.println(analyzer.getOutput());
}
public static class NewRatioVerifier {
static WhiteBox wb = WhiteBox.getWhiteBox();
public static void main(String args[]) {
if (args.length != 1) {
throw new IllegalArgumentException("Expected 1 arg: <expectedRatio>");
}
int expectedRatio = Integer.valueOf(args[0]);
switch (GCTypes.YoungGCType.getYoungGCType()) {
case DefNew:
case ParNew:
verifyDefNewNewRatio(expectedRatio);
break;
case PSNew:
verifyPSNewRatio(expectedRatio);
break;
case G1:
verifyG1NewRatio(expectedRatio);
break;
default:
throw new RuntimeException("Unexpected young GC type");
}
}
/**
* Verify NewSize for DefNew and ParNew collectors.
*
* Compare expected NewSize calculated according to sizing policies used by DefNew
* with NewSize value reported by MemoryPoolMXBeans.
*/
public static void verifyDefNewNewRatio(int expectedRatio) {
long initEden = HeapRegionUsageTool.getEdenUsage().getInit();
long initSurv = HeapRegionUsageTool.getSurvivorUsage().getInit();
long initOld = HeapRegionUsageTool.getOldUsage().getInit();
long newSize = initEden + 2 * initSurv;
long expectedNewSize = HeapRegionUsageTool.alignDown(initOld / expectedRatio,
wb.getHeapSpaceAlignment());
if (expectedNewSize != newSize) {
throw new RuntimeException("Expected young gen size is: " + expectedNewSize
+ ", but observed new size is: " + newSize);
}
}
/**
* Verify NewSize for PS collector.
* Expected NewSize calculated according to alignment policies used by PS
* and then compared with actual NewSize obtained from MemoryPoolMXBeans.
*/
public static void verifyPSNewRatio(int expectedRatio) {
long initEden = HeapRegionUsageTool.getEdenUsage().getInit();
long initSurv = HeapRegionUsageTool.getSurvivorUsage().getInit();
long initOld = HeapRegionUsageTool.getOldUsage().getInit();
long newSize = initEden + 2 * initSurv;
long alignedDownNewSize = HeapRegionUsageTool.alignDown(initOld / expectedRatio,
wb.getHeapSpaceAlignment());
long expectedNewSize = HeapRegionUsageTool.alignUp(alignedDownNewSize,
wb.psVirtualSpaceAlignment());
if (expectedNewSize != newSize) {
throw new RuntimeException("Expected young gen size is: " + expectedNewSize
+ ", but observed new size is: " + newSize);
}
}
/**
* Verify NewSize for G1 GC.
* Amount of young regions calculated according to sizing policies used by G1
* and then compared with actual number of young regions derived from
* values reported by MemoryPoolMXBeans and region size.
*/
public static void verifyG1NewRatio(int expectedRatio) {
long initEden = HeapRegionUsageTool.getEdenUsage().getInit();
long initSurv = HeapRegionUsageTool.getSurvivorUsage().getInit();
long maxOld = HeapRegionUsageTool.getOldUsage().getMax();
int regionSize = wb.g1RegionSize();
int youngListLength = (int) ((initEden + initSurv) / regionSize);
int maxRegions = (int) (maxOld / regionSize);
int expectedYoungListLength = (int) (maxRegions / (double) (expectedRatio + 1));
if (youngListLength != expectedYoungListLength) {
throw new RuntimeException("Expected G1 young list length is: " + expectedYoungListLength
+ ", but observed young list length is: " + youngListLength);
}
}
}
}

View file

@ -0,0 +1,296 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test TestNewSizeFlags
* @key gc
* @bug 8025166
* @summary Verify that young gen size conforms values specified by NewSize, MaxNewSize and Xmn options
* @library /testlibrary /../../test/lib
* @modules java.base/sun.misc
* java.management
* @build TestNewSizeFlags
* @run main ClassFileInstaller sun.hotspot.WhiteBox
* @run driver/timeout=240 TestNewSizeFlags
*/
import jdk.test.lib.AllocationHelper;
import java.io.IOException;
import java.lang.management.MemoryUsage;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import jdk.test.lib.HeapRegionUsageTool;
import jdk.test.lib.OutputAnalyzer;
import jdk.test.lib.ProcessTools;
import jdk.test.lib.Utils;
import sun.hotspot.WhiteBox;
public class TestNewSizeFlags {
public static final long M = 1024 * 1024;
public static void main(String args[]) throws Exception {
LinkedList<String> options = new LinkedList<>(
Arrays.asList(Utils.getFilteredTestJavaOpts("(-Xm[nsx][^ ]+)|"
+ "(-XX:(Max)?((New)|"
+ "(Heap))((Size)|"
+ "(Ratio))=[^ ]+)"))
);
// Test NewSize and MaxNewSize
testNewSizeFlags(20 * M, 10 * M, 30 * M, 40 * M, options, false);
testNewSizeFlags(10 * M, 20 * M, 30 * M, 40 * M, options, false);
testNewSizeFlags(-1, 20 * M, 30 * M, 40 * M, options, false);
testNewSizeFlags(10 * M, -1, 30 * M, 40 * M, options, false);
testNewSizeFlags(20 * M, 20 * M, 30 * M, 40 * M, options, false);
testNewSizeFlags(20 * M, 30 * M, 40 * M, 50 * M, options, false);
testNewSizeFlags(30 * M, 100 * M, 150 * M, 200 * M, options, false);
testNewSizeFlags(0, -1, 30 * M, 40 * M, options, false);
// Test -Xmn
testXmnFlags(0, 30 * M, 40 * M, options, true);
testXmnFlags(20 * M, 30 * M, 40 * M, options, false);
testXmnFlags(50 * M, 70 * M, 100 * M, options, false);
}
/**
* Verify that NewSize and MaxNewSize flags affect young gen size.
*
* @param newSize value of NewSize option, omitted if negative
* @param maxNewSize value of MaxNewSize option, omitted if negative
* @param heapSize value of HeapSize option
* @param maxHeapSize value of MaxHeapSize option
* @param options additional options for JVM
* @param failureExpected true if JVM should fail with passed heap size options
*/
public static void testNewSizeFlags(long newSize, long maxNewSize,
long heapSize, long maxHeapSize,
LinkedList<String> options,
boolean failureExpected) throws Exception {
testVMOptions(newSize, maxNewSize,
heapSize, maxHeapSize,
newSize, (maxNewSize >= 0 ? Math.max(maxNewSize, newSize) : maxNewSize),
options, failureExpected);
}
/**
* Verify that -Xmn flag affect young gen size.
*
* @param mnValue value of -Xmn option
* @param heapSize value of HeapSize option
* @param maxHeapSize value of MaxHeapSize option
* @param options additional options for JVM
* @param failureExpected true if JVM should fail with passed heap size options
*/
public static void testXmnFlags(long mnValue,
long heapSize, long maxHeapSize,
LinkedList<String> options,
boolean failureExpected) throws Exception {
LinkedList<String> newOptions = new LinkedList<>(options);
newOptions.add("-Xmn" + mnValue);
testVMOptions(-1, -1,
heapSize, maxHeapSize,
mnValue, mnValue,
newOptions, failureExpected);
}
/**
* Verify that NewSize and MaxNewSize flags affect young gen size.
*
* @param newSize value of NewSize option, omitted if negative
* @param maxNewSize value of MaxNewSize option, omitted if negative
* @param heapSize value of HeapSize option
* @param maxHeapSize value of MaxHeapSize option
* @param expectedNewSize expected initial young gen size
* @param expectedMaxNewSize expected max young gen size
* @param options additional options for JVM
* @param failureExpected true if JVM should fail with passed heap size options
*/
public static void testVMOptions(long newSize, long maxNewSize,
long heapSize, long maxHeapSize,
long expectedNewSize, long expectedMaxNewSize,
LinkedList<String> options, boolean failureExpected) throws Exception {
OutputAnalyzer analyzer = startVM(options, newSize, maxNewSize, heapSize, maxHeapSize, expectedNewSize, expectedMaxNewSize);
if (failureExpected) {
analyzer.shouldHaveExitValue(1);
analyzer.shouldMatch("(Error occurred during initialization of VM)|"
+ "(Error: Could not create the Java Virtual Machine.)");
} else {
analyzer.shouldHaveExitValue(0);
}
}
private static OutputAnalyzer startVM(LinkedList<String> options,
long newSize, long maxNewSize,
long heapSize, long maxHeapSize,
long expectedNewSize, long expectedMaxNewSize) throws Exception, IOException {
LinkedList<String> vmOptions = new LinkedList<>(options);
Collections.addAll(vmOptions,
"-Xbootclasspath/a:.",
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
(newSize >= 0 ? "-XX:NewSize=" + newSize : ""),
(maxNewSize >= 0 ? "-XX:MaxNewSize=" + maxNewSize : ""),
"-Xmx" + maxHeapSize,
"-Xms" + heapSize,
"-XX:GCLockerEdenExpansionPercent=0",
"-XX:-UseLargePages",
NewSizeVerifier.class.getName(),
Long.toString(expectedNewSize),
Long.toString(expectedMaxNewSize)
);
vmOptions.removeIf(String::isEmpty);
ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()]));
OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
return analyzer;
}
/**
* NewSizeVerifier checks that initial young gen size is equal to expected
* regardful to alignment and that young gen size will not be greater than
* expected max size.
* In order to verify that young gen size will not be greater then expected
* max size, NewSizeVerifier do some object allocation to force garbage
* collection and heap expansion.
*/
public static class NewSizeVerifier {
static WhiteBox wb = WhiteBox.getWhiteBox();
public static final int ARRAY_LENGTH = 100;
public static final int CHUNK_SIZE = 1024;
public static final int MAX_ITERATIONS = 10;
public static byte garbage[][] = new byte[ARRAY_LENGTH][];
public static void main(String args[]) throws Exception {
if (args.length != 2) {
throw new IllegalArgumentException("Expected 2 args: <expectedNewSize> <expectedMaxNewSize>");
}
final long newSize = Long.valueOf(args[0]);
final long maxNewSize = Long.valueOf(args[1]);
// verify initial size
verifyNewSize(newSize, maxNewSize);
// force GC and verify that size is still correct
AllocationHelper allocator = new AllocationHelper(MAX_ITERATIONS, ARRAY_LENGTH, CHUNK_SIZE, () -> (verifyNewSize(newSize, maxNewSize)));
allocator.allocateMemoryAndVerifyNoOOME();
}
/**
* Verify that actual young gen size conforms NewSize and MaxNewSize values.
*/
public static Void verifyNewSize(long newSize, long maxNewSize) {
long alignedNewSize = alignNewSize(newSize);
long alignedMaxNewSize = alignNewSize(maxNewSize);
MemoryUsage youngGenUsage = getYoungGenUsage();
if (newSize != -1) {
if (youngGenUsage.getInit() < alignedNewSize) {
throw new RuntimeException("initial new size < NewSize value: "
+ youngGenUsage.getInit() + " < " + alignedNewSize);
}
if (youngGenUsage.getCommitted() < alignedNewSize) {
throw new RuntimeException("actual new size < NewSize value: "
+ youngGenUsage.getCommitted() + " < " + alignedNewSize);
}
// for G1 max new size == committed new size
if (GCTypes.YoungGCType.getYoungGCType() != GCTypes.YoungGCType.G1
&& youngGenUsage.getMax() < alignedNewSize) {
throw new RuntimeException("max new size < NewSize value: "
+ youngGenUsage.getMax() + " < " + alignedNewSize);
}
}
if (maxNewSize != -1) {
if (youngGenUsage.getInit() > alignedMaxNewSize) {
throw new RuntimeException("initial new size > MaxNewSize value: "
+ youngGenUsage.getInit() + " > " + alignedMaxNewSize);
}
if (youngGenUsage.getCommitted() > alignedMaxNewSize) {
throw new RuntimeException("actual new size > MaxNewSize value: "
+ youngGenUsage.getCommitted() + " > " + alignedMaxNewSize);
}
if (GCTypes.YoungGCType.getYoungGCType() != GCTypes.YoungGCType.G1
&& youngGenUsage.getMax() != alignedMaxNewSize) {
throw new RuntimeException("max new size != MaxNewSize value: "
+ youngGenUsage.getMax() + " != " + alignedMaxNewSize);
}
}
return null;
}
/**
* Get young gen memory usage.
*
* For G1 it is EdenUsage + SurvivorUsage,
* for other GCs it is EdenUsage + 2 * SurvivorUsage.
* For G1 max value is just LONG_MAX.
* For all GCs used value is 0.
*/
private static MemoryUsage getYoungGenUsage() {
if (GCTypes.YoungGCType.getYoungGCType() == GCTypes.YoungGCType.G1) {
return new MemoryUsage(HeapRegionUsageTool.getEdenUsage().getInit()
+ HeapRegionUsageTool.getSurvivorUsage().getInit(),
0,
HeapRegionUsageTool.getEdenUsage().getCommitted()
+ HeapRegionUsageTool.getSurvivorUsage().getCommitted(),
Long.MAX_VALUE);
} else {
return new MemoryUsage(HeapRegionUsageTool.getEdenUsage().getInit()
+ HeapRegionUsageTool.getSurvivorUsage().getInit() * 2,
0,
HeapRegionUsageTool.getEdenUsage().getCommitted()
+ HeapRegionUsageTool.getSurvivorUsage().getCommitted() * 2,
HeapRegionUsageTool.getEdenUsage().getMax()
+ HeapRegionUsageTool.getSurvivorUsage().getMax() * 2);
}
}
/**
* Align value regardful to used young GC.
*/
public static long alignNewSize(long value) {
switch (GCTypes.YoungGCType.getYoungGCType()) {
case DefNew:
case ParNew:
return HeapRegionUsageTool.alignDown(value, wb.getHeapSpaceAlignment());
case PSNew:
return HeapRegionUsageTool.alignUp(HeapRegionUsageTool.alignDown(value,
wb.getHeapSpaceAlignment()),
wb.psVirtualSpaceAlignment());
case G1:
return HeapRegionUsageTool.alignUp(value, wb.g1RegionSize());
default:
throw new RuntimeException("Unexpected young GC type");
}
}
}
}

View file

@ -0,0 +1,182 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test TestSurvivorRatioFlag
* @key gc
* @summary Verify that actual survivor ratio is equal to specified SurvivorRatio value
* @library /testlibrary /../../test/lib
* @modules java.base/sun.misc
* java.management
* @build TestSurvivorRatioFlag
* @run main ClassFileInstaller sun.hotspot.WhiteBox
* @run driver TestSurvivorRatioFlag
*/
import jdk.test.lib.AllocationHelper;
import java.lang.management.MemoryUsage;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import jdk.test.lib.HeapRegionUsageTool;
import jdk.test.lib.OutputAnalyzer;
import jdk.test.lib.ProcessTools;
import jdk.test.lib.Utils;
import sun.hotspot.WhiteBox;
public class TestSurvivorRatioFlag {
public static final long M = 1024 * 1024;
public static final long HEAP_SIZE = 200 * M;
public static final long NEW_SIZE = 100 * M;
public static void main(String args[]) throws Exception {
LinkedList<String> options = new LinkedList<>(
Arrays.asList(Utils.getFilteredTestJavaOpts("-XX:[^ ]*SurvivorRatio=[^ ]+"))
);
testSurvivorRatio(3, options);
testSurvivorRatio(6, options);
testSurvivorRatio(10, options);
testSurvivorRatio(15, options);
testSurvivorRatio(20, options);
}
/**
* Verify that actual survivor ratio equal to specified.
*
* @param ratio survivor ratio that be verified
* @param options additional options to JVM
*/
public static void testSurvivorRatio(int ratio, LinkedList<String> options) throws Exception {
LinkedList<String> vmOptions = new LinkedList<>(options);
Collections.addAll(vmOptions,
"-Xbootclasspath/a:.",
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
"-XX:GCLockerEdenExpansionPercent=0",
"-XX:MaxNewSize=" + NEW_SIZE,
"-XX:NewSize=" + NEW_SIZE,
"-Xmx" + HEAP_SIZE,
"-Xms" + HEAP_SIZE,
"-XX:SurvivorRatio=" + ratio,
SurvivorRatioVerifier.class.getName(),
Integer.toString(ratio)
);
ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()]));
OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
analyzer.shouldHaveExitValue(0);
}
/**
* Class that verifies survivor ratio.
*/
public static class SurvivorRatioVerifier {
static WhiteBox wb = WhiteBox.getWhiteBox();
public static final int MAX_ITERATIONS = 10;
public static final int ARRAY_LENGTH = 10000;
public static final int CHUNK_SIZE = 10000;
public static void main(String args[]) throws Exception {
if (args.length != 1) {
throw new IllegalArgumentException("Expected 1 arg: <ratio>");
}
final int ratio = Integer.valueOf(args[0]);
AllocationHelper allocator = new AllocationHelper(MAX_ITERATIONS, ARRAY_LENGTH, CHUNK_SIZE, () -> (verifySurvivorRatio(ratio)));
allocator.allocateMemoryAndVerify();
}
/**
* Verify that actual survivor ratio is equal to expected.
* Depending on selected young GC we verify that:
* - for DefNew and ParNew: eden_size / survivor_size is close to expectedRatio;
* - for PSNew: survivor_size equal to young_gen_size / expectedRatio;
* - for G1: survivor_regions <= young_list_length / expectedRatio.
*/
public static Void verifySurvivorRatio(int expectedRatio) {
GCTypes.YoungGCType type = GCTypes.YoungGCType.getYoungGCType();
switch (type) {
case DefNew:
case ParNew:
verifyDefNewSurvivorRatio(expectedRatio);
break;
case PSNew:
verifyPSSurvivorRatio(expectedRatio);
break;
case G1:
verifyG1SurvivorRatio(expectedRatio);
break;
default:
throw new RuntimeException("Unexpected young GC type");
}
return null;
}
private static void verifyDefNewSurvivorRatio(int expectedRatio) {
MemoryUsage edenUsage = HeapRegionUsageTool.getEdenUsage();
MemoryUsage survivorUsage = HeapRegionUsageTool.getSurvivorUsage();
int actualRatio = (int) (edenUsage.getCommitted() / survivorUsage.getCommitted());
if (Math.abs(actualRatio - expectedRatio) > 1) {
throw new RuntimeException("Expected survivor ratio is: " + expectedRatio
+ ", but observed ratio is: " + actualRatio);
}
}
private static void verifyPSSurvivorRatio(int expectedRatio) {
MemoryUsage edenUsage = HeapRegionUsageTool.getEdenUsage();
MemoryUsage survivorUsage = HeapRegionUsageTool.getSurvivorUsage();
long youngGenSize = edenUsage.getMax() + 2 * survivorUsage.getMax();
// for Paralle GC Min/InitialSurvivorRatio = SurvivorRatio + 2
long expectedSize = HeapRegionUsageTool.alignDown(youngGenSize / (expectedRatio + 2),
wb.psHeapGenerationAlignment());
if (expectedSize != survivorUsage.getCommitted()) {
throw new RuntimeException("Expected survivor size is: " + expectedSize
+ ", but observed size is: " + survivorUsage.getCommitted());
}
}
private static void verifyG1SurvivorRatio(int expectedRatio) {
MemoryUsage survivorUsage = HeapRegionUsageTool.getSurvivorUsage();
int regionSize = wb.g1RegionSize();
int youngListLength = (int) Math.max(NEW_SIZE / regionSize, 1);
int expectedSurvivorRegions = (int) Math.ceil(youngListLength / (double) expectedRatio);
int observedSurvivorRegions = (int) (survivorUsage.getCommitted() / regionSize);
if (expectedSurvivorRegions < observedSurvivorRegions) {
throw new RuntimeException("Expected amount of G1 survivor regions is "
+ expectedSurvivorRegions + ", but observed "
+ observedSurvivorRegions);
}
}
}
}

View file

@ -0,0 +1,319 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* @test TestTargetSurvivorRatioFlag
* @key gc
* @summary Verify that option TargetSurvivorRatio affects survivor space occupancy after minor GC.
* @library /testlibrary /../../test/lib
* @modules java.base/sun.misc
* java.management
* @build TestTargetSurvivorRatioFlag
* @run main ClassFileInstaller sun.hotspot.WhiteBox
* @run driver TestTargetSurvivorRatioFlag
*/
import jdk.test.lib.AllocationHelper;
import java.lang.management.GarbageCollectorMXBean;
import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jdk.test.lib.HeapRegionUsageTool;
import sun.misc.Unsafe;
import jdk.test.lib.OutputAnalyzer;
import jdk.test.lib.ProcessTools;
import jdk.test.lib.Utils;
import sun.hotspot.WhiteBox;
/* In order to test that TargetSurvivorRatio affects survivor space occupancy
* we setup fixed MaxTenuringThreshold and then verifying that if size of allocated
* objects is lower than (survivor_size * TargetSurvivorRatio / 100) then objects
* will stay in survivor space until MaxTenuringThreshold minor GC cycles.
* If more than (survivor_size * TargetSurvivorRatio / 100) objects were allocated,
* then we verify that after MaxTenuringThreshold minor GC cycles survivor space
* is almost empty.
*/
public class TestTargetSurvivorRatioFlag {
public static final long M = 1024 * 1024;
// VM option values
public static final long MAX_NEW_SIZE = 40 * M;
public static final int SURVIVOR_RATIO = 8;
public static final int MAX_TENURING_THRESHOLD = 15;
// Value used to estimate amount of memory that should be allocated
// and placed in survivor space.
public static final double DELTA = 0.25;
// Max variance of observed ratio
public static double VARIANCE = 1;
// Messages used by debuggee
public static final String UNSUPPORTED_GC = "Unsupported GC";
public static final String START_TEST = "Start test";
public static final String END_TEST = "End test";
// Patterns used during log parsing
public static final String TENURING_DISTRIBUTION = "Desired survivor size";
public static final String AGE_TABLE_ENTRY = "-[\\s]+age[\\s]+([0-9]+):[\\s]+([0-9]+)[\\s]+bytes,[\\s]+([0-9]+)[\\s]+total";
public static final String MAX_SURVIVOR_SIZE = "Max survivor size: ([0-9]+)";
public static void main(String args[]) throws Exception {
LinkedList<String> options = new LinkedList<>(Arrays.asList(Utils.getTestJavaOpts()));
// Need to consider the effect of TargetPLABWastePct=1 for G1 GC
if (options.contains("-XX:+UseG1GC")) {
VARIANCE = 2;
} else {
VARIANCE = 1;
}
negativeTest(-1, options);
negativeTest(101, options);
positiveTest(20, options);
positiveTest(30, options);
positiveTest(55, options);
positiveTest(70, options);
}
/**
* Verify that VM will fail to start with specified TargetSurvivorRatio
*
* @param ratio value of TargetSurvivorRatio
* @param options additional VM options
*/
public static void negativeTest(int ratio, LinkedList<String> options) throws Exception {
LinkedList<String> vmOptions = new LinkedList<>(options);
vmOptions.add("-XX:TargetSurvivorRatio=" + ratio);
vmOptions.add("-version");
ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()]));
OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
analyzer.shouldHaveExitValue(1);
analyzer.shouldContain("Error: Could not create the Java Virtual Machine.");
}
/**
* Verify that actual survivor space usage ratio conforms specified TargetSurvivorRatio
*
* @param ratio value of TargetSurvivorRatio
* @param options additional VM options
*/
public static void positiveTest(int ratio, LinkedList<String> options) throws Exception {
LinkedList<String> vmOptions = new LinkedList<>(options);
Collections.addAll(vmOptions,
"-Xbootclasspath/a:.",
"-XX:+UnlockDiagnosticVMOptions",
"-XX:+WhiteBoxAPI",
"-XX:+UseAdaptiveSizePolicy",
"-XX:+PrintTenuringDistribution",
"-XX:MaxTenuringThreshold=" + MAX_TENURING_THRESHOLD,
"-XX:NewSize=" + MAX_NEW_SIZE,
"-XX:MaxNewSize=" + MAX_NEW_SIZE,
"-XX:InitialHeapSize=" + 2 * MAX_NEW_SIZE,
"-XX:MaxHeapSize=" + 2 * MAX_NEW_SIZE,
"-XX:SurvivorRatio=" + SURVIVOR_RATIO,
"-XX:TargetSurvivorRatio=" + ratio,
// For reducing variance of survivor size.
"-XX:TargetPLABWastePct=" + 1,
TargetSurvivorRatioVerifier.class.getName(),
Integer.toString(ratio)
);
ProcessBuilder procBuilder = ProcessTools.createJavaProcessBuilder(vmOptions.toArray(new String[vmOptions.size()]));
OutputAnalyzer analyzer = new OutputAnalyzer(procBuilder.start());
analyzer.shouldHaveExitValue(0);
String output = analyzer.getOutput();
// Test avoids verification for parallel GC
if (!output.contains(UNSUPPORTED_GC)) {
// Two tests should be done - when actual ratio is lower than TargetSurvivorRatio
// and when it is higher. We chech that output contains results for exactly two tests.
List<Double> ratios = parseTestOutput(output);
if (ratios.size() != 2) {
System.out.println(output);
throw new RuntimeException("Expected number of ratios extraced for output is 2,"
+ " but " + ratios.size() + " ratios were extracted");
}
// At the end of the first test survivor space usage ratio should lies between
// TargetSurvivorRatio and TargetSurvivorRatio - 2*DELTA
if (ratio < ratios.get(0) || ratio - ratios.get(0) > VARIANCE) {
System.out.println(output);
throw new RuntimeException("Survivor space usage ratio expected to be close to "
+ ratio + ", but observed ratio is: " + ratios.get(0));
}
// After second test survivor space should be almost empty.
if (ratios.get(1) > VARIANCE) {
System.out.println(output);
throw new RuntimeException("Survivor space expected to be empty due to "
+ "TargetSurvivorRatio overlimit, however observed "
+ "survivor space usage ratio is: " + ratios.get(1));
}
} else {
System.out.println("Selected GC does not support TargetSurvivorRatio option.");
}
}
/**
* Parse output produced by TargetSurvivorRatioVerifier.
*
* @param output output obtained from TargetSurvivorRatioVerifier
* @return list of parsed test results, where each result is an actual
* survivor ratio after MaxTenuringThreshold minor GC cycles.
*/
public static List<Double> parseTestOutput(String output) {
List<Double> ratios = new LinkedList<Double>();
String lines[] = output.split("[\n\r]");
boolean testStarted = false;
long survivorSize = 0;
long survivorOccupancy = 0;
int gcCount = 0;
Pattern ageTableEntry = Pattern.compile(AGE_TABLE_ENTRY);
Pattern maxSurvivorSize = Pattern.compile(MAX_SURVIVOR_SIZE);
for (String line : lines) {
if (Pattern.matches(MAX_SURVIVOR_SIZE, line)) {
// We found estimated survivor space size
Matcher m = maxSurvivorSize.matcher(line);
m.find();
survivorSize = Long.valueOf(m.group(1));
} else if (line.contains(START_TEST) && !testStarted) {
// Start collecting test results
testStarted = true;
gcCount = 0;
} else if (testStarted) {
if (line.contains(TENURING_DISTRIBUTION)) {
// We found start of output emitted by -XX:+PrintTenuringDistribution
// If it is associated with "MaxTenuringThreshold" GC cycle, then it's
// time to report observed survivor usage ratio
gcCount++;
double survivorRatio = survivorOccupancy / (double) survivorSize;
if (gcCount == MAX_TENURING_THRESHOLD || gcCount == MAX_TENURING_THRESHOLD * 2) {
ratios.add(survivorRatio * 100.0);
testStarted = false;
}
survivorOccupancy = 0;
} else if (Pattern.matches(AGE_TABLE_ENTRY, line)) {
// Obtain survivor space usage from "total" age table log entry
Matcher m = ageTableEntry.matcher(line);
m.find();
survivorOccupancy = Long.valueOf(m.group(3));
} else if (line.contains(END_TEST)) {
// It is expected to find at least MaxTenuringThreshold GC events
// until test end
if (gcCount < MAX_TENURING_THRESHOLD) {
throw new RuntimeException("Observed " + gcCount + " GC events, "
+ "while it is expected to see at least "
+ MAX_TENURING_THRESHOLD);
}
testStarted = false;
}
}
}
return ratios;
}
public static class TargetSurvivorRatioVerifier {
static final WhiteBox wb = WhiteBox.getWhiteBox();
static final Unsafe unsafe = Utils.getUnsafe();
// Desired size of memory allocated at once
public static final int CHUNK_SIZE = 1024;
// Length of byte[] array that will have occupy CHUNK_SIZE bytes in heap
public static final int ARRAY_LENGTH = CHUNK_SIZE - Unsafe.ARRAY_BYTE_BASE_OFFSET;
public static void main(String args[]) throws Exception {
if (args.length != 1) {
throw new IllegalArgumentException("Expected 1 arg: <ratio>");
}
if (GCTypes.YoungGCType.getYoungGCType() == GCTypes.YoungGCType.PSNew) {
System.out.println(UNSUPPORTED_GC);
return;
}
int ratio = Integer.valueOf(args[0]);
long maxSurvivorSize = getMaxSurvivorSize();
System.out.println("Max survivor size: " + maxSurvivorSize);
allocateMemory(ratio - DELTA, maxSurvivorSize);
allocateMemory(ratio + DELTA, maxSurvivorSize);
}
/**
* Allocate (<b>ratio</b> * <b>maxSize</b> / 100) bytes of objects
* and force at least "MaxTenuringThreshold" minor GCs.
*
* @param ratio ratio used to calculate how many objects should be allocated
* @param maxSize estimated max survivor space size
*/
public static void allocateMemory(double ratio, long maxSize) throws Exception {
GarbageCollectorMXBean youngGCBean = GCTypes.YoungGCType.getYoungGCBean();
long garbageSize = (long) (maxSize * (ratio / 100.0));
int arrayLength = (int) (garbageSize / CHUNK_SIZE);
AllocationHelper allocator = new AllocationHelper(1, arrayLength, ARRAY_LENGTH, null);
System.out.println(START_TEST);
System.gc();
final long initialGcId = youngGCBean.getCollectionCount();
// allocate memory
allocator.allocateMemoryAndVerify();
// force minor GC
while (youngGCBean.getCollectionCount() <= initialGcId + MAX_TENURING_THRESHOLD * 2) {
byte b[] = new byte[ARRAY_LENGTH];
}
allocator.release();
System.out.println(END_TEST);
}
/**
* Estimate max survivor space size.
*
* For non-G1 GC returns value reported by MemoryPoolMXBean
* associated with survivor space.
* For G1 GC return max number of survivor regions * region size.
* Number if survivor regions estimated from MaxNewSize and SurvivorRatio.
*/
public static long getMaxSurvivorSize() {
if (GCTypes.YoungGCType.getYoungGCType() == GCTypes.YoungGCType.G1) {
int youngLength = (int) Math.max(MAX_NEW_SIZE / wb.g1RegionSize(), 1);
return (long) Math.ceil(youngLength / (double) SURVIVOR_RATIO) * wb.g1RegionSize();
} else {
return HeapRegionUsageTool.getSurvivorUsage().getMax();
}
}
}
}

View file

@ -0,0 +1,116 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.test.lib;
import java.util.LinkedList;
import java.util.concurrent.Callable;
/**
* Helper class which allocates memory.
*
* Typical usage:
* <pre>
* {@code
* AllocationHelper allocator = new AllocationHelper(MAX_ITERATIONS, ARRAY_LENGTH, CHUNK_SIZE,
* () -> (verifier()));
* // Allocate byte[CHUNK_SIZE] ARRAY_LENGTH times. Total allocated bytes will be CHUNK_SIZE * ARRAY_LENGTH + refs length.
* // Then invoke verifier and iterate MAX_ITERATIONS times.
* allocator.allocateMemoryAndVerify();
* }
* </pre>
*/
public final class AllocationHelper {
private final int arrayLength;
private final int maxIterations;
private final int chunkSize;
// garbageStorage is used to store link to garbage to prevent optimization.
private static Object garbageStorage;
private byte garbage[][];
private final Callable<?> verifierInstance;
/**
* Create an AllocationHelper with specified iteration count, array length, chunk size and verifier.
*
* @param maxIterations
* @param arrayLength
* @param chunkSize
* @param verifier - Callable instance which will be invoked after all allocation cycle. Can be null;
*/
public AllocationHelper(int maxIterations, int arrayLength, int chunkSize, Callable<?> verifier) {
if ((arrayLength <= 0) || (maxIterations <= 0) || (chunkSize <= 0)) {
throw new IllegalArgumentException("maxIterations, arrayLength and chunkSize should be greater then 0.");
}
this.arrayLength = arrayLength;
this.maxIterations = maxIterations;
this.chunkSize = chunkSize;
verifierInstance = verifier;
garbage = new byte[this.arrayLength][];
garbageStorage = garbage;
}
private void allocateMemoryOneIteration() {
for (int j = 0; j < arrayLength; j++) {
garbage[j] = new byte[chunkSize];
}
}
/**
* Allocate memory and invoke Verifier during all iteration.
*
* @throws java.lang.Exception
*/
public void allocateMemoryAndVerify() throws Exception {
for (int i = 0; i < maxIterations; i++) {
allocateMemoryOneIteration();
if (verifierInstance != null) {
verifierInstance.call();
}
}
}
/**
* The same as allocateMemoryAndVerify() but hides OOME
*
* @throws Exception
*/
public void allocateMemoryAndVerifyNoOOME() throws Exception {
try {
allocateMemoryAndVerify();
} catch (OutOfMemoryError e) {
// exit on OOME
}
}
/**
* Release link to allocated garbage to make it available for further GC
*/
public void release() {
if (garbage != null) {
garbage = null;
garbageStorage = null;
}
}
}

View file

@ -0,0 +1,107 @@
/*
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.test.lib;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
/**
* Utility class used by tests to get heap region usage.
*/
public final class HeapRegionUsageTool {
/**
* Get MemoryUsage from MemoryPoolMXBean which name matches passed string.
*
* @param name
* @return MemoryUsage
*/
private static MemoryUsage getUsage(String name){
for (MemoryPoolMXBean pool : ManagementFactory.getMemoryPoolMXBeans()) {
if (pool.getName().matches(name)) {
return pool.getUsage();
}
}
return null;
}
/**
* Get MemoryUsage of Eden space.
*
* @return MemoryUsage
*/
public static MemoryUsage getEdenUsage() {
return getUsage(".*Eden.*");
}
/**
* Get MemoryUsage of Survivor space.
*
* @return MemoryUsage
*/
public static MemoryUsage getSurvivorUsage() {
return getUsage(".*Survivor.*");
}
/**
* Get memory usage of Tenured space
*
* @return MemoryUsage
*/
public static MemoryUsage getOldUsage() {
return getUsage(".*(Old|Tenured).*");
}
/**
* Get heap usage.
*
* @return MemoryUsage
*/
public static MemoryUsage getHeapUsage() {
return ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
}
/**
* Helper function to align up.
*
* @param value
* @param alignment
* @return aligned value
*/
public static long alignUp(long value, long alignment) {
return (value + alignment - 1) & ~(alignment - 1);
}
/**
* Helper function to align down.
*
* @param value
* @param alignment
* @return aligned value
*/
public static long alignDown(long value, long alignment) {
return value & ~(alignment - 1);
}
}