Merge sort could work 10 times faster
Some pointers:


You keep adding 2 numbers here: startingIndex + i, that could probably be avoided if you went for(int i = startIndex; i &lt; lastIndex; i++){ where lastIndex would be startIndex + count
Since ++ happens post evaluation you could simply do buffer[startingIndex + i] = source[index1++];, it does not have to be a separate statement
Short circuit logic : if index2 &gt;= maxIndex2, then you should probably have a  System.arraycopy copy that fills the rest into index1 and get out. You could the same for when index1 &gt; maxIndex1 but obviously you would fill into index2 then


From a naming perspective, I would rather read upperIndex and lowerIndex than index1 and index2

Totally untested, I would try something like this:

private static void merge(int[] source, int[] buffer, int startingIndex, int count) {
    int lowerIndex = startingIndex;
    int lowerBound = startingIndex + count / 2;
    int upperIndex = lowerBound;
    int upperBound = startingIndex + count;

    for(int i = startingIndex ; i &lt; upperBound ; i++){
        if(upperIndex &gt;= upperBound ){
          System.arraycopy(source, lowerIndex , buffer, i, i - startingIndex );
          return;
        }
        if(lowerIndex &gt;= lowerBound ){
          System.arraycopy(source, upperIndex , buffer, i, i - startingIndex );
          return;
        }
        buffer[i] = source[lowerIndex] &lt; source[upperIndex] ? source[lowerIndex++] : source[upperIndex++]
    }
}