3

多くの人が以前にこの質問をしたことは知っていますが、私の焦点はパフォーマンスではなく、操作のメモリ フットプリントです。

次のダミー クラスを検討してください。

public class MemoryDemo implements Runnable{

    private boolean run;

    public MemoryDemo(){
        run = true;
    }

    @Override
    public void run(){
        byte[] wbuffer; //Here

        final int n = ... //Some big quantity in order of millions.


        while(run){
            //byte[] wbuffer; or here?

            wbuffer = new byte[n]; //Reallocate every loop? or just keep the same memory?

            //Do stuff with the byte array here

            //Copies the data to the target buffer (Not shown here, from another class)
            System.arraycopy(wbuffer, 0, n, targetbuffer, 0, wbuffer.length);
        }
    }
}

私はあちこちから知っていますが、パフォーマンスに関してはまったく違いがなく、範囲を限定する方が良いと人々は言っていますが、メモリフットプリントはどうですか?

上から観察すると、かなり大きな配列を割り当てていることがわかります.Javaには削除関数/構造がないため、メモリを解放するにはガベージコレクターに頼る必要があります. 私のアプリケーションには、これらのループ (基本的にはリアルタイムの画像処理パイプライン) がいくつかあります。メモリ フットプリントをできるだけ最小限に抑えようとしています (つまり、GC が最適な方法でジョブを実行できるようにします)。

ガベージ コレクションの観点から、ループ内とループ宣言外のどちらが優れているか。GC はメモリへの参照がなくなった場合にのみメモリを解放できることは知っていますが、変数を再割り当てするとどうなるか (つまり、ループが再起動してwbufferオブジェクトが再度割り当てられるとき) は不明です。内部ループ変数はその参照全体を失うので、最初にガベージコレクションを取得しますか? それとも、変数が再割り当てされると、両方ともガベージコレクションされますか? System.gc();すべてのループの最後に呼び出す必要がありますか?

また、コードがバイト配列内のすべてのバイトに書き込むことができると仮定すると、変数を再割り当てしない場合 (ループ内で new byte[n] を呼び出さない場合と同様)、バイト配列を再割り当てするよりも良いアプローチ (より醜い1つも...)?

NB配列を再割り当てしないことは、私にとって(すべてのクラスにとって)実行可能なオプションではない可能性があります。それが最良のオプションであることが判明した場合、どちらが2番目に優れているか(ループの内側/外側または違いなし)も説明してください!

4

6 に答える 6

4

メモリの観点から重要なことは、オブジェクトがいつガベージ コレクションの対象になるかを判断することです。

あなたの場合、違いはありませんwbuffer = new byte[n];。前のバイト配列を書き込むとすぐに、到達不能になり、GC の対象になります。

同じ配列を再利用すると、メモリ フットプリントが改善されます。この場合、ループの前に宣言する必要があります。

また、GC は必要に応じて実行されます。非常に特殊な使用例を除けば、一般的に呼び出すのはお勧めSystem.gc();できません。実際にパフォーマンスに悪影響を及ぼす可能性があります。

于 2013-08-04T07:33:33.357 に答える
1

リンク先の質問を誤解しています。

これらの質問では、問題は変数が定義されていることでしたが、割り当てられたオブジェクトの数は変わりませんでした。したがって、パフォーマンスには影響しませんでした。

しかし、その間

byte[] wbuffer = new byte[size];
for (....) {
}

for (....) {
   byte[] wbuffer = new byte[size];
}

メモリとパフォーマンスの違いがあります。

2 番目のオブジェクトでは、さらに多くのオブジェクトが作成され、パフォーマンスとメモリの両方に影響を与えます。

検索した質問は、2 番目の形式と 2 番目の形式に違いがないことを説明しています

byte[] wbuffer;
for (...) {
   wbuffer = new byte[size];
}
于 2013-08-04T07:36:10.163 に答える
0

ループの内側または外側で宣言しbyte[] wbuffer; ても、単なる参照変数として大きな違いはありません。

メインメモリの割り当てはここwbuffer = new byte[n];で行われます。ループの繰り返しごとに新しい配列を作成すると、GC が確実に高くなります。

どういうわけか、すべてのループ反復で同じ配列を使用できる場合、間違いなくメモリを節約できます。それ以外の場合は、あちこちでシャッフルしても、パフォーマンスに実際の違いはありません。

于 2013-08-04T07:33:57.573 に答える
0

すべてのループを再割り当てするべきではありません。また、毎回ガベージ コレクションを実行するべきではありません。

しかし、実際には大きな違いはありません。

于 2013-08-04T07:37:08.890 に答える
0

JVM のガベージ コレクションはまったく予測できません。実装が異なればパフォーマンスも異なる可能性があり、Oracle の JVM だけでも複数のガベージ コレクター戦略があります。

幸いなことに、ここではプリミティブ配列を使用しています。これにより、複雑さが大幅に軽減されます。new を 1 回だけ呼び出すと、作成される配列は 1 つだけになります。System.arrayCopy による場合でも、配列内の値を編集すると、新しい配列を作成するのではなく、実際にはそれらの値を編集します。ループの外側で配列を宣言すると、ガベージ コレクションは方程式にも入りません。

于 2013-08-04T07:37:25.703 に答える
0

繰り返しごとに宣言変数を再割り当てするため、宣言変数をループの内側または外側のどちらに配置しても意味がありません。この場合、ゼロで初期化されたクリーンなバイト配列が必要になる可能性があるため、同じバイト配列を再利用しない場合にのみ、ループの外に置くことは意味があります。

于 2013-08-04T07:38:53.243 に答える