Java 什么是堆写入流量?为什么在ArrayList中需要它?
我只是想知道这意味着什么,为什么在ArrayList实现中需要它Java 什么是堆写入流量?为什么在ArrayList中需要它?,java,Java,我只是想知道这意味着什么,为什么在ArrayList实现中需要它 ArrayList实现的代码片段,请参见带有注释的行 @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size;
ArrayList
实现的代码片段,请参见带有注释的行
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
@覆盖
@抑制警告(“未选中”)
公共空间无效(消费者可能是作者想使用局部变量i
,因为它可能在启动时被堆栈分配。与cursor
不同,i
变量由于i++
语句在while
循环中被多次更改。在堆栈上增加它应该更便宜kip所有Java内存模型含义。Iterator.cursor
是一个成员字段,它可能总是在堆上,尤其是Iterator
对象在用户代码中传递。通常cursor
变量指向Iterator
返回的下一个元素uld需要每次更新光标
变量,使其始终指向正确的元素
但是,forEachRemaining
方法本身就完成了迭代。它并不意味着要暂停。因此,在方法完成之前,您可以忽略更新游标变量。当方法迭代时,游标将指向错误的元素。但由于您不能暂停该方法,因此不会产生任何差异法国
这样可以减少对游标的分配量,从而减少堆流量
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
// Update cursor while iterating
cursor = i;
}
或者直接使用光标而不是附加的i
while (cursor != size && modCount == expectedModCount) {
consumer.accept((E) elementData[cursor++]);
}
但是,您可以使用成员变量而不是局部变量i
。使用i
更便宜,有关详细信息,请参阅@kdowbecki的答案。如果忽略所有保护条件,next()
执行以下操作:
public E next() {
Object[] elementData = ArrayList.this.elementData;
int i = cursor;
cursor = i + 1;
lastRet = i;
return (E) elementData[i];
}
forEachRemaining()
基本上会继续调用next()
并在每个元素上调用使用者,因此如果我们这样做,内联next()
逻辑,我们会得到:
public void forEachRemaining(Consumer<? super E> consumer) {
final int size = ArrayList.this.size;
final Object[] elementData = ArrayList.this.elementData;
int i = cursor;
while (i != size) { // same as hasNext()
// begin: consumer.accept(next())
cursor = i + 1;
lastRet = i;
consumer.accept((E) elementData[i]);
// end: consumer.accept(next())
i++;
}
}
其效果是,在迭代过程中,仅更新堆栈变量i
,而两个堆值被单独保留
一旦JIT启动,如果accept()
调用是内联的,那么堆栈变量i
甚至可能被删除,成为一个寄存器值,从而大大减少了“slow”的更新次数内存。感谢您提供的答案。为了更好地解释,我添加了内部Java内存模型,如下图所示。正如@kdowbecki、@Zabuza和@Andreas所指出的,在本地执行时使用线程堆栈
内存,然后在每次迭代时使用堆
内存是有效的。它可能会失败r这一类
在操作系统中检查(JVM是一个进程)也很有趣
public void forEachRemaining(Consumer<? super E> consumer) {
final int size = ArrayList.this.size;
final Object[] elementData = ArrayList.this.elementData;
int i = cursor;
while (i != size) {
consumer.accept((E) elementData[i++]);
}
cursor = i;
lastRet = i - 1;
}