これらの8つの実用的なJavaスキルを習得すれば、メモリリークについて心配する必要がなくなります。

序文

中国語に翻訳された「OutOfMemory」のフルネームであるOOMは、java.lang.OutOfMemoryErrorから派生した「outofmemory」を意味します。

公式の説明を見てください:Java仮想マシンがメモリ不足のためにオブジェクトを割り当てることができず、ガベージコレクターがそれ以上メモリを使用できない場合にスローされます。オブジェクトがスペースを割り当て、ガベージコレクターに再利用するスペースがない場合、このエラーがスローされます。Javaメモリモデルに関するメモをまとめました。アドレス:Javaバックエンドインタビューの質問

(注:この問題はアプリケーションで処理できるほど深刻であるため、例外ではありません)。

静的コレクションクラス

HashMap、LinkedListなど。これらのコンテナが静的である場合、それらのライフサイクルはプログラムと一致しており、プログラムが終了する前にコンテナ内のオブジェクトを解放できないため、メモリリークが発生します。簡単に言うと、寿命の長いオブジェクトは寿命の短いオブジェクトへの参照を保持します。寿命の短いオブジェクトは使用されなくなりましたが、寿命の長いオブジェクトは参照を保持しているため、リサイクルできません。

データベース接続、ネットワーク接続、IO接続などのさまざまな接続。

データベースを操作する過程で、まずデータベースとの接続を確立する必要があります。データベースが使用されなくなったら、closeメソッドを呼び出してデータベースとの接続を解放する必要があります。接続が閉じられた後でのみ、ガベージコレクターは対応するオブジェクトを再利用します。そうしないと、データベースにアクセスするプロセスで、Connection、Statement、またはResultSetが明示的に閉じられていない場合、多数のオブジェクトをリサイクルできなくなり、メモリリークが発生します。

変数の不合理なスコープ

一般的に、変数の定義の範囲はその使用の範囲よりも大きく、メモリリークを引き起こす可能性があります。一方、オブジェクトが時間内にnullに設定されていない場合、メモリリークが発生する可能性があります。

public class UsingRandom {private String msg;public void receiveMsg() { readFromNet(); // 从网络中接受数据保存到msg中 saveDB(); // 把msg保存到数据库中 }}

上記の擬似コードと同様に、受信したメッセージはreadFromNetメソッドを介して変数msgに保存され、saveDBメソッドが呼び出されてmsgの内容がデータベースに保存されます。現時点では、msgは寿命のために使用できません。 msgのサイクルとオブジェクトの存続期間サイクルは同じであり、現時点ではmsgを再利用できないため、メモリリークが発生します。

実際、このmsg変数はreceiveMsgメソッド内に配置できます。メソッドが使い果たされると、msgのライフサイクルも終了し、この時点でリサイクルできます。別の方法として、msgを使用した後、msgをnullに設定して、ガベージコレクターがmsgのメモリスペースを再利用するようにします。

内側のクラスは外側のクラスを保持します

外部クラスのインスタンスオブジェクトのメソッドが内部クラスのインスタンスオブジェクトを返す場合、外部クラスのインスタンスオブジェクトが使用されなくなったとしても、内部クラスがインスタンスを保持しているため、内部クラスのオブジェクトは長期間参照されます。外部クラスのオブジェクト。この外部クラスオブジェクトはガベージコレクションされないため、メモリリークも発生します。

ハッシュ値を変更する

オブジェクトがHashSetコレクションに格納されている場合、ハッシュ値の計算に関与するオブジェクトのフィールドは変更できません。それ以外の場合、オブジェクトの変更されたハッシュ値は、HashSetコレクションに最初に格納されているハッシュ値と同じです。違いは、この場合、containsメソッドがオブジェクトの現在の参照をパラメーターとして使用してHashSetコレクション内のオブジェクトを取得しても、オブジェクトが見つからないという結果を返すため、これも発生します。 HashSetコレクションから個別に削除できないようにする現在のオブジェクト、メモリリークの原因

たとえば、メモリリークを見つけることができるかどうかを確認します

import java.util.Arrays;public class Stack {private Object[] elements;private int size = 0;private static final int DEFAULT_INITIAL_CAPACITY = 16;public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; }public void push(Object e) { ensureCapacity(); elements[size++] = e; }public Object pop() {if (size == 0)throw new EmptyStackException();return elements[--size]; }private void ensureCapacity() {if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); }}原因分析

上記のプログラムに明らかなエラーはありませんが、このプログラムにはメモリリークがあります。GCアクティビティの増加またはメモリ使用量の継続的な増加により、プログラムのパフォーマンスが低下します。深刻な場合、メモリリークが発生する可能性があります。 。失敗は比較的少ないです。

