正如你所提到的,这个问题实际上可以被看作远远超出了scratch空间中的图像。我实际上在许多不同形式中遇到过这个问题(内存部分、类型化数组、线程、网络连接等)。
所以我最终写了一个通用的"
BufferPool
"。它是一个管理任何形式的缓冲区对象的类,无论是字节数组、其他一些内存片段,还是(在您的情况下)已分配的图像。我从
ThreadPool
借鉴了这个想法。
这是一个相当简单的类,它维护一个
Buffer
对象池,您可以在需要时
acquire
一个缓冲区,并在使用完后将其
release
回池中。
acquire
函数将检查是否有可用的缓冲区在池中,如果没有,将创建一个新的。如果池中有一个缓冲区,它将
reset
该
Buffer
,即清除它,使其行为与新创建的缓冲区相同。
我有几个静态实例的这个BufferPool,每种不同类型的缓冲区使用一个:一个用于字节数组,一个用于字符数组,...(我在Java中编码,如果你想知道的话... :) 然后我在我编写的
所有库函数中使用这些静态实例。例如,这使我的加密函数可以与我的二进制平坦化函数共享字节数组或应用程序中的任何其他代码。这样,我可以最大限度地重用这些对象,并在许多情况下给我带来了
主要性能提升。
在C++中,您可以通过编写基于此池技术的数据结构的自定义分配器来非常优雅地实现此处处使用方案(感谢Andrew指出;请参见评论)。
我为字节数组缓冲区做的一件事是,acquire
函数将接受一个minimumLength
参数,指定我所需的缓冲区的最小大小。如果池中只有较小的图像或者池为空,它将仅返回至少具有此长度的字节数组,或创建一个新的字节数组。您可以使用相同的方法来处理您的图像缓冲区。让acquire
函数接受minWidth
和minHeight
参数,然后从池中返回至少具有这些尺寸的图像,或创建一个恰好具有这些尺寸的图像。然后,如果您需要清除它,您可以让reset
函数仅清除图像的(0, 0)到(minWidth
, minHeight
)部分。
我在我的代码中决定不担心的一个功能,但您可能要考虑根据您的应用程序运行时间和要处理的不同图像大小的数量是否希望限制缓冲区大小,并释放缓存图像以减少应用程序的内存使用。
这里只是一个示例,这是我用于ByteArrayPool
的代码:
public class ByteArrayPool {
private static final Map<Integer, Stack<byte[]>> POOL = new HashMap<Integer, Stack<byte[]>>();
public static byte[] acquire(int length) {
Stack<byte[]> stack = POOL.get(length);
if (stack==null) {
if (CompileFlags.DEBUG) System.out.println("Creating new byte[] pool of lenth "+length);
return new byte[length];
}
if (stack.empty()) return new byte[length];
byte[] result = stack.pop();
Arrays.fill(result, (byte) 0);
return result;
}
public static byte[] acquire(int length, boolean zero) {
Stack<byte[]> stack = POOL.get(length);
if (stack==null) {
if (CompileFlags.DEBUG) System.out.println("Creating new byte[] pool of lenth "+length);
return new byte[length];
}
if (stack.empty()) return new byte[length];
byte[] result = stack.pop();
if (zero) Arrays.fill(result, (byte) 0);
return result;
}
public static byte[] acquire(int length, byte initializationValue) {
Stack<byte[]> stack = POOL.get(length);
if (stack==null) {
if (CompileFlags.DEBUG) System.out.println("Creating new byte[] pool of lenth "+length);
byte[] result = new byte[length];
Arrays.fill(result, initializationValue);
return result;
}
if (stack.empty()) {
byte[] result = new byte[length];
Arrays.fill(result, initializationValue);
return result;
}
byte[] result = stack.pop();
Arrays.fill(result, initializationValue);
return result;
}
public static byte[] release(byte[] buffer) {
Stack<byte[]> stack = POOL.get(buffer.length);
if (stack==null) {
stack = new Stack<byte[]>();
POOL.put(buffer.length, stack);
}
stack.push(buffer);
return buffer;
}
}
然后,在我需要 byte[]
的所有代码中,我使用类似以下的内容:
byte[] buffer = ByteArrayPool.acquire(65536, false);
try {
} finally {
ByteArrayPool.release(buffer);
}
注意,我添加了3个不同的
acquire
函数,使我能够指定我请求的缓冲区需要多么“干净”。例如,如果我总是覆盖它,那么没有必要浪费时间先清零它。
push()
、pop()
和peek()
函数的IQueue
;实际的实现可以是链表、堆或其他任何数据结构。 - BlueRaja - Danny Pflughoeft