Java StringBuilder(StringBuffer)的ensureCapacity():为什么它加倍并递增2?

时间:2017-07-14 04:17:03

标签: java stringbuilder stringbuffer capacity

我已经搜索了这个,但是我无法找到为什么StringBuilder的return $this->hasOne(Menumodel::class, '...', '...'); return $this->hasMany(Menumodel::class, '...', '...'); 方法通过加倍而增加两个来延长旧容量。

因此,当默认容量16已满时,下一个加长值将为34,除非整个字符串长度不超过34.为什么不应该是32?

我最好的猜测是考虑一个空字符,' \ u0000',但我不确定。谁能告诉我为什么?

5 个答案:

答案 0 :(得分:8)

我认为这与一个简单的,有点愚蠢的方法有关,以确保非常小的字符串的角落情况。

例如,如果我有字符串

""

我只加倍,我没有足够的尺寸来存放其他任何东西。如果我加倍并添加一个小的常数空格,我可以保证我的新值大于旧值。

为什么要将它增加2呢?可能是一个小的性能改进。通过添加两个而不是1,我可以避免小扩展的中间扩展(下面详细介绍0到10个字符)

"" => expand => "1" => expand => "123" expand => "1234567" expand => "123456789012345"

相比是4扩展
"" => expand => "12" => expand => "123456" => expand => "123456789012"

这是3扩展。这也适用于一个字符串(扩展到10个字符)

"1" => expand => "1234" => expand => "1234567890"

而1 char扩展例程看起来像

"1" => expand => "123" => expand => "1234567" => expand => "123456789012345"

最后,增加的两个增量往往会在大约50%的时间内对齐,而增加的一个或三个增量会大约25%的时间。虽然这看起来似乎不是什么大问题,但是如果没有昂贵的中断调用来重写CPU中的读取,某些架构无法容纳非对齐读取,从而导致各种性能问题。

答案 1 :(得分:0)

我认为原因是

的组合
  • 一些古老的;-)启发式策略如何扩展容量,尤其适用于 短缓冲区,

  • 在最早的Java API文档中记录此策略,

  • Sun / Oracle非常谨慎地坚持曾经记录过的行为。

StringBuilder与它的前身StringBuffer共享这个方法,它读取(可能是从最早开始,至少在j2sdk1.4_02中,它碰巧仍存在于我机器上的某个存档文件夹中):

/**
 * Ensures that the capacity of the buffer is at least equal to the
 * specified minimum.
 * If the current capacity of this string buffer is less than the 
 * argument, then a new internal buffer is allocated with greater 
 * capacity. The new capacity is the larger of: 
 * <ul>
 * <li>The <code>minimumCapacity</code> argument. 
 * <li>Twice the old capacity, plus <code>2</code>. 
 * </ul>
 * If the <code>minimumCapacity</code> argument is nonpositive, this
 * method takes no action and simply returns.
 *
 * @param   minimumCapacity   the minimum desired capacity.
 */
public synchronized void ensureCapacity(int minimumCapacity) {
    if (minimumCapacity > value.length) {
        expandCapacity(minimumCapacity);
    }
}

它完全记录了时间 - 两个加两个行为,所以即使在此期间一些JRE开发人员找到了更好的策略,也没有机会在这里实现它因为它不符合到文档。

答案 2 :(得分:0)

我认为对象对齐是一个关键,因为length * 2 + 2 - 策略是记忆效应的(见下面的解释)。

让我们考虑 HotSpot JVM

首先,java对象是8字节对齐的,而char数组也不例外。

其次,sizeof(object header)等于{strong> 32位JVM 上的8 bytes 64位JVM上的16 bytes -XX:-UseCompressedOops

因此,物体应按8 bytes对齐:
objectBodySize(charArray) == sizeOf(arrayLength) + sizeOf(arrayValues) == (4 bytes) + (arrayLength * 2 bytes)

如果旧的数组长度甚至,那么新的数组长度将始终为零大小对齐。

示例:

  1. oldCharArrayLength == 6然后newCharArrayLength == 14objectBodySize(newCharArray) == 4 + 14 * 2 == 32

  2. oldCharArrayLength == 4然后newCharArrayLength == 10objectBodySize(newCharArray) == 4 + 10 * 2 == 24

  3. 值得注意的是, -XX:+ UseCompressedOops 标志可用,因为 1.6 ,而StringBuilderAbstractStringBuilder可用因为 1.5 。这意味着上面带有两个额外字符的策略在 1.6 之前 64位JVM 上的内存成本为零,而在上运行时为sizeof(object header) == 12 bytes带有-XX的64位JVM:+ UseCompressedOops

答案 3 :(得分:0)

我从Oracle Web下载了Java 1.5的原始源代码,它包含以下几行:

/**
 * This implements the expansion semantics of ensureCapacity with no
 * size check or synchronization.
 */
void expandCapacity(int minimumCapacity) {
    int newCapacity = (value.length + 1) * 2;
    if (newCapacity < 0) {
        newCapacity = Integer.MAX_VALUE;
    } else if (minimumCapacity > newCapacity) {
        newCapacity = minimumCapacity;
    }   
    char newValue[] = new char[newCapacity];
    System.arraycopy(value, 0, newValue, 0, count);
    value = newValue;
} 

所以至少有两件事是清楚的:

  • 另外添加其他修正的理论是错误的(例如&#34;奇数(双+ 2)语义在函数中唯一的一行时更有意义&#34;不是真的)
  • 最有可能它最初的意思是&#34;让我们为至少一个角色腾出空间,然后让它乘以两个&#34;

答案 4 :(得分:0)

 public void ensureCapacity(int minimumCapacity) {
     if (minimumCapacity > value.length) {
         expandCapacity(minimumCapacity);
     }
 }



 void expandCapacity(int minimumCapacity) {
     int newCapacity = (value.length + 1) * 2;
     if (newCapacity < 0) {
         newCapacity = Integer.MAX_VALUE;
     } else if (minimumCapacity > newCapacity) {
         newCapacity = minimumCapacity;
     }
    value = Arrays.copyOf(value, newCapacity);
}

注意:value.length是StringBuffer的容量,而不是长度。

它与空字符串无关,因为最小容量为16。

我认为,内存分配花费了很多时间,如果我们经常通过增加minimumCapacity调用ensureCapacity(),(capacity +1)* 2会分配更多的内存,可能会减少进一步的分配并节省一些时间。

让初始容量考虑为16,

只有加倍16,32,64,128,256,512,1024,2048等......

用双+2 16,34,70,142,286,574,1150,2302等......

因此,内存将逐渐保持增加,并可能减少内存分配的数量。