8247373: ArraysSupport.newLength doc, test, and exception message
Reviewed-by: rriggs, psandoz, martin, prappo
This commit is contained in:
parent
96c43210d3
commit
f18c019287
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2015, 2020, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2015, 2021, 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
|
||||
@ -575,52 +575,83 @@ public class ArraysSupport {
|
||||
}
|
||||
|
||||
/**
|
||||
* The maximum length of array to allocate (unless necessary).
|
||||
* Some VMs reserve some header words in an array.
|
||||
* Attempts to allocate larger arrays may result in
|
||||
* {@code OutOfMemoryError: Requested array size exceeds VM limit}
|
||||
* A soft maximum array length imposed by array growth computations.
|
||||
* Some JVMs (such as HotSpot) have an implementation limit that will cause
|
||||
*
|
||||
* OutOfMemoryError("Requested array size exceeds VM limit")
|
||||
*
|
||||
* to be thrown if a request is made to allocate an array of some length near
|
||||
* Integer.MAX_VALUE, even if there is sufficient heap available. The actual
|
||||
* limit might depend on some JVM implementation-specific characteristics such
|
||||
* as the object header size. The soft maximum value is chosen conservatively so
|
||||
* as to be smaller than any implementation limit that is likely to be encountered.
|
||||
*/
|
||||
public static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
|
||||
public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
|
||||
|
||||
/**
|
||||
* Calculates a new array length given an array's current length, a preferred
|
||||
* growth value, and a minimum growth value. If the preferred growth value
|
||||
* is less than the minimum growth value, the minimum growth value is used in
|
||||
* its place. If the sum of the current length and the preferred growth
|
||||
* value does not exceed {@link #MAX_ARRAY_LENGTH}, that sum is returned.
|
||||
* If the sum of the current length and the minimum growth value does not
|
||||
* exceed {@code MAX_ARRAY_LENGTH}, then {@code MAX_ARRAY_LENGTH} is returned.
|
||||
* If the sum does not overflow an int, then {@code Integer.MAX_VALUE} is
|
||||
* returned. Otherwise, {@code OutOfMemoryError} is thrown.
|
||||
* Computes a new array length given an array's current length, a minimum growth
|
||||
* amount, and a preferred growth amount. The computation is done in an overflow-safe
|
||||
* fashion.
|
||||
*
|
||||
* @param oldLength current length of the array (must be non negative)
|
||||
* @param minGrowth minimum required growth of the array length (must be
|
||||
* positive)
|
||||
* @param prefGrowth preferred growth of the array length (ignored, if less
|
||||
* then {@code minGrowth})
|
||||
* @return the new length of the array
|
||||
* @throws OutOfMemoryError if increasing {@code oldLength} by
|
||||
* {@code minGrowth} overflows.
|
||||
* This method is used by objects that contain an array that might need to be grown
|
||||
* in order to fulfill some immediate need (the minimum growth amount) but would also
|
||||
* like to request more space (the preferred growth amount) in order to accommodate
|
||||
* potential future needs. The returned length is usually clamped at the soft maximum
|
||||
* length in order to avoid hitting the JVM implementation limit. However, the soft
|
||||
* maximum will be exceeded if the minimum growth amount requires it.
|
||||
*
|
||||
* If the preferred growth amount is less than the minimum growth amount, the
|
||||
* minimum growth amount is used as the preferred growth amount.
|
||||
*
|
||||
* The preferred length is determined by adding the preferred growth amount to the
|
||||
* current length. If the preferred length does not exceed the soft maximum length
|
||||
* (SOFT_MAX_ARRAY_LENGTH) then the preferred length is returned.
|
||||
*
|
||||
* If the preferred length exceeds the soft maximum, we use the minimum growth
|
||||
* amount. The minimum required length is determined by adding the minimum growth
|
||||
* amount to the current length. If the minimum required length exceeds Integer.MAX_VALUE,
|
||||
* then this method throws OutOfMemoryError. Otherwise, this method returns the greater of
|
||||
* the soft maximum or the minimum required length.
|
||||
*
|
||||
* Note that this method does not do any array allocation itself; it only does array
|
||||
* length growth computations. However, it will throw OutOfMemoryError as noted above.
|
||||
*
|
||||
* Note also that this method cannot detect the JVM's implementation limit, and it
|
||||
* may compute and return a length value up to and including Integer.MAX_VALUE that
|
||||
* might exceed the JVM's implementation limit. In that case, the caller will likely
|
||||
* attempt an array allocation with that length and encounter an OutOfMemoryError.
|
||||
* Of course, regardless of the length value returned from this method, the caller
|
||||
* may encounter OutOfMemoryError if there is insufficient heap to fulfill the request.
|
||||
*
|
||||
* @param oldLength current length of the array (must be nonnegative)
|
||||
* @param minGrowth minimum required growth amount (must be positive)
|
||||
* @param prefGrowth preferred growth amount
|
||||
* @return the new array length
|
||||
* @throws OutOfMemoryError if the new length would exceed Integer.MAX_VALUE
|
||||
*/
|
||||
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
|
||||
// preconditions not checked because of inlining
|
||||
// assert oldLength >= 0
|
||||
// assert minGrowth > 0
|
||||
|
||||
int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
|
||||
if (newLength - MAX_ARRAY_LENGTH <= 0) {
|
||||
return newLength;
|
||||
int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
|
||||
if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
|
||||
return prefLength;
|
||||
} else {
|
||||
// put code cold in a separate method
|
||||
return hugeLength(oldLength, minGrowth);
|
||||
}
|
||||
return hugeLength(oldLength, minGrowth);
|
||||
}
|
||||
|
||||
private static int hugeLength(int oldLength, int minGrowth) {
|
||||
int minLength = oldLength + minGrowth;
|
||||
if (minLength < 0) { // overflow
|
||||
throw new OutOfMemoryError("Required array length too large");
|
||||
throw new OutOfMemoryError(
|
||||
"Required array length " + oldLength + " + " + minGrowth + " is too large");
|
||||
} else if (minLength <= SOFT_MAX_ARRAY_LENGTH) {
|
||||
return SOFT_MAX_ARRAY_LENGTH;
|
||||
} else {
|
||||
return minLength;
|
||||
}
|
||||
if (minLength <= MAX_ARRAY_LENGTH) {
|
||||
return MAX_ARRAY_LENGTH;
|
||||
}
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.Stream;
|
||||
import org.testng.annotations.Test;
|
||||
import static jdk.internal.util.ArraysSupport.MAX_ARRAY_LENGTH;
|
||||
import static jdk.internal.util.ArraysSupport.SOFT_MAX_ARRAY_LENGTH;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
@ -172,7 +172,7 @@ public class MergeTest {
|
||||
}
|
||||
|
||||
public void OOM() {
|
||||
String maxString = "*".repeat(MAX_ARRAY_LENGTH);
|
||||
String maxString = "*".repeat(SOFT_MAX_ARRAY_LENGTH);
|
||||
|
||||
try {
|
||||
StringJoiner sj1 = new StringJoiner("", "", "");
|
||||
|
@ -32,7 +32,7 @@
|
||||
import java.util.ArrayList;
|
||||
import java.util.StringJoiner;
|
||||
import org.testng.annotations.Test;
|
||||
import static jdk.internal.util.ArraysSupport.MAX_ARRAY_LENGTH;
|
||||
import static jdk.internal.util.ArraysSupport.SOFT_MAX_ARRAY_LENGTH;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
@ -49,7 +49,7 @@ public class StringJoinerTest {
|
||||
private static final String FOUR = "Four";
|
||||
private static final String FIVE = "Five";
|
||||
private static final String DASH = "-";
|
||||
private static final String MAX_STRING = "*".repeat(MAX_ARRAY_LENGTH);
|
||||
private static final String MAX_STRING = "*".repeat(SOFT_MAX_ARRAY_LENGTH);
|
||||
|
||||
public void addAddAll() {
|
||||
StringJoiner sj = new StringJoiner(DASH, "{", "}");
|
||||
|
102
test/jdk/jdk/internal/util/ArraysSupport/NewLength.java
Normal file
102
test/jdk/jdk/internal/util/ArraysSupport/NewLength.java
Normal file
@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2021, 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
|
||||
* @bug 8247373
|
||||
* @modules java.base/jdk.internal.util
|
||||
* @run testng NewLength
|
||||
* @summary Test edge cases of ArraysSupport.newLength
|
||||
*/
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.fail;
|
||||
|
||||
import jdk.internal.util.ArraysSupport;
|
||||
|
||||
public class NewLength {
|
||||
static final int IMAX = Integer.MAX_VALUE;
|
||||
static final int SOFT = ArraysSupport.SOFT_MAX_ARRAY_LENGTH;
|
||||
|
||||
// Data that is expected to return a valid value.
|
||||
|
||||
@DataProvider(name = "valid")
|
||||
public Object[][] validProvider() {
|
||||
return new Object[][] {
|
||||
// old min pref expected
|
||||
{ 0, 1, 0, 1 },
|
||||
{ 0, 1, 2, 2 },
|
||||
{ 0, 2, 1, 2 },
|
||||
{ 0, 1, SOFT-1, SOFT-1 },
|
||||
{ 0, 1, SOFT, SOFT },
|
||||
{ 0, 1, SOFT+1, SOFT },
|
||||
{ 0, 1, IMAX, SOFT },
|
||||
{ 0, SOFT-1, IMAX, SOFT },
|
||||
{ 0, SOFT, IMAX, SOFT },
|
||||
{ 0, SOFT+1, IMAX, SOFT+1 },
|
||||
{ SOFT-2, 1, 2, SOFT },
|
||||
{ SOFT-1, 1, 2, SOFT },
|
||||
{ SOFT, 1, 2, SOFT+1 },
|
||||
{ SOFT+1, 1, 2, SOFT+2 },
|
||||
{ IMAX-2, 1, 2, IMAX-1 },
|
||||
{ IMAX-1, 1, 2, IMAX },
|
||||
{ SOFT-2, 1, IMAX, SOFT },
|
||||
{ SOFT-1, 1, IMAX, SOFT },
|
||||
{ SOFT, 1, IMAX, SOFT+1 },
|
||||
{ SOFT+1, 1, IMAX, SOFT+2 },
|
||||
{ IMAX-2, 1, IMAX, IMAX-1 },
|
||||
{ IMAX-1, 1, IMAX, IMAX }
|
||||
};
|
||||
}
|
||||
|
||||
// Data that should provoke an OutOfMemoryError
|
||||
|
||||
@DataProvider(name = "error")
|
||||
public Object[][] errorProvider() {
|
||||
return new Object[][] {
|
||||
// old min pref
|
||||
{ 1, IMAX, IMAX },
|
||||
{ SOFT, IMAX, 0 },
|
||||
{ SOFT, IMAX, IMAX },
|
||||
{ IMAX-1, 2, 0 },
|
||||
{ IMAX, 1, 0 },
|
||||
{ IMAX, IMAX, 0 },
|
||||
{ IMAX, IMAX, IMAX }
|
||||
};
|
||||
}
|
||||
|
||||
@Test(dataProvider = "valid")
|
||||
public void valid(int old, int min, int pref, int expected) {
|
||||
assertEquals(ArraysSupport.newLength(old, min, pref), expected);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "error")
|
||||
public void error(int old, int min, int pref) {
|
||||
try {
|
||||
int r = ArraysSupport.newLength(old, min, pref);
|
||||
fail("expected OutOfMemoryError, got normal return value of " + r);
|
||||
} catch (OutOfMemoryError success) { }
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user