8223593: Refactor code for reallocating storage
authorigerasim
Tue, 21 May 2019 18:40:29 -0700
changeset 54971 4285b4d13471
parent 54970 76d3d96a8bc2
child 54972 f98a0ab24887
8223593: Refactor code for reallocating storage Reviewed-by: prappo, plevart, rriggs, smarks
src/java.base/share/classes/java/io/BufferedInputStream.java
src/java.base/share/classes/java/io/ByteArrayOutputStream.java
src/java.base/share/classes/java/lang/StringLatin1.java
src/java.base/share/classes/java/lang/StringUTF16.java
src/java.base/share/classes/java/nio/file/Files.java
src/java.base/share/classes/java/util/AbstractCollection.java
src/java.base/share/classes/java/util/ArrayList.java
src/java.base/share/classes/java/util/PriorityQueue.java
src/java.base/share/classes/java/util/Vector.java
src/java.base/share/classes/java/util/regex/Pattern.java
src/java.base/share/classes/jdk/internal/util/ArraysSupport.java
--- a/src/java.base/share/classes/java/io/BufferedInputStream.java	Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/io/BufferedInputStream.java	Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2019, 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
@@ -26,6 +26,7 @@
 package java.io;
 
 import jdk.internal.misc.Unsafe;
+import jdk.internal.util.ArraysSupport;
 
 /**
  * A <code>BufferedInputStream</code> adds
@@ -54,14 +55,6 @@
     private static int DEFAULT_BUFFER_SIZE = 8192;
 
     /**
-     * The maximum size of array to allocate.
-     * Some VMs reserve some header words in an array.
-     * Attempts to allocate larger arrays may result in
-     * OutOfMemoryError: Requested array size exceeds VM limit
-     */
-    private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
-
-    /**
      * As this class is used early during bootstrap, it's motivated to use
      * Unsafe.compareAndSetObject instead of AtomicReferenceFieldUpdater
      * (or VarHandles) to reduce dependencies and improve startup time.
@@ -220,7 +213,7 @@
         byte[] buffer = getBufIfOpen();
         if (markpos < 0)
             pos = 0;            /* no mark: throw away the buffer */
-        else if (pos >= buffer.length)  /* no room left in buffer */
+        else if (pos >= buffer.length) { /* no room left in buffer */
             if (markpos > 0) {  /* can throw away early part of the buffer */
                 int sz = pos - markpos;
                 System.arraycopy(buffer, markpos, buffer, 0, sz);
@@ -229,11 +222,10 @@
             } else if (buffer.length >= marklimit) {
                 markpos = -1;   /* buffer got too big, invalidate mark */
                 pos = 0;        /* drop buffer contents */
-            } else if (buffer.length >= MAX_BUFFER_SIZE) {
-                throw new OutOfMemoryError("Required array size too large");
             } else {            /* grow buffer */
-                int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
-                        pos * 2 : MAX_BUFFER_SIZE;
+                int nsz = ArraysSupport.newLength(pos,
+                        1,  /* minimum growth */
+                        pos /* preferred growth */);
                 if (nsz > marklimit)
                     nsz = marklimit;
                 byte[] nbuf = new byte[nsz];
@@ -248,6 +240,7 @@
                 }
                 buffer = nbuf;
             }
+        }
         count = pos;
         int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
         if (n > 0)
--- a/src/java.base/share/classes/java/io/ByteArrayOutputStream.java	Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/io/ByteArrayOutputStream.java	Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2019, 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
@@ -29,6 +29,8 @@
 import java.util.Arrays;
 import java.util.Objects;
 
