如何在不进行内存分配(避免GC延迟)的情况下迭代遍历ArrayBlockingQueue。

huangapple go评论73阅读模式
英文:

How to iterate through ArrayBlockingQueue without memory allocation (GC delays)

问题

我正在开发一个Android的Java游戏,并且尝试在游戏循环中减少内存分配。每个游戏对象在每帧都会绘制其存储在ArrayBlockingQueue中的项目。每个ForEach循环为迭代器分配了少量内存,在大规模情况下(每秒60次,成千上万的对象和项目),这会导致0.1-0.2秒的GC延迟,影响帧率:

ArrayBlockingQueue<Item> outputQueue = conveyor.getOutputQueue();

for (Item item : outputQueue) {
    progress = 1.0f - (finishedCounter * stridePerItem);
    drawItem(camera, x, y, width, height, item, progress);
    finishedCounter++;
}

我所需要的是在不更改它的情况下访问ArrayBlockingQueue中的所有条目。使用ArrayBlockQueue.toArray()方法会复制队列并分配额外的内存。

如何在迭代ArrayBlockingQueue时避免内存分配(GC延迟)?

附:该游戏模拟工厂供应链 - 队列。所有这些队列都可以从两个线程(OpenGL线程、GameLoop线程)中访问,我需要线程安全的队列。我应该使用其他类型的队列或可以从不同线程并发访问的特殊集合吗?

英文:

I'm developing an Android Java game and trying to minimize memory allocation in the game loop. Every game objects on every frame draw its items, which stored in ArrayBlockingQueue. Every ForEach cycle allocates little portion memory for iterator and on large scales (60 times per second, thousands of objects and items), it causes 0.1-0.2 sec GC delays that affect FPS:

 ArrayBlockingQueue&lt;Item&gt; outputQueue = conveyor.getOutputQueue();

    for (Item item:outputQueue) {
        progress = 1.0f - (finishedCounter * stridePerItem);
        drawItem (camera, x, y, width, height, item, progress);
        finishedCounter++;
    }

All I need is to access all entries in ArrayBlockingQueue without changing it. Using ArrayBlockQueue.toArray() method makes copy of queue and allocates additional memory.

How to iterate through ArrayBlockingQueue without memory allocation (GC delays)?

p.s. The game simulates factory supply chains - queues. All of them accessed from two threads (OpenGL thread, GameLoop thread) and I need thread-safe Queues. Should I use any other type of Queue or special collections that can be accessed from different threads concurrently?

如何在不进行内存分配(避免GC延迟)的情况下迭代遍历ArrayBlockingQueue。

答案1

得分: 1

如果您使用的是Java 8或更高版本,您可以使用ArrayBlockingQueue#forEach来进行迭代。

如果您查看源代码,您可以很容易地看到forEach不会为此分配Iterator

ArrayBlockingQueue<Item> outputQueue = conveyor.getOutputQueue();

outputQueue.forEach(item -> {
    //your code
});

QueueArrayBlockingQueue没有提供在不使用Iterator或将其转换为数组/其他集合的情况下进行迭代的方法。这似乎是在不分配额外对象的情况下迭代ArrayBlockingQueue的唯一方法。

这种方法的缺点是您无法访问不是final或实际上是final的局部变量。您需要创建一个存储变量的对象,或者如果要读取其他地方更改的局部变量,则需要使用副本。

如果需要在迭代器中更改变量,还可以将变量包装起来。

public class IntWrapper {//在方法外部
    private int value;
    //getter/setter/constructor
}

ArrayBlockingQueue<Item> outputQueue = conveyor.getOutputQueue();
final IntWrapper finishedCounterWrapper = new IntWrapper(finishedCounter);

outputQueue.forEach(item -> {
    progress = 1.0f - (finishedCounter * stridePerItem);
    drawItem(camera, x, y, width, height, item, progress);
    finishedCounterWrapper.setValue(finishedCounterWrapper.getValue());
});
finishedCounter = finishedCounterWrapper.getValue();

然而,这会为IntWrapper分配堆空间,即使您可以重用包装器。

英文:

If you use Java 8 or later, you can use ArrayBlockingQueue#forEach in order to iterate.

If you look at the sources, you can easily see that forEach does not allocate an Iterator for this.

ArrayBlockingQueue&lt;Item&gt; outputQueue = conveyor.getOutputQueue();

    outputQueue.forEach(item-&gt; {
        //your code
    });

Queue or ArrayBlockingQueue do not provide a way to iterate without using an Iterator or converting it to an array/other collection. This seems to be the only way to iterate over an ArrayBlockingQueue without allocating an extra object.

The disadvantage of this method is that you cannot access local variables that are not final or effectively final. You need to either create an object storing your variables or use a copy if you want to read local variables changed elsewhere.

You can also wrap variables if you need to change them in the iterator.

public class IntWrapper{//outside of the method
    private int value;
    //getter/setter/constructor
}

ArrayBlockingQueue&lt;Item&gt; outputQueue = conveyor.getOutputQueue();
final IntWrapper finishedCounterWrapper=new IntWrapper(finishedCounter);

outputQueue.forEach(item-&gt; {
    progress = 1.0f - (finishedCounter * stridePerItem);
    drawItem (camera, x, y, width, height, item, progress);
    finishedCounterWrapper.setValue(finishedCounterWrapper.getValue());
});
finishedCounter=finishedCounterWrapper.getValue();

However, this allocates heap space for the IntWrapper, even if you can reuse the wrapper.

huangapple
  • 本文由 发表于 2020年9月21日 13:01:19
  • 转载请务必保留本文链接:https://go.coder-hub.com/63986434.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定