調査 Java でメモリ リークを作成する




java メモリ解放 強制 (24)

そのクラスのfinalizeメソッドでクラスの新しいインスタンスを作成することによって、メモリリークを作成することができます。ファイナライザが複数のインスタンスを作成した場合のボーナスポイント。ヒープサイズに応じて、数秒から数分の間にいつでもヒープ全体をリークする簡単なプログラムがあります:

class Leakee {
    public void check() {
        if (depth > 2) {
            Leaker.done();
        }
    }
    private int depth;
    public Leakee(int d) {
        depth = d;
    }
    protected void finalize() {
        new Leakee(depth + 1).check();
        new Leakee(depth + 1).check();
    }
}

public class Leaker {
    private static boolean makeMore = true;
    public static void done() {
        makeMore = false;
    }
    public static void main(String[] args) throws InterruptedException {
        // make a bunch of them until the garbage collector gets active
        while (makeMore) {
            new Leakee(0).check();
        }
        // sit back and watch the finalizers chew through memory
        while (true) {
            Thread.sleep(1000);
            System.out.println("memory=" +
                    Runtime.getRuntime().freeMemory() + " / " +
                    Runtime.getRuntime().totalMemory());
        }
    }
}

私はちょうどインタビューを受けて、Javaでメモリリークを作成するように求められました。 言うまでもなく、私はそれを作成し始める方法についての手がかりがないかわいそうだと感じました。

例は何でしょうか?


私は、有効な例として、スレッドがプールされている環境でThreadLocal変数を使用することができると思います。

たとえば、サーブレットでThreadLocal変数を使用して他のWebコンポーネントと通信し、そのスレッドがコンテナによって作成され、アイドル状態のものをプール内に維持することができます。ThreadLocal変数は、正しくクリーンアップされていないと、同じWebコンポーネントがそれらの値を上書きするまで、そこに存続します。

もちろん、いったん特定されると、問題は簡単に解決できます。


簡単なことは、間違った(または存在しない) hashCode()またはequals()でHashSetを使用し、次に "duplicates"を追加し続けることです。 重複を無視するのではなく、その集合が拡大し、削除することはできません。

これらの悪いキー/要素をハングアップさせたい場合は、次のような静的フィールドを使用できます。

class BadKey {
   // no hashCode or equals();
   public final String key;
   public BadKey(String key) { this.key = key; }
}

Map map = System.getProperties();
map.put(new BadKey("key"), "value"); // Memory leak even if your threads die.

おそらく潜在的なメモリリークの最も単純な例の1つであり、避ける方法はArrayList.remove(int)の実装です:

public E remove(int index) {
    RangeCheck(index);

    modCount++;
    E oldValue = (E) elementData[index];

    int numMoved = size - index - 1;
    if (numMoved > 0)
        System.arraycopy(elementData, index + 1, elementData, index,
                numMoved);
    elementData[--size] = null; // (!) Let gc do its work

    return oldValue;
}

自分で実装していた場合は、もはや使用されていない配列要素( elementData[--size] = null )をクリアすると思いelementData[--size] = nullか? その参照は、巨大なオブジェクトを生きているかもしれない...


私は最近、より微妙な種類のリソースリークに遭遇しました。クラスローダーのgetResourceAsStreamを介してリソースを開き、入力ストリームハンドルが閉じられていないことが起こりました。

うーん、あなたは、馬鹿なことを言うかもしれない。

JVMのヒープではなく、元のプロセスのヒープメモリがリークする可能性があります。

Javaコードから参照されるファイルが入ったjarファイルだけが必要です。jarファイルが大きければ大きいほど、より速いメモリが割り当てられます。

このようなjarファイルは、次のクラスで簡単に作成できます。

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