コードの主な問題は、このアイコンで下に示されているポップ関数です。

下の図に示すように、このスタックが成長していると仮定します。

これらの8つの実用的なJavaスキルを習得すれば、メモリリークについて心配する必要がなくなります。

次の図に示すように、多数のポップ操作が実行されると、参照がブランクにされないため、gcは解放されません。

これらの8つの実用的なJavaスキルを習得すれば、メモリリークについて心配する必要がなくなります。

上の図から、スタックが最初に拡大してから縮小した場合、スタックからポップされたオブジェクトはガベージコレクションとして扱われず、プログラムがこれらのオブジェクトをスタックで使用しなくなっても、収集されないことがわかります。 。このオブジェクトへの参照はまだスタックに格納されているため(一般に期限切れの参照と呼ばれます)、このメモリリークは非常に隠されています。

解決

public Object pop() {if (size == 0)throw new EmptyStackException();Object result = elements[--size]; elements[size] = null;return result;}

参照の有効期限が切れたら、これらの参照をクリアし、参照を空にします。

これらの8つの実用的なJavaスキルを習得すれば、メモリリークについて心配する必要がなくなります。

キャッシュリーク

メモリリークのもう1つの一般的な原因はキャッシュです。オブジェクト参照をキャッシュに入れると、忘れがちです。この問題では、WeakHashMapを使用してキャッシュを表すことができます。このタイプのマップの特徴は、キーこのキーへの他の参照がない場合、このマップはこの値を自動的に破棄します

1.コード例

package com.ratel.test;/** * @业务描述: * @package_name:com.ratel.test * @project_name:ssm * @author:[email protected] * @copyright (c) ratelfu 版权所有 */import java.util.HashMap;import java.util.Map;import java.util.WeakHashMap;import java.util.concurrent.TimeUnit;public class MapTest {static Map wMap = new WeakHashMap();static Map map = new HashMap();public static void main(String[] args) { init(); testWeakHashMap(); testHashMap(); }public static void init(){String ref1= new String("obejct1");String ref2 = new String("obejct2");String ref3 = new String ("obejct3");String ref4 = new String ("obejct4"); wMap.put(ref1, "chaheObject1"); wMap.put(ref2, "chaheObject2"); map.put(ref3, "chaheObject3"); map.put(ref4, "chaheObject4"); System.out.println("String引用ref1,ref2,ref3,ref4 消失"); }public static void testWeakHashMap(){ System.out.println("WeakHashMap GC之前");for (Object o : wMap.entrySet()) { System.out.println(o); }try { System.gc(); TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace(); } System.out.println("WeakHashMap GC之后");for (Object o : wMap.entrySet()) { System.out.println(o); } }public static void testHashMap(){ System.out.println("HashMap GC之前");for (Object o : map.entrySet()) { System.out.println(o); }try { System.gc(); TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) {// TODO Auto-generated catch block e.printStackTrace(); } System.out.println("HashMap GC之后");for (Object o : map.entrySet()) { System.out.println(o); } }}/** 结果 String引用ref1,ref2,ref3,ref4 消失 WeakHashMap GC之前 obejct2=chaheObject2 obejct1=chaheObject1 WeakHashMap GC之后 HashMap GC之前 obejct4=chaheObject4 obejct3=chaheObject3 Disconnected from the target VM, address: '127.0.0.1:51628', transport: 'socket' HashMap GC之后 obejct4=chaheObject4 obejct3=chaheObject3 **/

これらの8つの実用的なJavaスキルを習得すれば、メモリリークについて心配する必要がなくなります。

上記のコードと図は、WeakHashMapがキャッシュオブジェクトを自動的に解放する方法を示しています。init関数が実行されると、ローカル変数の文字列参照weakd1、weakd2、d1、d2は表示されなくなります。現時点では、静的な文字列オブジェクトへの参照のみが表示されます。マップが保存されます。、gcを呼び出した後、ハッシュマップはリサイクルされませんが、WeakHashmapのキャッシュはリサイクルされます。

リスナーとコールバック

メモリリークの3番目の一般的な原因は、リスナーとその他のコールバックです。クライアントが実装するAPIにコールバックを登録したが、それをキャンセルしなかった場合、それは蓄積されます。コールバックがすぐにガベージコレクションとして扱われるようにする最善の方法は、参照のみを保存することです。たとえば、WeakHashMapにキーとして保存します。

おすすめ

転載: blog.51cto.com/14994509/2657227