2012-12-15 5 views
2

아래 예에서 메모리 누수가 발생합니다.setListener 동안의 메모리 누수

1.SpeedHelper.java :

public class SpeedHelper { 

    interface Listener { 
     void OnSelected(String entry); 
    } 
    static Listener sListener; 

    static void setListener(Listener listener) { 
     sListener = listener; 
    } 
    static Listener getListener() { 
     return sListener; 
    } 
    static void clearListener() { 
     sListener = null; 
    } 
} 

2.CallSpeed.java

public class CallSpeed { 

    protected void speed() { 
     SpeedHelper.Listener litener = SpeedHelper.getListener(); 
     if (litener != null) { 
      litener.OnSelected("mEntry"); 
     } 
    } 
} 

3.MainActivity.java "sListener"에 대한 메모리 누수를 방지하는 방법

public class MainActivity extends Activity { 

    private CallSpeed callspeed; 

    @Override 
    protected void onCreate(Bundle savedInstanceState) { 
     super.onCreate(savedInstanceState); 
     SpeedHelper.setListener(mCallSpeedListener); 
     callspeed = new CallSpeed(); 
     callspeed.speed(); 
    } 
    private SpeedHelper.Listener mCallSpeedListener = new SpeedHelper.Listener() { 

     @Override 
     public void OnSelected(String entry) { 
      Toast.makeText(getApplicationContext(), entry, Toast.LENGTH_SHORT).show(); 
     } 
    }; 
    @Override 
    protected void onDestroy() { 
     // TODO Auto-generated method stub 
     super.onDestroy(); 
     SpeedHelper.clearListener(); 
    } 
} 

?

답변

1

필드 sListener이 아닌 정적 밀폐 클래스이기 때문에, 그 바깥 클래스 MainActivity에 대한 참조를 포함

private SpeedHelper.Listener mCallSpeedListener = new SpeedHelper.Listener() { 

     @Override 
     public void OnSelected(String entry) { 
      Toast.makeText(getApplicationContext(), entry, Toast.LENGTH_SHORT).show(); 
     } 
    }; 

. 숨겨진 참조가 포함 된 객체 mCallSpeedListenerSpeedHelper.setListener(mCallSpeedListener); 명령어로 정적 필드 sListener에 저장되므로이 숨겨진 참조는 GC에 의해 MainActivity 클래스의 출시를 차단합니다. 따라서 MainActivity 개체는 절대로 수집되지 않으며 해당 onDestroy 함수는 호출되지 않고 모든 것이 메모리에 남아 있습니다. GC의 도달 범위를 벗어났다. 정적 필드를 사용하는 것은 메모리 누수에 대한 문을 활짝 열어 놓고 있으며 동봉 된 클래스 (익명 또는 아님)에 숨겨진 참조가 있으면이 문제가 증폭됩니다.

경우에 따라 정적 필드 sListener을 제거하거나 익명 클래스를 일반 클래스로 변경해야합니다. 하나는 외부 MainActivity 클래스에 대한 숨겨진 참조가 없거나 활동이 종료 될 때 다른 콜백에서 clearListener으로 직접 호출해야합니다.

+0

"활동이 종료 될 때 다른 콜백에서 clearListener를 직접 호출하십시오.", 나는 이것을 얻지 못했습니다. – user1905715

+0

개인 SpeedHelper.Listener mCallSpeedListener = 새로운 SpeedHelper.Listener() { @Override 공공 무효 OnSelected (문자열 입력) { Toast.makeText (getApplicationContext(), 항목, Toast.LENGTH_SHORT) .show(); 'SpeedHelper.clearListener(); mCallSpeedListener = null; ' } }; 맞습니까? – user1905715

+0

onStop 또는 onPause와 같은 onDestroy 이외의 다른 것을 사용하는 것에 대한 의견은 죄송합니다. 늦은 밤이었고 onDestroy는 GC 프로세스에 실제로 묶여 있지 않았습니다. GC가 활동 메모리를 수집 할 수없는 경우에도 호출됩니다. sListener에 메모리 누수가 있다는 결정을 어떻게 내 렸나요? – SylvainL

1

당신은 당신의 SpeedHelper 클래스 변수 정의에 대한 static WeakReferences<Listener> sListener를 사용에 getter와 setter 메소드를 변경하려고 할 수 있습니다

귀하의 문제는 정적과 관련하여 다음과 같은 비 정적 동봉 된 익명의 클래스에있다
static void setListener(Listener listener) { 
    sListener = new WeakReference(listener); 
} 
static Listener getListener() { 
    return sListener.get(); 
} 
+0

이것이 충분한 지 확신 할 수 없습니다. 강력한 참조도 지정하지 않으면 개체 sListener가 여전히 필요하지만 GC에 의해 수집 될 수 있습니다. 그러나 MainActivity 객체의 강력한 참조를 sListener 객체에 추가하면이 정적 참조가 더 이상 필요하지 않습니다. – SylvainL