+import jdk.internal.util.ArraysSupport;
+
 /**
  * This class implements an output stream in which the data is
  * written into a byte array. The buffer automatically grows as data
@@ -84,48 +86,20 @@
      * at least the number of elements specified by the minimum
      * capacity argument.
      *
-     * @param  minCapacity the desired minimum capacity
-     * @throws OutOfMemoryError if {@code minCapacity < 0}.  This is
-     * interpreted as a request for the unsatisfiably large capacity
+     * @param  minCapacity the desired minimum capacity.
+     * @throws OutOfMemoryError if {@code minCapacity < 0} and
+     * {@code minCapacity - buf.length > 0}.  This is interpreted as a
+     * request for the unsatisfiably large capacity.
      * {@code (long) Integer.MAX_VALUE + (minCapacity - Integer.MAX_VALUE)}.
      */
     private void ensureCapacity(int minCapacity) {
         // overflow-conscious code
-        if (minCapacity - buf.length > 0)
-            grow(minCapacity);
-    }
-
-    /**
-     * The maximum size of array to allocate.
-     * Some VMs reserve some header words in an array.
-     * Attempts to allocate larger arrays may result in
-     * OutOfMemoryError: Requested array size exceeds VM limit
-     */
-    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
-    /**
-     * Increases the capacity to ensure that it can hold at least the
-     * number of elements specified by the minimum capacity argument.
-     *
-     * @param minCapacity the desired minimum capacity
-     */
-    private void grow(int minCapacity) {
-        // overflow-conscious code
         int oldCapacity = buf.length;
-        int newCapacity = oldCapacity << 1;
-        if (newCapacity - minCapacity < 0)
-            newCapacity = minCapacity;
-        if (newCapacity - MAX_ARRAY_SIZE > 0)
-            newCapacity = hugeCapacity(minCapacity);
-        buf = Arrays.copyOf(buf, newCapacity);
-    }
-
-    private static int hugeCapacity(int minCapacity) {
-        if (minCapacity < 0) // overflow
-            throw new OutOfMemoryError();
-        return (minCapacity > MAX_ARRAY_SIZE) ?
-            Integer.MAX_VALUE :
-            MAX_ARRAY_SIZE;
+        int minGrowth = minCapacity - oldCapacity;
+        if (minGrowth > 0) {
+            buf = Arrays.copyOf(buf, ArraysSupport.newLength(oldCapacity,
+                    minGrowth, oldCapacity /* preferred growth */));
+        }
     }
 
     /**
--- a/src/java.base/share/classes/java/lang/StringLatin1.java	Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/StringLatin1.java	Tue May 21 18:40:29 2019 -0700
@@ -35,6 +35,7 @@
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 import jdk.internal.HotSpotIntrinsicCandidate;
+import jdk.internal.util.ArraysSupport;
 
 import static java.lang.String.LATIN1;
 import static java.lang.String.UTF16;
@@ -42,14 +43,6 @@
 
 final class StringLatin1 {
 
-    /**
-     * The maximum size of array to allocate (unless necessary).
-     * Some VMs reserve some header words in an array.
-     * Attempts to allocate larger arrays may result in
-     * OutOfMemoryError: Requested array size exceeds VM limit
-     */
-    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
     public static char charAt(byte[] value, int index) {
         if (index < 0 || index >= value.length) {
             throw new StringIndexOutOfBoundsException(index);
@@ -353,15 +346,7 @@
         i += targLen;
         while ((j = indexOf(value, valLen, targ, targLen, i)) > 0) {
             if (++p == pos.length) {
-                int cap = p + (p >> 1);
-                // overflow-conscious code
-                if (cap - MAX_ARRAY_SIZE > 0) {
-                    if (p == MAX_ARRAY_SIZE) {
-                        throw new OutOfMemoryError();
-                    }
-                    cap = MAX_ARRAY_SIZE;
-                }
-                pos = Arrays.copyOf(pos, cap);
+                pos = Arrays.copyOf(pos, ArraysSupport.newLength(p, 1, p >> 1));
             }
             pos[p] = j;
             i = j + targLen;
--- a/src/java.base/share/classes/java/lang/StringUTF16.java	Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/lang/StringUTF16.java	Tue May 21 18:40:29 2019 -0700
@@ -33,6 +33,7 @@
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 import jdk.internal.HotSpotIntrinsicCandidate;
+import jdk.internal.util.ArraysSupport;
 import jdk.internal.vm.annotation.ForceInline;
 import jdk.internal.vm.annotation.DontInline;
 
@@ -649,15 +650,7 @@
                             : indexOf(value, valLen, targ, targLen, i))) > 0)
         {
             if (++p == pos.length) {
-                int cap = p + (p >> 1);
-                // overflow-conscious code
-                if (cap - MAX_ARRAY_SIZE > 0) {
-                    if (p == MAX_ARRAY_SIZE) {
-                        throw new OutOfMemoryError();
-                    }
-                    cap = MAX_ARRAY_SIZE;
-                }
-                pos = Arrays.copyOf(pos, cap);
+                pos = Arrays.copyOf(pos, ArraysSupport.newLength(p, 1, p >> 1));
             }
             pos[p] = j;
             i = j + targLen;
@@ -1554,15 +1547,6 @@
 
     static final int MAX_LENGTH = Integer.MAX_VALUE >> 1;
 
-
-    /**
-     * The maximum size of array to allocate (unless necessary).
-     * Some VMs reserve some header words in an array.
-     * Attempts to allocate larger arrays may result in
-     * OutOfMemoryError: Requested array size exceeds VM limit
-     */
-    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
     // Used by trusted callers.  Assumes all necessary bounds checks have
     // been done by the caller.
 
--- a/src/java.base/share/classes/java/nio/file/Files.java	Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/nio/file/Files.java	Tue May 21 18:40:29 2019 -0700
@@ -77,6 +77,7 @@
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
+import jdk.internal.util.ArraysSupport;
 import sun.nio.ch.FileChannelImpl;
 import sun.nio.fs.AbstractFileSystemProvider;
 
@@ -3196,14 +3197,6 @@
         }
     }
 