public class BigJarCreator {
    public static void main(String[] args) throws IOException {
        ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(new File("big.jar")));
        zos.putNextEntry(new ZipEntry("resource.txt"));
        zos.write("not too much in here".getBytes());
        zos.closeEntry();
        zos.putNextEntry(new ZipEntry("largeFile.out"));
        for (int i=0 ; i<10000000 ; i++) {
            zos.write((int) (Math.round(Math.random()*100)+20));
        }
        zos.closeEntry();
        zos.close();
    }
}

BigJarCreator.javaという名前のファイルに貼り付け、コンパイルしてコマンドラインから実行します:

javac BigJarCreator.java
java -cp . BigJarCreator

Etvoilà:現在の作業ディレクトリに2つのファイルが入ったjarアーカイブがあります。

2番目のクラスを作成しましょう:

public class MemLeak {
    public static void main(String[] args) throws InterruptedException {
        int ITERATIONS=100000;
        for (int i=0 ; i<ITERATIONS ; i++) {
            MemLeak.class.getClassLoader().getResourceAsStream("resource.txt");
        }
        System.out.println("finished creation of streams, now waiting to be killed");

        Thread.sleep(Long.MAX_VALUE);
    }

}

このクラスは基本的に何もしませんが、参照されていないInputStreamオブジェクトを作成します。これらのオブジェクトはすぐにガベージコレクトされるため、ヒープサイズには寄与しません。この例では、jarファイルから既存のリソースをロードすることが重要です。サイズはここで重要です。

疑わしい場合は、上記のクラスをコンパイルして開始してください。ただし、適切なヒープサイズ(2 MB)を選択してください。

javac MemLeak.java
java -Xmx2m -classpath .:big.jar MemLeak

参照が保持されていないため、ここでOOMエラーが発生することはありません。上記の例では、ITERATIONSをどれだけ大きく選択しても、アプリケーションは動作し続けます。アプリケーションがwaitコマンドを取得しない限り、プロセスのメモリ消費量(先頭(RES / RSS)またはプロセスエクスプローラで表示)は増加します。上記の設定では、約150 MBのメモリを割り当てます。

アプリケーションを安全に再生したい場合は、作成された入力ストリームを閉じます。

MemLeak.class.getClassLoader().getResourceAsStream("resource.txt").close();

反復回数に関係なく、プロセスは35 MBを超えません。

かなり単純で驚くべきことです。


メモリリークとは何ですか?

  • これはバグデザインの悪さによるものです。
  • それは記憶の浪費です。
  • 時間が経つと悪化します。
  • ガーベッジコレクターはそれをきれいにすることはできません。

典型的な例:

オブジェクトのキャッシュは、物事を混乱させる良い出発点です。

private static final Map<String, Info> myCache = new HashMap<>();

public void getInfo(String key)
{
    // uses cache
    Info info = myCache.get(key);
    if (info != null) return info;

    // if it's not in cache, then fetch it from the database
    info = Database.fetch(key);
    if (info == null) return null;

    // and store it in the cache
    myCache.put(key, info);
    return info;
}

キャッシュが成長し、成長します。そしてすぐにデータベース全体がメモリに吸い込まれます。より良いデザインは、LRUMap(最近使用されたオブジェクトのみをキャッシュに保持する)を使用します。

確かに、あなたは事をもっと複雑にすることができます:

  • ThreadLocal構造を使用します。
  • より複雑な参照ツリーを追加する。
  • またはサードパーティのライブラリによって引き起こされたリーク。

何が起こるか:

このInfoオブジェクトに他のオブジェクトへの参照が含まれている場合、そのオブジェクトは他のオブジェクトへの参照を持ちます。ある意味では、これはある種のメモリリークであるとも考えられます(悪いデザインによるもの)。


メモリに漏れるさまざまな状況があります。私は遭遇しました。露出してはならない地図を他の場所で公開しています。

