역시나 아래 참고 싸이트를 번역한수준밖에는 안됩니다 ^^;;

 

보통 타이머를 사용하면

MainActivity에서, onCreate 부분에서 startTimer()를 호출하게 됩니다.

그러면 타이머 클래스는, 주기를 가지고 일정주기동안 계속해서 실행하게 됩니다.

 

Activity가 destroy가 호출되었지만 Activity내에서 타이머가 살아있게되면, 결국 Activity는 소멸되지못하고

Leak 이발생하게 됩니다.

 

어떻게 Memory Leak 을 막을 수 있을까요?

 

 

1. 액티비티가 종료되는 onDestory() 에서 cancerTimer() 와 같은, 타이머 종료호출 메서드를 call하여, 타이머를 종료시키고 그로인해, Activity의 reference가 잡히는것을 해지할 수 있습니다.

 

 

 

 

 

참고

https://android.jlelse.eu/9-ways-to-avoid-memory-leaks-in-android-b6d81648e35e

 

9 ways to avoid memory leaks in Android

I have been an android developer for quite some time now. And I realised that most of that time, I tend to spend on adding new features to…

android.jlelse.eu

 

아래 참고싸이트를 번역한 수준밖에안됩니다 ^^;;;

 

 

Thread 인스턴스를 생성해서 운영할때도 문제가 생길 수 있습니다.

Thread 가 실행되는 시점에서는 GC가 Thread를 수거해가지 않습니다. 또한, Thread가 참조하고있는

Outer class등의 reference도 잡고있어서, Activity내의 Thread를 운영할때는 주의를 기울여야 합니다.

 

 

흔히들 하는 실수

 

1. 액비비티 내에서 Thread의 참조변수를 static 변수로 두는 행위

 

2. custom Inner Thread class를 정의할때 non-static 으로 정의하는경우

 

 

 

해결책

 

1. Thread 인스턴스를 static변수로 저장하지 말아라.

 

 

2. custom static innter thread class를 정의하고 운영해라. 

단 Activity가 종료될때 (onDestroy() ), Thread의 종료를 끝낼 수 있는 로직을 추가해서 깔끔하게 종료되도록 해라.

 

 

 

 

 

 

참고

https://android.jlelse.eu/9-ways-to-avoid-memory-leaks-in-android-b6d81648e35e

 

9 ways to avoid memory leaks in Android

I have been an android developer for quite some time now. And I realised that most of that time, I tend to spend on adding new features to…

android.jlelse.eu

 

 

아래의 참고싸이트를 번역한 수준밖에 안됩니다 ^^;;;

 

보통 Handler를 사용할때 생길 수 있는 문제가...

 

1. 멤버변수로 new Handler() 의 인스턴스를 가지고 있는경우

핸들러의 인스턴스를 클래스가 가지고있으면, Message Queue가 Outer class인 Outer class의 reference를 잡고있게 되어버린다.

 

2. 핸들러가 Outer class의 reference를 가지고 있어야 할 경우

 

 

 

해결책

 

1. Handler 인스턴스가 필요한경우 새롭게 Handler class를 상속받는 static inner class를 내부에 정의하고 구현.

-> 이렇게 되면 Handler 인스턴스를 사용해도 Message Queue는 Outer class Reference를 안잡게되어, Activity가 종료되는순간 해제되게됨.

 

2. WeakReference 사용

Handler인스턴스도 결국 메시지를 받아서 Outer class인 Acitivtiy의 view를 변경해야할 작업이 있을 수 있음

이럴때 WeakReference로 받게되어 처리하게되면, Activity종료시점에 안정적으로 해제할 수 있음. 또한, Acitivtiy가 날라간 경우도 생길 수 있는데 이때 null check를 통해 안정적으로 가저가면 문제는 해결됨.

 

 

 

 

 

 

 

참고

https://android.jlelse.eu/9-ways-to-avoid-memory-leaks-in-android-b6d81648e35e

 

9 ways to avoid memory leaks in Android

I have been an android developer for quite some time now. And I realised that most of that time, I tend to spend on adding new features to…

android.jlelse.eu

 

아래 싸이트 에서 발췌하였습니다.

https://android.jlelse.eu/9-ways-to-avoid-memory-leaks-in-android-b6d81648e35e

불러오는 중입니다...

들어가서 확인해보시면 알겠지만(6. AsyncTask Reference) 흔히들 Activity에서 inner class로 AsyncTask를 상속받은 클래스를 만든다음 거기서 시간이 많이 지연되는일 혹은, Outer class인 Activity의 view들을 직접 참조하여 사용하는 경우가 많을 것입니다.

 

 