-    /**
-     * The maximum size of array to allocate.
-     * Some VMs reserve some header words in an array.
-     * Attempts to allocate larger arrays may result in
-     * OutOfMemoryError: Requested array size exceeds VM limit
-     */
-    private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
-
     private static final jdk.internal.access.JavaLangAccess JLA =
             jdk.internal.access.SharedSecrets.getJavaLangAccess();
 
@@ -3240,13 +3233,10 @@
                 break;
 
             // one more byte was read; need to allocate a larger buffer
-            if (capacity <= MAX_BUFFER_SIZE - capacity) {
-                capacity = Math.max(capacity << 1, BUFFER_SIZE);
-            } else {
-                if (capacity == MAX_BUFFER_SIZE)
-                    throw new OutOfMemoryError("Required array size too large");
-                capacity = MAX_BUFFER_SIZE;
-            }
+            capacity = Math.max(ArraysSupport.newLength(capacity,
+                                                        1,       /* minimum growth */
+                                                        capacity /* preferred growth */),
+                                BUFFER_SIZE);
             buf = Arrays.copyOf(buf, capacity);
             buf[nread++] = (byte)n;
         }
@@ -3283,7 +3273,7 @@
             if (sbc instanceof FileChannelImpl)
                 ((FileChannelImpl) sbc).setUninterruptible();
             long size = sbc.size();
-            if (size > (long) MAX_BUFFER_SIZE)
+            if (size > (long) Integer.MAX_VALUE)
                 throw new OutOfMemoryError("Required array size too large");
             return read(in, (int)size);
         }
--- a/src/java.base/share/classes/java/util/AbstractCollection.java	Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/util/AbstractCollection.java	Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -25,6 +25,8 @@
 
 package java.util;
 
