Skip to content

Issues with ArrayBuffer (and probably other collections) sizing/resizing #12464

Open
@NthPortal

Description

@NthPortal
  • calls to ArrayBuffer#ensureSize(this.size + n) without ensuring that the sum doesn't overflow Int.MaxValue.
  • ArrayBuffer.ensureSize (and probably ArrayBuffer.downsize as well) checks bounds poorly.
    • the current number of elements (before resizing) is named end, which is confusing.
    • we throw an exception iff the current size (not the size that we're ensuring) is exactly Int.MaxValue, regardless of whether or not the size we're ensuring is or exceeds Int.MaxValue. this is inconsistent at best.
      • the exception message is s"Collections can not have more than ${Int.MaxValue} elements", but we actually checked if the (wrong) size was Int.MaxValue, not if it exceeded it.
      • the parameter n for the size we're ensuring is an Int (as is end), and therefore by definition cannot ever exceed Int.MaxValue; consequently we cannot actually detect overflow anyway.
    • if the new array size (which attempts to be a power of 2, but may not be—see below) exceeds Int.MaxValue, we drop it down to Int.MaxValue. hotspot allows a maximum array size of Int.MaxValue - 2, and thus throw an OutOfMemoryError even if you had enough memory for the array. this is poor behaviour for the API if the actual size we're trying to ensure is less than the VM array size limit.
      scala> new Array[Int](Int.MaxValue)
      java.lang.OutOfMemoryError: Requested array size exceeds VM limit
        ... 32 elided
      
      scala> new Array[Int](Int.MaxValue - 1)
      java.lang.OutOfMemoryError: Requested array size exceeds VM limit
      
      scala> new Array[Int](Int.MaxValue - 2)    // my REPL doesn't have enough memory for this
      java.lang.OutOfMemoryError: Java heap space
  • if new ArrayBuffer(initialSize) is called with a size that is—or ArrayBuffer.from is called with a collection with a knownSize that is—greater than ArrayBuffer.DefaultInitialSize but not a power of 2, the buffer will be backed by an array that's not a power of 2. additionally calls to both ArrayBuffer.ensureSize and ArrayBuffer.downsize are likely to resize the backing array to non-power-of-2 sizes.

there is also some argument to be made that:

  • ArrayBuffer#trimToSize() should allow the backing array to have non-power-of-2-sizes (perhaps even less than ArrayBuffer.DefaultInitialValue?)
  • ArrayBuffer#sizeHint(Int) should perhaps allow the backing array to have non-power-of-2-sizes as well? (it also could use a better name that's not like the Builder API since it is no longer a Builder as of 2.13, but that's not fixable soon)

however, I don't think those necessarily need to be part of the ticket (they're just reasonable to change if we're already in the area making fixes)

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions