17

「 Effective Java 」を読んでいます。

ファイナライズに関する議論の中で、彼は次のように述べています。

C++ デストラクタは、他の非メモリ リソースを再利用するためにも使用されます。Java では、通常、try finally ブロックがこの目的で使用されます。

非メモリ リソースとは

データベース接続はメモリ以外のリソースですか? データベース接続を保持するためのオブジェクトがメモリを占有していませんか?

4

4 に答える 4

22

データベース接続、ネットワーク接続、ファイル ハンドル、ミューテックスなど。使い終わったときに (ガベージ コレクションだけでなく) 解放する必要があるもの。

はい、これらのオブジェクトは通常、一部のメモリを占有しますが、重要な点は、メモリに加えて一部のリソースにも (おそらく排他的に) アクセスできることです。

于 2011-08-12T08:50:41.777 に答える
5

データベース接続は非メモリリソースですか?

はい、それは最も一般的な例の1つです。その他には、ファイルハンドル、ネイティブGUIオブジェクト(SwingまたはAWTウィンドウなど)およびソケットがあります。

データベース接続を保持するためのオブジェクトは、ある程度のメモリを占有しませんか?

はい。ただし、重要なのは、リソースの非メモリ部分も解放する必要があり、通常、オブジェクトが使用する比較的少量のメモリよりもはるかに少ないということです。通常、このようなオブジェクトにはfinalize()非メモリリソースを解放するメソッドがありますが、問題は、オブジェクトがガベージコレクションされた場合にのみこのファイナライザーが実行されることです。

オブジェクトが小さいため、ガベージコレクターがほとんど実行されないように、使用可能なヒープメモリが十分にある可能性があります。また、ガベージコレクターの実行の間に、非メモリリソースが解放されず、リソースが不足する可能性があります。

これにより、単一のオブジェクトで問題が発生する場合もあります。たとえば、ファイルを開いてターゲットファイルを開き、データをコピーしてから元のファイルを削除することでファイルシステム間でファイルを移動する場合、ファイルが次の場合は削除に失敗します。まだ開いています-そして、入力ストリームへの参照をnullに設定し、明示的に呼び出さない場合はほぼ確実です。これはclose()、ガベージコレクターが、対象となるオブジェクトの間の正確に正しいポイントで実行される可能性が非常に低いためです。ガベージコレクションとdelete()

于 2011-08-12T09:00:57.823 に答える
0

いくつかの重要事項に触れるJava自動メモリ管理のもう1つの重要な平和 。

于 2011-08-12T08:59:14.017 に答える
-1

私の見解では、この質問は逆に答えたほうがよいでしょう。「メモリを手動で解放する必要がないのはなぜですか」。

これにより、「なぜリソースを解放する必要があるのか​​?」という疑問が生じます。

基本的に、実行中のプログラムはさまざまな形式のリソースを使用して実行および作業を行います (CPU サイクル、メモリ位置、ディスク アクセスなど)。これらのほとんどすべてが「希少性」に苦しんでいます。つまり、利用可能なそのようなリソースの固定プールがあり、すべてのリソースが割り当てられている場合、OS は要求を満たすことができず、通常、プログラムは続行できず、非常に無残に終了します-システム全体が不安定になる可能性があります。不足していない唯一のものはCPUサイクルです。これらは好きなだけ発行できます。発行できるレートによってのみ制限されます。それらは同じサイクルで消費されません。メモリまたはファイル ハンドルが存在することを認識します。

したがって、使用するすべてのリソース (メモリ、ファイル ハンドル、データベース接続、ネットワーク ソケットなど) は、そのようなリソースの固定量 (「プール」という言葉を避ける) から、プログラムとして (そして、他のことを心に留めておいてください) OS 自体は言うまでもなく、プログラム) がこれらのリソースを割り当てると、使用可能な量が減少します。

プログラムがリソースを要求して割り当てられ、他の場所で使用するためにそれらを解放しない場合、最終的に (多くの場合すぐに) システムはそのようなリソースを使い果たします。その時点で、システムが停止するか、問題のあるプログラムが突然強制終了されることがあります。