1. 일반 inner class로 생성하여 outer class의 reference를 쥐지 않도록 해야합니다. static inner class를 사용하는게 바람직합니다.

2.outer class의 activity가 종료될때 AsyncTask가 진행중인일이있다면 취소를해주고 종료를 시켜줘야 합니다. 안그렇게 되면 AsyncTask는 activity가 종료되었음에도 불구하고 계속해서 동작하기 때문입니다.

3.inner asynctask에서 직접 outer class의 activity의 view를 참고하지말아야합니다. (결국 context를 쥐고있는것과 같게 됩니다.)

 

 

위의 3가지 실수를 막기위한 옵션으로는

1. inner AsyncTask class -> static inner AsyncTask class로 변경

 

2.Activity onDestory시점에서 AsyncTask의 task를 끝낼수있도록 cancel 요청을 날려야함

 

3.WeakReference를 사용해서 view혹은 context를받아야함

 

 

 

흔히들 BroadcastReceiver 를 사용할때 등록하는것은 예제코들 잘 보고 실천을 하지만

해제하는것에 대해서는 심각하게 생각들을 안하는 것 같다.

 

BroadCastReceiver를 등록한다는것 자체가 익명의 클래스를 생성해서 Android Framework에 등록을 하게된다.

아직  BroadCastReceiver 를 등록했던 액티비티가 사라지게되면 어떻게 될까?

 

해당 액티비티의 context를 Android Framework가 쥐고있기에 완벽하게 해제가 되지않고 memory leak 이 발생하게 된다.

 

onStop 혹은 onDestroy method에서 적절하게 BroadCastReceiver를 필히 해제해줘야 할 것이다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
 
 
public BroadCastActivitiy extends AppCompatActivity {
 
    private BroadCastReceiver br;
 
 
    ....
 
    protected void onStart() {
        super.onstart();
        br = new BroadCastReceiver() {
            @override
            public void onReceive(Context context, Intent intent) {
 
            }
          }
        registerReceiver(br, new IntentFilter("hello.myworld"));
    }
 
 
 
    protected void onStop() {
        super.onStart();
        if (br != null)
             unregisterReceiver(br);        
    }
 
    protected void onDestory() {
        super.onDestory();
        if (br != null)
             unregisterReceiver(br);
    }
 
 
}
 
 

 

 

앞선 포스팅 (3. memory leak 회피방법: Inner Class Reference를 주의하라)

과 원리는 동일하다.

 

 

Class에서 익명 class를 만들게되면 익명클래스의 instance는 outer class의 instance에 대해서 

reference를 가지게 된다.

 

 

상황 1. 참고싸이트에서 해결방법 요약