+import jdk.internal.util.ArraysSupport;
+
 /**
  * This class provides a skeletal implementation of the {@code Collection}
  * interface, to minimize the effort required to implement this interface. <p>
@@ -204,14 +206,6 @@
     }
 
     /**
-     * The maximum size of array to allocate.
-     * Some VMs reserve some header words in an array.
-     * Attempts to allocate larger arrays may result in
-     * OutOfMemoryError: Requested array size exceeds VM limit
-     */
-    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
-    /**
      * Reallocates the array being used within toArray when the iterator
      * returned more elements than expected, and finishes filling it from
      * the iterator.
@@ -223,29 +217,19 @@
      */
     @SuppressWarnings("unchecked")
     private static <T> T[] finishToArray(T[] r, Iterator<?> it) {
-        int i = r.length;
+        int len = r.length;
+        int i = len;
         while (it.hasNext()) {
-            int cap = r.length;
-            if (i == cap) {
-                int newCap = cap + (cap >> 1) + 1;
-                // overflow-conscious code
-                if (newCap - MAX_ARRAY_SIZE > 0)
-                    newCap = hugeCapacity(cap + 1);
-                r = Arrays.copyOf(r, newCap);
+            if (i == len) {
+                len = ArraysSupport.newLength(len,
+                        1,             /* minimum growth */
+                        (len >> 1) + 1 /* preferred growth */);
+                r = Arrays.copyOf(r, len);
             }
             r[i++] = (T)it.next();
         }
         // trim if overallocated
-        return (i == r.length) ? r : Arrays.copyOf(r, i);
-    }
-
-    private static int hugeCapacity(int minCapacity) {
-        if (minCapacity < 0) // overflow
-            throw new OutOfMemoryError
-                ("Required array size too large");
-        return (minCapacity > MAX_ARRAY_SIZE) ?
-            Integer.MAX_VALUE :
-            MAX_ARRAY_SIZE;
+        return (i == len) ? r : Arrays.copyOf(r, i);
     }
 
     // Modification Operations
--- a/src/java.base/share/classes/java/util/ArrayList.java	Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/util/ArrayList.java	Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1997, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1997, 2019, 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
@@ -29,6 +29,7 @@
 import java.util.function.Predicate;
 import java.util.function.UnaryOperator;
 import jdk.internal.access.SharedSecrets;
+import jdk.internal.util.ArraysSupport;
 
 /**
  * Resizable-array implementation of the {@code List} interface.  Implements
@@ -219,14 +220,6 @@
     }
 
     /**
-     * The maximum size of array to allocate (unless necessary).
-     * Some VMs reserve some header words in an array.
-     * Attempts to allocate larger arrays may result in
-     * OutOfMemoryError: Requested array size exceeds VM limit
-     */
-    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
-    /**
      * Increases the capacity to ensure that it can hold at least the
      * number of elements specified by the minimum capacity argument.
      *
@@ -234,8 +227,15 @@
      * @throws OutOfMemoryError if minCapacity is less than zero
      */
     private Object[] grow(int minCapacity) {
-        return elementData = Arrays.copyOf(elementData,
-                                           newCapacity(minCapacity));
+        int oldCapacity = elementData.length;
+        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+            int newCapacity = ArraysSupport.newLength(oldCapacity,
+                    minCapacity - oldCapacity, /* minimum growth */
+                    oldCapacity >> 1           /* preferred growth */);
+            return elementData = Arrays.copyOf(elementData, newCapacity);
+        } else {
+            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
+        }
     }
 
     private Object[] grow() {
@@ -243,39 +243,6 @@
     }
 
     /**
-     * Returns a capacity at least as large as the given minimum capacity.
-     * Returns the current capacity increased by 50% if that suffices.
-     * Will not return a capacity greater than MAX_ARRAY_SIZE unless
-     * the given minimum capacity is greater than MAX_ARRAY_SIZE.
-     *
-     * @param minCapacity the desired minimum capacity
-     * @throws OutOfMemoryError if minCapacity is less than zero
-     */
-    private int newCapacity(int minCapacity) {
-        // overflow-conscious code
-        int oldCapacity = elementData.length;
-        int newCapacity = oldCapacity + (oldCapacity >> 1);
-        if (newCapacity - minCapacity <= 0) {
-            if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
-                return Math.max(DEFAULT_CAPACITY, minCapacity);
-            if (minCapacity < 0) // overflow
-                throw new OutOfMemoryError();
-            return minCapacity;
-        }
-        return (newCapacity - MAX_ARRAY_SIZE <= 0)
-            ? newCapacity
-            : hugeCapacity(minCapacity);
-    }
-
-    private static int hugeCapacity(int minCapacity) {
-        if (minCapacity < 0) // overflow
-            throw new OutOfMemoryError();
-        return (minCapacity > MAX_ARRAY_SIZE)
-            ? Integer.MAX_VALUE
-            : MAX_ARRAY_SIZE;
-    }
-
-    /**
      * Returns the number of elements in this list.
      *
      * @return the number of elements in this list
--- a/src/java.base/share/classes/java/util/PriorityQueue.java	Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/util/PriorityQueue.java	Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2003, 2019, 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
@@ -28,6 +28,7 @@
 import java.util.function.Consumer;
 import java.util.function.Predicate;
 import jdk.internal.access.SharedSecrets;
+import jdk.internal.util.ArraysSupport;
 
 /**
  * An unbounded priority {@linkplain Queue queue} based on a priority heap.
@@ -282,14 +283,6 @@
     }
 
     /**
-     * The maximum size of array to allocate.
-     * Some VMs reserve some header words in an array.
-     * Attempts to allocate larger arrays may result in
-     * OutOfMemoryError: Requested array size exceeds VM limit
-     */
-    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
-    /**
      * Increases the capacity of the array.
      *
      * @param minCapacity the desired minimum capacity
@@ -297,23 +290,13 @@
     private void grow(int minCapacity) {
         int oldCapacity = queue.length;
         // Double size if small; else grow by 50%
-        int newCapacity = oldCapacity + ((oldCapacity < 64) ?
-                                         (oldCapacity + 2) :
-                                         (oldCapacity >> 1));
-        // overflow-conscious code
-        if (newCapacity - MAX_ARRAY_SIZE > 0)
-            newCapacity = hugeCapacity(minCapacity);
+        int newCapacity = ArraysSupport.newLength(oldCapacity,
+                minCapacity - oldCapacity, /* minimum growth */
+                oldCapacity < 64 ? oldCapacity + 2 : oldCapacity >> 1
+                                           /* preferred growth */);
         queue = Arrays.copyOf(queue, newCapacity);
     }
 