public class ServiceFactory {

private Map<String, Service> services;

private static ServiceFactory singleton;

private ServiceFactory() {
    services = new HashMap<String, Service>();
}

public static synchronized ServiceFactory getDefault() {

    if (singleton == null) {
        singleton = new ServiceFactory();
    }
    return singleton;
}

public void addService(String name, Service serv) {
    services.put(name, serv);
}

public void removeService(String name) {
    services.remove(name);
}

public Service getService(String name, Service serv) {
    return services.get(name);
}

// the problematic api, which expose the map.
//and user can do quite a lot of thing from this api.
//for example, create service reference and forget to dispose or set it null
//in all this is a dangerous api, and should not expose 
public Map<String, Service> getAllServices() {
    return services;
}

}

// resource class is a heavy class
class Service {

}

多くの人々が示唆しているように、JDBCの例のように、リソースリークが発生するのはかなり簡単です。実際のメモリリークはもう少し難しいです - 特にJVMの壊れたビットに頼っていないのであれば...

フットプリントが非常に大きく、アクセスできないオブジェクトを作成するというアイディアは、実際のメモリリークでもありません。何もアクセスできない場合はガベージコレクションが行われ、何かがアクセスできる場合はリークではありません...

一つの方法に使用し、それがまだない場合、私は知らない- -しかし仕事には、三深い循環チェーンを持つことです。オブジェクトAはオブジェクトBを参照するので、オブジェクトBはオブジェクトCを参照し、オブジェクトCはオブジェクトAを参照します。GCは、A < - > Bのように2つの深い連鎖 - AとBが何か他にアクセスできないが、三方鎖を扱うことができなければ、安全に収集することができます...


ここの例のほとんどは「複雑すぎる」です。 彼らはエッジケースです。 これらの例では、プログラマーが間違いを犯しました(等価/ハッシュコードを再定義しないのと同じように)か、JVM / JAVA(静的なクラスの読み込み...)のコーナーケースに噛まれました。 インタビュアーが望んでいるタイプのものではなく、最も一般的なケースでもないと思います。

しかし、実際にはメモリリークの場合があります。 ガベージコレクタは、もはや参照されていないものだけを解放します。 Java開発者はメモリを気にしません。 私たちは必要に応じてそれを割り当て、自動的に解放させます。 ファイン。

しかし、長期間使用されるアプリケーションでは、状態が共有される傾向があります。 それは何でも、静的であっても、シングルトンであってもかまいません。通常、アプリケーションは複雑なオブジェクトグラフを作成する傾向があります。 nullへの参照を設定することを忘れたり、コレクションから1つのオブジェクトを削除することを頻繁に忘れると、メモリリークを起こすだけで十分です。

もちろん、UIリスナーのようなあらゆる種類のリスナー、キャッシュ、または長命の共有状態では、適切に処理されないとメモリリークが発生する傾向があります。 これはJavaのコーナーケースではなく、ガベージコレクタの問題ではないことが理解されます。 それは設計上の問題です。 長寿命オブジェクトにリスナーを追加するように設計しましたが、不要になったリスナーは削除しません。 オブジェクトはキャッシュされますが、キャッシュから削除する方法はありません。

我々は、計算によって必要とされる以前の状態を記憶する複雑なグラフを有している可能性がある。 しかし、以前の状態はそれ自体が前の状態にリンクされています。

私たちはSQL接続やファイルを閉じる必要があります。 nullへの適切な参照を設定し、コレクションから要素を削除する必要があります。 適切なキャッシング戦略(最大メモリサイズ、要素数、またはタイマー)が必要です。 リスナーに通知を許可するすべてのオブジェクトは、addListenerメソッドとremoveListenerメソッドの両方を提供する必要があります。 これらの通知が使用されなくなったら、リスナーリストをクリアする必要があります。

メモリリークは確かに本当に可能であり、完全に予測可能です。 特別な言語機能やコーナーケースは必要ありません。 メモリリークは、何か不足している可能性があるか、または設計上の問題があるかのいずれかの指標です。


