我已经搜索了这个,但是我无法找到为什么StringBuilder的return $this->hasOne(Menumodel::class, '...', '...');
return $this->hasMany(Menumodel::class, '...', '...');
方法通过加倍而增加两个来延长旧容量。
因此,当默认容量16已满时,下一个加长值将为34,除非整个字符串长度不超过34.为什么不应该是32?
我最好的猜测是考虑一个空字符,' \ u0000',但我不确定。谁能告诉我为什么?
答案 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)
。
如果旧的数组长度甚至,那么新的数组长度将始终为零大小对齐。
示例:
oldCharArrayLength == 6
然后newCharArrayLength == 14
和objectBodySize(newCharArray) == 4 + 14 * 2 == 32
oldCharArrayLength == 4
然后newCharArrayLength == 10
和objectBodySize(newCharArray) == 4 + 10 * 2 == 24
值得注意的是, -XX:+ UseCompressedOops 标志可用,因为 1.6 ,而StringBuilder
和AbstractStringBuilder
可用因为 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;
}
所以至少有两件事是清楚的:
答案 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等......
因此,内存将逐渐保持增加,并可能减少内存分配的数量。