-    private static int hugeCapacity(int minCapacity) {
-        if (minCapacity < 0) // overflow
-            throw new OutOfMemoryError();
-        return (minCapacity > MAX_ARRAY_SIZE) ?
-            Integer.MAX_VALUE :
-            MAX_ARRAY_SIZE;
-    }
-
     /**
      * Inserts the specified element into this priority queue.
      *
--- a/src/java.base/share/classes/java/util/Vector.java	Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/util/Vector.java	Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 1994, 2018, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1994, 2019, 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
@@ -32,6 +32,8 @@
 import java.util.function.Predicate;
 import java.util.function.UnaryOperator;
 
+import jdk.internal.util.ArraysSupport;
+
 /**
  * The {@code Vector} class implements a growable array of
  * objects. Like an array, it contains components that can be
@@ -242,14 +244,6 @@
     }
 
     /**
-     * The maximum size of array to allocate (unless necessary).
-     * Some VMs reserve some header words in an array.
-     * Attempts to allocate larger arrays may result in
-     * OutOfMemoryError: Requested array size exceeds VM limit
-     */
-    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
-
-    /**
      * Increases the capacity to ensure that it can hold at least the
      * number of elements specified by the minimum capacity argument.
      *
@@ -257,8 +251,12 @@
      * @throws OutOfMemoryError if minCapacity is less than zero
      */
     private Object[] grow(int minCapacity) {
-        return elementData = Arrays.copyOf(elementData,
-                                           newCapacity(minCapacity));
+        int oldCapacity = elementData.length;
+        int newCapacity = ArraysSupport.newLength(oldCapacity,
+                minCapacity - oldCapacity, /* minimum growth */
+                capacityIncrement > 0 ? capacityIncrement : oldCapacity
+                                           /* preferred growth */);
+        return elementData = Arrays.copyOf(elementData, newCapacity);
     }
 
     private Object[] grow() {
@@ -266,37 +264,6 @@
     }
 
     /**
-     * Returns a capacity at least as large as the given minimum capacity.
-     * Will not return a capacity greater than MAX_ARRAY_SIZE unless
-     * the given minimum capacity is greater than MAX_ARRAY_SIZE.
-     *
-     * @param minCapacity the desired minimum capacity
-     * @throws OutOfMemoryError if minCapacity is less than zero
-     */
-    private int newCapacity(int minCapacity) {
-        // overflow-conscious code
-        int oldCapacity = elementData.length;
-        int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
-                                         capacityIncrement : oldCapacity);
-        if (newCapacity - minCapacity <= 0) {
-            if (minCapacity < 0) // overflow
-                throw new OutOfMemoryError();
-            return minCapacity;
-        }
-        return (newCapacity - MAX_ARRAY_SIZE <= 0)
-            ? newCapacity
-            : hugeCapacity(minCapacity);
-    }
-
-    private static int hugeCapacity(int minCapacity) {
-        if (minCapacity < 0) // overflow
-            throw new OutOfMemoryError();
-        return (minCapacity > MAX_ARRAY_SIZE) ?
-            Integer.MAX_VALUE :
-            MAX_ARRAY_SIZE;
-    }
-
-    /**
      * Sets the size of this vector. If the new size is greater than the
      * current size, new {@code null} items are added to the end of
      * the vector. If the new size is less than the current size, all
--- a/src/java.base/share/classes/java/util/regex/Pattern.java	Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/java/util/regex/Pattern.java	Tue May 21 18:40:29 2019 -0700
@@ -43,6 +43,7 @@
 import java.util.stream.Stream;
 import java.util.stream.StreamSupport;
 
+import jdk.internal.util.ArraysSupport;
 
 /**
  * A compiled representation of a regular expression.
@@ -2315,13 +2316,15 @@
         }
     }
 
-    private void append(int ch, int len) {
-        if (len >= buffer.length) {
-            int[] tmp = new int[len+len];
-            System.arraycopy(buffer, 0, tmp, 0, len);
-            buffer = tmp;
-        }
-        buffer[len] = ch;
+    private void append(int ch, int index) {
+        int len = buffer.length;
+        if (index - len >= 0) {
+            len = ArraysSupport.newLength(len,
+                    1 + index - len, /* minimum growth */
+                    len              /* preferred growth */);
+            buffer = Arrays.copyOf(buffer, len);
+        }
+        buffer[index] = ch;
     }
 
     /**
--- a/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java	Tue May 21 19:19:44 2019 -0400
+++ b/src/java.base/share/classes/jdk/internal/util/ArraysSupport.java	Tue May 21 18:40:29 2019 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2015, 2017, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2015, 2019, 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
@@ -28,7 +28,9 @@
 import jdk.internal.misc.Unsafe;
 
 /**
- * Utility methods to find a mismatch between two primitive arrays.
+ * Utility methods to work with arrays.  This includes a set of methods
+ * to find a mismatch between two primitive arrays.  Also included is
+ * a method to calculate the new length of an array to be reallocated.
  *
  * <p>Array equality and lexicographical comparison can be built on top of
  * array mismatch functionality.
@@ -571,4 +573,54 @@
 
         return -1;
     }
+
+    /**
+     * 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}
+     */
+    public static final int 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.
+     *
+     * @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.
+     */
+    public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
+        // assert oldLength >= 0
+        // assert minGrowth > 0
+
+        int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
+        if (newLength - MAX_ARRAY_LENGTH <= 0) {
+            return newLength;
+        }
+        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");
+        }
+        if (minLength <= MAX_ARRAY_LENGTH) {
+            return MAX_ARRAY_LENGTH;
+        }
+        return Integer.MAX_VALUE;
+    }
 }