インタビュアーは、循環参照ソリューションを探している可能性があります。

    public static void main(String[] args) {
        while (true) {
            Element first = new Element();
            first.next = new Element();
            first.next.next = first;
        }
    }

これは、ガベージコレクタ参照カウントの古典的な問題です。その後、JVMがこの制限を持たないもっと洗練されたアルゴリズムを使用していることを丁寧に説明します。

-Wes Tarle


静的マップを作成し、それにハード参照を追加し続けます。それらは決してGCされません。

public class Leaker {
    private static final Map<String, Object> CACHE = new HashMap<String, Object>();

    // Keep adding until failure.
    public static void addToCache(String key, Object value) { Leaker.CACHE.put(key, value); }
}

誰も内部クラスの例を使用していないのは面白いと思った。あなたが内部クラスを持っている場合。本質的にそれを含むクラスへの参照を維持します。もちろん、Javaは最終的にはそれをきれいにするので、技術的にはメモリリークではありません。しかし、これにより、クラスが予期されていたよりも長くぶら下がる可能性があります。

public class Example1 {
  public Example2 getNewExample2() {
    return this.new Example2();
  }
  public class Example2 {
    public Example2() {}
  }
}

今度はExample1を呼び出し、Example2をExample1を破棄すると、本質的にExample1オブジェクトへのリンクが残っています。

public class Referencer {
  public static Example2 GetAnExample2() {
    Example1 ex = new Example1();
    return ex.getNewExample2();
  }

  public static void main(String[] args) {
    Example2 ex = Referencer.GetAnExample2();
    // As long as ex is reachable; Example1 will always remain in memory.
  }
}

私はまた、特定の時間よりも長く存在する変数を持っていれば噂が聞こえました。Javaはそれが常に存在すると想定し、コード内でこれ以上到達できない場合、実際にはそれをクリーンアップしようとはしません。しかし、それは完全に未確認です。


おそらく、JNIを通じて外部ネイティブコードを使用することによって?

純粋なJavaでは、ほとんど不可能です。

しかし、それはもうメモリにアクセスできないときには "標準"タイプのメモリリークですが、それはまだアプリケーションによって所有されています。代わりに、未使用のオブジェクトへの参照を保持したり、後でそれらを閉じることなくストリームを開くことができます。


静的フィールド保持オブジェクト参照[esp finalフィールド]

class MemorableClass {
    static final ArrayList list = new ArrayList(100);
}

長いStringのString.intern()呼び出し

String str=readString(); // read lengthy string any source db,textbox/jsp etc..
// This will place the string in memory pool from which you can't remove
str.intern();

(未公開)公開ストリーム(ファイル、ネットワークなど)

try {
    BufferedReader br = new BufferedReader(new FileReader(inputFile));
    ...
    ...
} catch (Exception e) {
    e.printStacktrace();
}

閉じられていない接続

try {
    Connection conn = ConnectionFactory.getConnection();
    ...
    ...
} catch (Exception e) {
    e.printStacktrace();
}

JVMのガベージコレクタから到達できない領域(ネイティブメソッドによって割り当てられたメモリなど)

Webアプリケーションでは、一部のオブジェクトは、アプリケーションが明示的に停止または削除されるまで、アプリケーションスコープに格納されます。

getServletContext().setAttribute("SOME_MAP", map);

不適切なまたは不適切なJVMオプション (IBM JDKのnoclassgcオプションなど、未使用のクラスガベージコレクションを防止するなど)

IBM jdk設定を参照してください。


私はここから私の答えをコピーすることができます: Javaでメモリリークを引き起こす最も簡単な方法は?

「コンピューター・サイエンス(またはこのような状況での漏れ)でのメモリー・リークは、コンピューター・プログラムがメモリーを消費してもオペレーティング・システムに戻すことができないときに発生します」 (ウィキペディア)

