おっしゃったように、この質問は実際には、スクラッチ スペースの画像をはるかに超えていると考えることができます。私は実際にこれにさまざまな形で遭遇しました (メモリ セクション、型付き配列、スレッド、ネットワーク接続など)。
だから私がやったのは、一般的な " BufferPool
" を自分で書くことでした。これは、バイト配列、他のメモリ、または(あなたの場合)割り当てられたイメージなど、あらゆる形式のバッファオブジェクトを管理するクラスです。このアイデアは からお借りしましたThreadPool
。
Buffer
これは、オブジェクトのプールを維持する非常に単純なクラスでacquire
あり、必要なときにバッファリングrelease
し、使い終わったらプールに戻すことができます。このacquire
関数は、プールに使用可能なバッファがあるかどうかを確認し、ない場合は新しいバッファを作成します。プールに 1 つある場合は、新しく作成されたものと同じように動作するように、それをクリアしますreset
。Buffer
次に、 this の静的インスタンスをいくつか用意します。使用するBufferPool
型ごとに 1 つずつです。次に、私が書いているすべてのライブラリ関数でこれらの静的インスタンスを使用します。これにより、たとえば、暗号化関数がバイト配列をバイナリ フラット化関数やアプリケーション内の他のコードと共有できるようになります。このようにして、これらのオブジェクトを最大限に再利用できるようになり、多くの場合、パフォーマンスが大幅に向上しました。Buffer
byte
char
C++ では、このプーリング手法に基づいて必要なデータ構造のカスタム アロケーターを作成することで、このどこでも使用できるスキームを非常にエレガントに実装できる場合があります(これを指摘してくれた Andrew に感謝します。コメントを参照してください)。
バイト配列バッファーに対して私が行ったことの 1 つは、必要なバッファーの最小サイズを指定acquire
するパラメーターを関数が受け入れるようにすることです。minimumLength
次に、プールから少なくともこの長さのバイト配列のみを返すか、プールが空であるか、プールに小さいイメージしかない場合は新しいバイト配列を作成します。画像バッファーでも同じアプローチを使用できます。関数にandパラメータをacquire
受け入れさせ、プールから少なくともこれらの寸法の画像を返すか、正確にこれらの寸法の画像を作成します。その後、画像の (0, 0) から ( , ) セクションのみをクリアする必要がある場合でも、関数でクリアすることができます。minWidth
minHeight
reset
minWidth
minHeight
私のコードでは気にしないことにした機能の 1 つは、アプリケーションの実行時間と処理する画像サイズの数に応じて、何らかの方法でバッファー サイズを制限するかどうかを検討することをお勧めします。キャッシュされた画像を解放して、アプリケーションのメモリ使用量を減らします。
例として、ここに私が使用するコードを示しますByteArrayPool
。
public class ByteArrayPool {
private static final Map<Integer, Stack<byte[]>> POOL = new HashMap<Integer, Stack<byte[]>>();
/**
* Returns a <code>byte[]</code> of the given length from the pool after clearing
* it with 0's, if one is available. Otherwise returns a fresh <code>byte[]</code>
* of the given length.
*
* @param length the length of the <code>byte[]</code>
* @return a fresh or zero'd buffer object
*/
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;
}
/**
* Returns a <code>byte[]</code> of the given length from the pool after optionally clearing
* it with 0's, if one is available. Otherwise returns a fresh <code>byte[]</code>
* of the given length.<br/>
* <br/>
* If the initialized state of the needed <code>byte[]</code> is irrelevant, calling this
* method with <code>zero</code> set to <code>false</code> leads to the best performance.
*
* @param length the length of the <code>byte[]</code>
* @param zero T - initialize a reused array to 0
* @return a fresh or optionally zero'd buffer object
*/
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;
}
/**
* Returns a <code>byte[]</code> of the given length from the pool after setting all
* of its entries to the given <code>initializationValue</code>, if one is available.
* Otherwise returns a fresh <code>byte[]</code> of the given length, which is also
* initialized to the given <code>initializationValue</code>.<br/>
* <br/>
* For performance reasons, do not use this method with <code>initializationValue</code>
* set to <code>0</code>. Use <code>acquire(<i>length</i>)</code> instead.
*
* @param length the length of the <code>byte[]</code>
* @param initializationValue the
* @return a fresh or zero'd buffer object
*/
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;
}
/**
* Puts the given <code>byte[]</code> back into the <code>ByteArrayPool</code>
* for future reuse.
*
* @param buffer the <code>byte[]</code> to return to the pool
*/
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 {
// Do something requiring a byte[] of length 65536 or longer
} finally {
ByteArrayPool.release(buffer);
}
acquire
要求しているバッファをどの程度「クリーン」にする必要があるかを指定できるようにする3 つの異なる関数をどのように追加したかに注意してください。たとえば、とにかくすべてを上書きする場合、最初にゼロにするために時間を無駄にする必要はありません。