(https://android.jlelse.eu/9-ways-to-avoid-memory-leaks-in-android-b6d81648e35e)

익명클래스로 선언하고 outer class의 context를 static inner class instance로 넘김.

static inner class는 outer class의 instance를 가지지만 WeakReference로 가지기 때문에

스레드에서 긴작업을 하는도중 outer class의 instance가 해제되면, GC에 의해 잘 수거가 이루어지게 되고

WeakReference.get()을 하게되면 null값 반환후 종료되게된다.

 

 

상황2.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
 
public class Outer{
    public void sampleMethod () {
        SampleThread st = new SampleThread();
        st.start();
    }
    
    private static class SampleThread extends Thread {
        public void run() {
            Object sampleObject = new Object();
            // ...
        }
    }
 
}
 
 
s

위의 예제는 올바르다고 볼수있는가? => 그렇다.

Main thread (GC Root)는 Outer와 Static inner class를 따로 참조하고있으며

Outer와 static inner class간에는 서로 참조관계가 없다.

 

 

 

아래의 예제는 올바르다고 볼수있는가? => 아니다.

static inner class를 사용했지만 익명객체를 선언하게 되면서 (New Runnable...)

Runnable 객체가 Outer Class의 instance를 가지게 되고, SampleThread(static inner class)

익명의 Runnable 을 받게되어버리면 다시 Outer class의 intance를 reference하기에 문제가 생기게된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 
public class Outer{
    public void sampleMethod () {
        SampleThread st = new SampleThread(new Runnable() {
            public void run () {
                Object sampleObject = new Object();
                // ...
            }
        });
        st.start();
    }
    
    private static class SampleThread extends Thread {
        public SampleThread(Runnable runnable) {
            super(runnable);
        }
    }
 
}
 
http://colorscripter.com/info#e" target="_blank" style="color:#e5e5e5text-decoration:none">Colored by Color Scripter
 

 

 

Outer가 일을 끝내서 소멸되려고 하는찰나에, 익명의 runable이 실행시간이 길게된다면 이는 outer class의 reference를 계속 잡고있기에 outer class의 memory leak이 발생하게된다.

 

 

 

참고

https://android.jlelse.eu/9-ways-to-avoid-memory-leaks-in-android-b6d81648e35e

 

9 ways to avoid memory leaks in Android

I have been an android developer for quite some time now. And I realised that most of that time, I tend to spend on adding new features to…

android.jlelse.eu

 

최악의 상황을 가정해보자

 

1. static변수가 Inner class의 인스턴스를 저장하고있다.

2. inner class가 outer class의 context 정보를 가지고 있다.

 

 

해결방법

 

1. static변수에 context를 담지말고 일반변수로 담아라

2. inner class 를 -> static inner class로 변경해라. => static inner class는 hidden으로 outer class instance의 reference를 가지지 않는다.

3. WeakReference를 이용해서 context를 받고 필요할때 get으로 꺼내서 지역변수 수준으로 사용해라.

 

static inner class 생성자 {

   private final WeakReference<Activity> activityRef = new WeakReference<>(activtiy);

}

...

 

public void someMethod() {

  Activtiy activity =  activityRef.get();

 

  if (activtiy != null) {

   ....

  }

 

}

 

 

 

 

 

 

참고

https://android.jlelse.eu/9-ways-to-avoid-memory-leaks-in-android-b6d81648e35e

 

9 ways to avoid memory leaks in Android

I have been an android developer for quite some time now. And I realised that most of that time, I tend to spend on adding new features to…

android.jlelse.eu

 

일단 내가 찾아본 자료들로는 명시적으로 non static inner class의 memory를 해제할 순 없어보인다

무조건 outer class의 instance를 들고있게된다. (hidden으로 들고있게된다고 한다.)

 

 

 의문이 들었던 것은 non static inner class는 무조건 memory leak을 유발시키는 것인가에 대한 고민이었는데

그럴것 같진 않다.

 

GC의 ROOT에서부터 Reach가되는 Object instance가지 탐색을해서 reach하지 않게되면 instacne를 해제하게되는데 

 

아래 그림과 같은 경우는 outer class 및 inner class (non static inner class)의 instance는 사이좋게 GC에 의해 수거가 되게된다.

 

 

하지만 inner class에서 thread가 돌아가면서 긴 시간의 일을 하게되면 어떻게 될까?

 

GC ROOT쪽에서 Outer class instance의 참조를 만약 명시적으로 null 로 준다고 하여도

 

inner class의 Thread가 돌아가게 되고 이는 main process가 inner class instance를 참조하게되고 또한

 

outer class instance역시 참조를 당하기에 memory leak을 유발하게 될 것이다.

 

 

또한 위의 그림과 같은구조인데 static변수가 inner class의 instance를 들고있게되면 역시 위와 같은 

Reference chain이 생기게되어 memory leak이 발생하게 된다.

 

 

 

 

 

참고

https://blog.androidcafe.in/android-memory-leak-part-1-context-85cebdc97ab3

 

Android Memory Leak Part 1: Inner class

Vietnamese Link: https://medium.com/@huynhquangthao/2a487116496f

blog.androidcafe.in

 

 

 

static inner class는 static영역에 처음부터 상주하게 된다. 즉 프로세스가 실행되는 시점부터

이미 메모리에 올라가게된다. 그러므로 static inner class는 outer class의 static변수에는 접근이 가능하다.

 

사실 static inner class는 inner class 라 보기에 어려운 점이 한두가지가 아닌것이 일단 outer class에 대한

reference정보가 없다. 당연한것이 outer class는 static class가 아니고 인스턴스가 생성되기 전이므로

정보가 없게된다. 그러므로 Memory Leak을 관리하기 위한 이점이 있는편이다.

 

다만 static inner class라고 하더라도 outer class의 참조값 혹은 context를 받아와야하는 경우가 생긴다.

그럴때는, WeakRefernce를 사용해서 받아줘야한다.

 

 

 

 

-궁금해서 실험해본내역

static inner class 라고해서 내부의 변수들이 모두 static 변수는 아니다.

명시적으로 static inner class에 static으로 선언한애들만 static 변수가 되는것이다.

static inner class 의 일반변수들도 new로 인하여 인스턴스가 생성될때 할당되며 그때의 인스턴스를통해

맴버변수들에 접근할 수 있게된다. 

 

그러므로 static inner class라고 하더라도 멤버변수들이 outer class의 context같은 참조값을 들고있게된다면

위험한건 마찬가지다. 그러므로 WeakReference 변수로 받아줘야할 필요가 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
 
public class Main {
    static int c = 20;
    
    
    public static void main(String[] args) {
        
        TestClass tc1 = new TestClass();
        tc1.a = 3;
        
        TestClass tc2 = new TestClass();
        tc2.a = 4;
        
        System.out.println("tc1.a="+tc1.a+"  tc2.a="+tc2.a);
        System.out.println("tc1="+tc1+"  tc2="+tc2);
        
        System.out.println("tc1.b="+tc1.b+"  tc2.b="+tc2.b);
        
        // Main.TestClass.a = 3; //컴파일에러
        System.out.println("static TestClass.c=" + TestClass.c);
 
    }
    
    static class TestClass {
        int a;
        int b = c;
        static int c = 200;
        
    }
    
}
 
 
 

 

 

 

참고

 

https://movefast.tistory.com/41

 

14. Inner Class (Nested Class) and Interface - 중첩 클래스와 중첩 인터페이스

▶ 중첩 클래스 및 중첩 인터페이스를 사용하는 이유 - 자바는 객체 지향 프로그램으로 각 클래스가 관계를 맺으며 상호작용을 한다. 어떤 클래스는 많은 클래스와 상호작용을 하기도 하지만, 다른 어떤 클래스는..

movefast.tistory.com

일반 inner class는 outer class의 인스턴스를 먼저 생성하고나서야 inner class의 인스턴스를 생성할 수 있다.

 

static inner class는, outer class의 이름으로바로 접근하여 인스턴스를 생성할수있다.

 

이점만 봐도 사실 outer와 static inner class간의 메모리 영역은 다른곳에 상주하고있다고 생각을해도 될것같다.

 

 

 

 

SingleTon Class처럼 오래사는 인스턴스에게 가끔 context를 넘겨줘야할 상황이 있을수도있다.

그러나 엄청난 주의가 필요하다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 
public class SingletonLeakActivity extends AppCompatActivity {
    
    private SingleTonClass stc;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_static_ref_leak);
 
        stc = SingleTonClass.getInstance(this);
    }
}
 