簡単な答えは:できません。 Javaは自動メモリー管理を行い、必要のないリソースを解放します。 これを止めることはできません。 それは常にリソースを解放することができるでしょう。 手動メモリ管理のプログラムでは、これは異なります。 あなたはmalloc()を使ってCでいくらかのメモリを得ることができません。 メモリを解放するには、mallocが返したポインタが必要で、free()を呼び出します。 しかし、ポインタをもう持たない(上書きする、または寿命を超えた)場合は、残念ながらこのメモリを解放することができず、したがってメモリリークが発生します。

これまでのその他の答えは、私の定義では実際にメモリリークではありません。彼らはすべて無意味なもので本当に素早くメモリを満たすことを目指しています。しかし、いつでもあなたが作成したオブジェクトを逆参照し、メモリを解放することができます - >漏れはありません。彼の解決策が効果的にガベージコレクタを無限ループで強制的に "クラッシュ"させていることを認めなければならないので、acconradの答えはかなり近いです。

長い答えは次のとおりです。JNIを使​​用してJava用のライブラリを作成すると、メモリリークが発生する可能性があります。手動でメモリを管理してメモリリークが発生する可能性があります。このライブラリを呼び出すと、Javaプロセスがメモリをリークします。または、JVMにバグがあると、JVMがメモリを失います。おそらくJVMにはバグがあります。ガベージコレクションはそれほど簡単ではないので、既知のものもあるかもしれませんが、それでもバグです。設計上これは不可能です。あなたは、そのようなバグによって影響を受けるいくつかのJavaコードを求めているかもしれません。申し訳ありませんが、私はそれを知らないし、とにかく次のJavaバージョンではもはやバグではないかもしれません。


このGUIコードの一般的な例は、ウィジェット/コンポーネントを作成し、静的/アプリケーションスコープのオブジェクトにリスナーを追加し、ウィジェットが破棄されたときにリスナーを削除しない場合です。メモリリークだけでなく、イベントを起こすために何を聞いていても、すべての古いリスナーも呼び出されるので、パフォーマンスが低下します。


誰もが常にネイティブコードルートを忘れてしまいます。リークの簡単な公式は次のとおりです。

  1. ネイティブメソッドを宣言します。
  2. ネイティブメソッドでは、呼び出しますmalloc。電話しないでくださいfree
  3. ネイティブメソッドを呼び出します。

ネイティブコードのメモリ割り当ては、JVMヒープからのものであることに注意してください。


私はPermGenとXMLパースとの関係で素晴らしい "メモリリーク"を経験しました。私たちが使ったXMLパーサ(どれがどちらか覚えていない)は、比較を高速化するために、タグ名にString.intern()を付けました。私たちの顧客の一人は、XML属性やテキストではなく、タグ名としてデータ値を格納するという素晴らしいアイデアを持っていました。

<data>
   <1>bla</1>
   <2>foo</>
   ...
</data>

実際には、数字は使用せず、1日に10~1500万の割合で入ってきた独自のテキストID(約20文字)を使用しました。これにより、1日に200 MBのゴミが発生します。これは決して再び必要とされません。また、GCedは決してパーマネントではありません。permgenは512 MBに設定されていたので、メモリ不足例外(OOME)が到着するまでに約2日間かかりました...


任意のサーブレットコンテナ(Tomcat、Jetty、Glassfishな​​ど)で実行されているWebアプリケーションを取り出します。アプリを10または20回続けて再デプロイします(サーバーのautodeployディレクトリにあるWARに触れるだけで十分です)。

誰かが実際にこれをテストしていない限り、いくつかの再デプロイ後にOutOfMemoryErrorが発生する可能性が高いです。なぜなら、アプリケーションはそれ以降にクリーンアップを気にかけないからです。あなたは、このテストであなたのサーバにバグを見つけることさえできます。

問題は、コンテナの存続期間がアプリケーションの存続期間よりも長いことです。コンテナーがアプリケーションのオブジェクトやクラスに持つすべての参照をガベージコレクションできることを確認する必要があります。

