Add SizeTrackingAppendOnlyBuffer and tests

This buffer is supported by an underlying PrimitiveVector. It is to be
used for unrolling partitions, so we can efficiently check the size of
the in-memory buffer periodically.

Note that the underlying buffer cannot be an existing implementation of
a mutable Scala or Java collection. This is because we need to be exposed
to when the underlying array is resized. Otherwise, size estimation may
not be accurate.
Change BlockManager interface to use Arrays

... rather than ArrayBuffer. We only ever iterate through it anyway,
so there is really no reason for it to be a mutable buffer of any sort.
This change is introduced so that we can eventually directly pass our
SizeTrackingAppendOnlyBuffer's underlying array to BlockManager, instead
of having to awkwardly make it an ArrayBuffer first.
Fix CacheManagerSuite to use Array

In addition, avoid using EasyMock for one of our tests, which expects
BlockManager#put to be called with an Array[Any] parameter. Even with
all the EasyMock matchers, it is impossible to match an Array[Any]
because of a combination of the following: (1) the fact that Arrays
are not covariant, (2) EasyMock provides `aryEq` matchers for all the
Java primitive types, which conflict with Any, and (3) EasyMock's super
general matchers like `anyObject` or `isA` also do not match for some
reason.
Prevent OOM if a single RDD partition is too large

The idea is to always use at least a fixed amount of memory (M bytes)
to unroll RDD partitions. This space is not reserved, but instead
allocated dynamically by dropping existing blocks when necessary.

We maintain this buffer as a global quantity shared across all cores.
The way we synchronize the usage of this buffer is very similar to the
way we share memory across all threads for shuffle aggregation. In
particular, each thread cautiously requests for more memory
periodically,
and if there is not enough global memory to grant the request, the
thread concedes and spills. However, this relies on the accuracy of
size estimation, which is not guaranteed. Therefore, as in the shuffle
case, we need an equivalent spark.storage.safetyFraction in case size
estimation is slightly off.

We expose M to the user as spark.storage.bufferFraction, with a default
value of 0.2.