如何在没有内存分配的情况下循环访问ArrayBlockingQueue(GC延迟)

时间:2020-09-21 05:01:19

标签: java android

我正在开发一个Android Java游戏,并试图最小化游戏循环中的内存分配。每帧上的每个游戏对象都绘制其项目,这些项目存储在ArrayBlockingQueue中。每个ForEach周期都会为迭代器分配少量内存,并且会大规模(每秒60次,数千个对象和项目)分配内存,这会导致0.1-0.2秒的GC延迟影响FPS:

 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()方法可以复制队列并分配额外的内存。

如何在不分配内存(GC延迟)的情况下遍历ArrayBlockingQueue?

p.s。该游戏模拟工厂供应链-队列。它们都是从两个线程(OpenGL线程,GameLoop线程)访问的,我需要线程安全的队列。我应该使用可以同时从不同线程访问的任何其他类型的Queue或特殊集合吗?

enter image description here

1 个答案:

答案 0 :(得分:1)

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

如果您查看the sources,很容易发现forEach并未为此分配Iterator

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

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

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

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

如果需要在迭代器中更改变量,也可以包装它们。

public class IntWrapper{//outside of the method
    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分配堆空间。