あなたのWebアプリケーションの展開解除から生き残った参照が1つしかない場合、対応するクラスローダと結果としてあなたのWebアプリケーションのすべてのクラスはガベージコレクションできません。

アプリケーションによって起動されるスレッド、ThreadLocal変数、ロギングアペンダは、クラスローダのリークを引き起こす通常の疑いのあるものです。



答えは、インタビュアーが何を求めていると思ったかによってまったく異なります。

実際にJavaリークを作ることは可能ですか? もちろんですが、他の答えにはたくさんの例があります。

しかし、質問されているかもしれない複数のメタ質問がありますか?

  • 理論的に「完璧な」Java実装はリークに対して脆弱ですか?
  • 候補者は理論と現実の違いを理解していますか?
  • 候補者はガベージコレクションの仕組みを理解していますか?
  • またはガベージコレクションが理想的なケースでどのように動作するはずですか?
  • ネイティブインターフェイスを通じて他の言語を呼び出せることは知っていますか?
  • 彼らは他の言語で記憶を漏らすことを知っていますか?
  • 候補者はメモリ管理が何であるか、そしてJavaの場面で何が起こっているのかを知っていますか?

私はあなたのメタ質問を「私はこのインタビュー状況で使用した答えは何ですか?」と読んでいます。 したがって、私はJavaの代わりにインタビュー技術に焦点を当てます。 インタビューで質問に対する答えを知らないという状況を繰り返す可能性が高いと思うのは、あなたがJavaのリークをどうやって作るのかを知る必要がある場所にいるはずです。 だからうまくいけば、これは助けになるでしょう。

インタビューのために開発できる最も重要なスキルの1つは、積極的に質問に耳を傾け、インタビュアーと協力してその意図を抽出することです。 これにより、あなたは彼らが望むように質問に答えるだけでなく、あなたにいくつかの重要なコミュニケーションスキルがあることが示されます。 同様に多くの才能のある開発者の間の選択肢になると、毎回対応する前に聞き、考え、理解する人を雇うつもりです。


http://wiki.eclipse.org/Performance_Bloopers#String.substring.28.29シンプルで邪悪なものがありhttp://wiki.eclipse.org/Performance_Bloopers#String.substring.28.29

public class StringLeaker
{
    private final String muchSmallerString;

    public StringLeaker()
    {
        // Imagine the whole Declaration of Independence here
        String veryLongString = "We hold these truths to be self-evident...";

        // The substring here maintains a reference to the internal char[]
        // representation of the original string.
        this.muchSmallerString = veryLongString.substring(0, 1);
    }
}

部分文字列は元の文字列、つまりはるかに長い文字列の内部表現を参照するため、元の文字はメモリに残ります。したがって、StringLeakerを演奏している限り、文字列を保持していると思うかもしれませんが、メモリ内に元の文字列全体があります。

元の文字列への不要な参照を格納しないようにするには、次のようにします。

...
this.muchSmallerString = new String(veryLongString.substring(0, 1));
...

さらに悪いことに、あなた.intern()は部分文字列かもしれません:

...
this.muchSmallerString = veryLongString.substring(0, 1).intern();
...

そうすることで、StringLeakerインスタンスが破棄された後であっても、オリジナルの長い文字列と導出された部分文字列がメモリに保持されます。


以下は、 JDBC理解していない場合、非常に意味のない例です。 または、少なくとも、JDBCは、開発者が、 finalizeの実装に頼るのではなく、 ConnectionStatementおよびResultSetインスタンスを破棄したり、それらへの参照を失う前に閉じることを期待finalizeます。

void doWork()
{
   try
   {
       Connection conn = ConnectionFactory.getConnection();
       PreparedStatement stmt = conn.preparedStatement("some query"); // executes a valid query
       ResultSet rs = stmt.executeQuery();
       while(rs.hasNext())
       {
          ... process the result set
       }
   }
   catch(SQLException sqlEx)
   {
       log(sqlEx);
   }
}

