본문 바로가기

개발 코딩 정보 공유/안드로이드 자바 코틀린

안드로이드 메모리 관리는 어떻게 ? (누수 예방법)

 

 

 

 

 

 

 

 

 

 

 

안드로이드 메모리 관리 (누수 방지

 

 

안녕하세요. 오늘은 안드로이드 메모리 관련 입니다.

기존에도 한번 썻던것 같은데요. 

메모리가 언제 leak이 나더라...다시 생각해보니... 

어느새 다 잊어버렸습니다.하하하ㅏㅎㅎㅎㅎ....

 

안드로이드의 경우 자바의 피를 이어받아... 자바의 성향을 그대로 따릅니다. (당연;;)

그렇다면 언제 메모리 누수가 발생하느냐? 뭐 무수히 많은 경우가 있겠지만

안드로이드 기준으로 흔히 사용하게 되는 inner class 가 문제인 것입니다.

웹 개발할 당시는 inner class를 1년에 한번 쓸일이 있을까 말까 했습니다만

안드로이드 개발시에는 하루에도 수십번 사용합니다. 그것도 아주 무분별 하게...

 

그렇다면 왜 inner class 가 문제가 되냐? 에 대해 알아보겠습니다.

JAVA 특성상 inner class 를 사용하게 되면 내부에 OUTER 객체를 암시적으로 참조하게 됩니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class AType{
 
    ...
 
    private class BType{
 
        //암시적으로 outer를 가지고 있습니다.
 
        //즉 보이지 않는 Atype의 객체를 가지고 있다는 말입니다.
 
        //그래서 우리가 outer 의 메서드나 변수들을 맘대로 가져다 쓸수 있었던 것입니다.ㅎㅎㅎ
 
    }
 
}
cs

 

그렇습니다. 보이지 않지만 가지고 있고 그것을 참조하고 있다는 말이지요.

 

"근데 이게 무슨 문제가 된다는 거죠???"

 

일반적인 상황에서는 별 문제가 없습니다만... 

leak이 많이 발생하는 부분은 비동기쓰레드를 사용하게 될때 입니다.

 

어떤상황인지 위에 클래스로 예를 들어보면

 

1.Atype 에서 BType의 쓰레드 이너클래스를 생성합니다.

2.BType에서는 긴작업이 예정되어있습니다.

3.드디어 BType의 쓰레드가 돌아가기 시작합니다.

4.결과를 기다리다가 지루해 우리가 앱을 종료하고 싶어집니다.

5.마침내 AType 액티비티를 꺼버렸습니다.(finish)

 

 

화면상에서 Atype 는 사라져서 없어졌습니다. 

즉 가비지 대상이라는 얘기죠.(메모리해제)

그러나 아까 말했듯이 inner class 상의 Atype 객체의 암시적 참조로 인해

GC 대상이 되지 않습니다. 로그를 찍어보면 액티비티는 분명히 onDestroy 되는걸 확인할수 있죠.

그러나 메모리가 해지 되지 않습니다. ㅠㅠㅠㅠㅠㅠ

 

 

이를 예방하는 방법은 있습니다.

바로 static class로 명시하는 것인데요.

static 클래스로 명시하게 되면 암시적인 outer 객체 참조를 하지 않게됩니다.

 

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class AType {
 
    ...
 
    private static class BType extends Thread{
 
        @Override
 
            public void run() {
 
            }
 
    }
 
}
cs

 

또한 아래와 같은 어너니머스 이너클래스도 마찬가지로 적용됩니다.

 

1
2
3
4
5
6
7
 private static Runnable runnable = new Runnable() {
 
    @Override
 
    public void run() {}
 
  }
cs

 

 

자바는 메모리관리를 전적으로 가비지컬렉션에게 맡기고 있는 이상...

메모리에 대해 잘 알고 사용하는것이 좋겠네요. (그냥 알아서 자동으로좀 해줘라...ㅠㅠㅠㅠㅠㅠ)

 

 

누수 방지를 위해 내부클래스를 static으로 만들었죠.

좋다 이겁니다. 그런데 내부의 쓰레드나 핸들러, 또는 비동기 작업자에서 outer를 참조해서 

무언가를 작업해야 하는 경우는 어쩔것이냐?

바로 아래와 같이 하시면됩니다. 참고 되시길

 

 

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
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
public class newActivity extends AppCompatActivity {
    private TextView uiText;
    private Button btnClose;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_new);
 
        uiText = findViewById(R.id.ui_text);
        btnClose = findViewById(R.id.btn_close);
        btnClose.setOnClickListener(new View.OnClickListener(){
 
            @Override
            public void onClick(View view) {
                finish();
            }
        });
 
        handlerT t = new handlerT("TestThread1"this);
        t.start();
    }
 
    private static final class handlerT extends Thread{
        private int cnt = 0;
        private WeakReference<newActivity> weakReference;
        
        public handlerT(String name, newActivity newAct) {
            super(name);
            weakReference = new WeakReference<>(newAct);
        }
 
        @Override
        public void run() {
            while(true){
                if(weakReference.get() == null){
                    break;
                }
                
                if(cnt >= 100){
                    break;
                }
 
                try{
                    Thread.sleep(1000);
                    ++cnt;
                    Log.i("newActivity","newActivity : " + weakReference.get().toString());
                    Log.i("newActivity","cnt : " + cnt);
                }catch (Exception e){
                }
            }
        }
    }
 
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.i("NewActivityTag","===NewAct onDestory===");
 
    }
}
 
 
 
 
cs