90 年代より前、リソース管理 (少なくとも主流の開発では) は、すべてのプログラマーが明確に対処しなければならない問題でした。一部のリソース割り当て管理はそれほど難しくありません。主に、割り当てが既に抽象化されており (ファイル ハンドルやネットワーク ソケットなど)、リソースを取得して使用し、不要になったときに明示的に解放できるためです。

ただし、メモリの管理は非常に困難です。特に、(重要な状況では) 設計時にメモリ割り当てを計算できないためです。たとえば、データベース接続はこの方法で適切に管理できます。(使用するメモリの量を知る方法はありません。メモリの割り当てがいつ使用されなくなったかを知ることは非常に困難/不可能です)。また、メモリ割り当てはしばらくハングアラウンドする傾向があり、他のほとんどのリソース割り当ては狭い範囲に制限され、多くの場合、単一の try ブロックまたはメソッド内、通常はクラス内に制限されます。そのため、ベンダーはメモリ割り当てを抽象化し、それをプログラムではなく実行環境によって処理される単一の管理システムの下に置く方法を開発しました。

これは、管理された環境 (Java、.NET など) と管理されていない環境 (OS を介して直接実行される C、C++ など) の違いです。C/C++ では、メモリ割り当ては (malloc()/new および関連する再割り当てを使用して) 明示的に行われるため、あらゆる種類の問題が発生します。どのくらい必要ですか? より多く/より少なく必要な場合、どのように計算するのですか? メモリを解放するにはどうすればよいですか? すでに解放されているメモリを使用していないことを確認するにはどうすればよいですか? メモリ割り当て要求が失敗する状況を検出して管理するにはどうすればよいですか? メモリを上書きしないようにするにはどうすればよいですか (おそらく自分のメモリでさえありません)。これはすべて非常に困難であり、メモリリーク、コアダンプ、およびあらゆる種類の半ランダムで再現不可能なエラーにつながります.

そのため、Java は自動メモリ管理を実装しています。プログラマーは単純に新しいオブジェクトを割り当てるだけで、何のメモリがどこに割り当てられるかという点には関心がありませんし、関心があるべきでもありません (これが、管理された環境でポインタの方法があまりない理由でもあります)。

object thing = new Object();

やるべきことはそれだけです。JVM は、使用可能なメモリ、割り当てが必要な時期、解放できる時期 (使用されなくなったため) を追跡し、メモリ不足の状況にできるだけ適切に対処する方法を提供します (問題をスレッド/JVM を実行し、OS 全体をダウンさせない)。

自動メモリ管理は現在、ほとんどのプログラミングで標準になっています。メモリ管理は、管理するのが最も難しいリソースです (主に、データベース接続プール、ソケットの抽象化など、他のリソースがある程度抽象化されているため)。

したがって、質問に答えるには、はい、すべてのリソースを管理する必要がありますが、Java では、自分でメモリを明示的に管理する必要はありません (また、管理することもできません) (ただし、キャッシュの設計など、状況によっては検討する価値があります)。これにより、明示的に管理する必要がある他のすべてのリソースが残ります (これらはメモリ以外のリソース、つまりオブジェクトのインスタンス化/破棄以外のすべてです)。

これらの他のすべてのリソースは明らかにメモリ リソースにラップされていますが、それはここでは問題ではありません。たとえば、開くことができるデータベース接続の数には制限があり、作成できるファイル ハンドルの数にも制限があります。これらの割り当てを管理する必要があります。finally ブロックを使用すると、例外が発生した場合でも、リソースの割り当てを確実に解除できます。

例えば

public void server()
{
  try
  {
    ServerSocket serverSocket = new ServerSocket(25);
  }
  catch (Exception exception)
  {
     // Something went wrong.
  }
  finally
  {
    // Clear up and deallocate the unmanaged resource serverSocket here.
    // The close method will internally ensure that the network socket is actually flushed, closed and network resources released.
    serverSocket.close();
    // The memory used by serverSocket will be automatically released by the JVM runtime at this point, as the serverSocket has gone out-of-scope- it is never used again, so can safely be deallocated.
  }
}
于 2011-08-12T13:21:15.943 に答える