上記の問題は、 Connectionオブジェクトが閉じられていないため、ガベージコレクタが周囲に来て到達不能になるまで物理接続が開いたままであることです。 GCはfinalizeメソッドを呼び出しますが、少なくとも、 Connection.closeと同じ方法ではなく、 finalize実装しないJDBCドライバがあります。 結果として、到達不能なオブジェクトが収集されたためにメモリが再利用されますが、 Connectionオブジェクトに関連付けられたリソース(メモリを含む)は単に再利用されない可能性があります。

Connectionfinalizeメソッドがすべてをクリーンアップしないようなイベントでは、実際にデータベースサーバーへの物理的接続がいくつかのガベージコレクションサイクルを継続することがあります。もしそうならば)、閉じなければならない。

JDBCドライバがfinalizeを実装finalize場合でも、ファイナライズ時に例外がスローされる可能性があります。 その結果、 finalizeは一度だけ呼び出されることが保証されるため、現在の "休止中の"オブジェクトに関連付けられたメモリは再利用されません。

オブジェクトの終了時に例外が発生する上記のシナリオは、メモリリーク、オブジェクトの復活につながる可能性のある別のシナリオに関連しています。 オブジェクトの復活は、他のオブジェクトから、オブジェクトのファイナライズが行われないように強い参照を作成することによって、意図的に行われることがよくあります。 オブジェクトの復活を悪用すると、他のメモリリークの原因と組み合わせてメモリリークが発生します。

あなたが上手く描くことができる多くの例があります

  • リストに追加するだけでリストから削除しない場合のListインスタンスの管理(不要になった要素を削除する必要があります)、または
  • SocketまたはFileオープンしFileが、もはやそれらが必要でなくなったときにクローズしません(上記のConnectionクラスに関連する例に似ています)。
  • Java EEアプリケーションを停止するときにシングルトンをアンロードしない。 どうやら、シングルトンクラスをロードしたClassloaderはクラスへの参照を保持するため、シングルトンインスタンスは決して収集されません。 アプリケーションの新しいインスタンスがデプロイされると、通常は新しいクラスローダが作成され、以前のクラスローダはシングルトンのために存在し続けます。

インタビュアーはおそらく以下のコードのような循環参考文献を探していたでしょう(参照計数を使用していた非常に古いJVMのメモリーが漏れているだけです)。しかし、それは非常にあいまいな質問なので、JVMのメモリ管理についてのあなたの理解を誇示することができます。

class A {
    B bRef;
}

class B {
    A aRef;
}

public class Main {
    public static void main(String args[]) {
        A myA = new A();
        B myB = new B();
        myA.bRef = myB;
        myB.aRef = myA;
        myA=null;
        myB=null;
        /* at this point, there is no access to the myA and myB objects, */
        /* even though both objects still have active references. */
    } /* main */
}

次に、参照カウントで、上記のコードがメモリをリークすることを説明することができます。しかし、現代のJVMのほとんどは参照カウントを使用しません。ほとんどの場合、このガベージコレクタを使用します。これは実際にこのメモリを収集します。

次に、基本的なネイティブリソースを持つObjectの作成について説明します。

public class Main {
    public static void main(String args[]) {
        Socket s = new Socket(InetAddress.getByName("google.com"),80);
        s=null;
        /* at this point, because you didn't close the socket properly, */
        /* you have a leak of a native descriptor, which uses memory. */
    }
}

次に、これは技術的にメモリリークだと説明できますが、実際には、Javaコードによって解放されなかったネイティブリソースをJVM内のネイティブコードによって割り当てられているネイティブコードが原因です。

今日の終わりには、現代のJVMで、JVMの認識の通常の範囲外でネイティブリソースを割り当てるJavaコードを記述する必要があります。





memory-leaks