class SingleTonClass {
    private Context mContext;
    private static SingleTonClass instance;
    private SingleTonClass (Context context) {
        mContext = context;
    }
    static synchronized public SingleTonClass getInstance(Context context) {
        if (instance == null)
            instance = new SingleTonClass(context);
        return instance;
    }
 
}
 
cs

 

위의같은경우라면 SignleTonClass 인스턴스가 SigleTonLeakActivtiy의 context를 계속 쥐고있는 상황이생겨

액티비티가 종료되어도 GC가 수거를 못할수도 있게된다.

 

이럴땐 2가지 솔루션이있다.

1.this대신 getApplicatoinContext()를 넘겨주는것이다. Applicatoin이 살아있는동안의 context이고 액티비티들의 라이프사이클과 관련이없기때문이다.

use the Application context. This context will live as long as your application is alive and does not depend on the activities life cycle. 

결론=> 오래살아야하는 인스턴스중 context가 필요하다면 application-context를 넘겨주는게 좋다.  (activity-context를 넘겨주지마라)

 

 

2.하지만 액티비티의 라이프사이클이 필요해서 this / activity-context 넘겼다면 어떻게해야할까?

명시적으로 null처리를 잘해야한다. 위의예제같은경우는 액티비티(컴포넌트)가 onDestroy될때 싱글톤객체에게 context를 null로 만들도록 명시적으로 처리를 해줘야 할 것이다.

 

 

 

 

참고

https://m.blog.naver.com/PostView.nhn?blogId=rjs5730&logNo=221304797233&categoryNo=0&proxyReferer=https%3A%2F%2Fwww.google.com%2F

 

(Android) ActivityContext? ApplicationContext?

안녕하세요. 안드로이드 스튜디오 툴을 해보신 분이면 Context를 한번 쯤 보셨을 거예요. 이렇게 친숙한 ...

blog.naver.com

http://sunphiz.me/wp/archives/tag/applicationcontext

 

applicationcontext – Dog발자

컨텍스트(Context)란? 컨텍스트란 작게는 어플리케이션 자신이 가지고 있는 이미지, 문자열, 레이아웃 같은 리소스 참조를, 크게는 안드로이드 시스템 서비스에 접속하기 위한 관문 역할을 하는 객체다. 이에 대해서는 이미 좋은 글이 있다. 컨텍스트(Context)를 얻는 방법에는 무엇이 있나? 액티비티나 서비스에서 getApplicationContext() 호출 : Application 객체가 Context 형으로 반환된다. 액티비티나 서비스에서 getA

sunphiz.me

https://android-developers.googleblog.com/2009/01/avoiding-memory-leaks.html

 

context에 대한설명과 메모리 leak

 

Avoiding memory leaks

The latest Android and Google Play news and tips for app and game developers.

android-developers.googleblog.com

 

+ Recent posts