안드로이드 메모리 릭(memory leak)

Lee Jun Hyeong·2022년 12월 23일
0

Android 톺아보기 😇

목록 보기
1/17

📌 Java Virtual Machine(JVM)과 메모리 관리

C나 C++에서는 OS레벨의 메모리에 직접 접근하기 때문에 free()라는 메소드를 호출하여 할당 받았던 메모리를 명시적으로 해제해주어야 한다. 그렇지 않으면 memory leak이 발생한다.
자바는 OS의 메모리 영역에 직접적으로 접근하지 않고 JVM을 이용해서 간접적으로 접근한다. JVM은 C로 쓰여진 또 다른 프로그램이다. 오브젝트가 필요해지지 않는 시점에서 알아서 free()를 수행하여 메모리를 확보한다. 웹 애플리케이션을 만들 때 모든 것을 다 직접 개발하여 쓰기보다 검증된 라이브러리나 프레임워크를 이용하는 것이 안전하고 펴리한 것처럼, 메모리 관리라는 까다로운 부분을 자바 가상머신에 모두 맡겨버리는 것이다.
프로그램 실행시 JVM 옵션을 주어서 OS에 요청한 사이즈 만큼의 메모리를 할당 받아서 실행하게 된다. 할당 받은 이상의 메모리를 사용하게 되면 에러가 나면서 자동으로 프로그램이 종료된다. 그러므로 현재 프로세스에서 메모리 누수가 발생하더라도 현재 실행중인 것만 죽고, 다른 것에는 영향을 주지 않는다.

📌 가비지 콜렉터(Garbage Collector)란?

  • 모든 어플리케이션은 작업을 하는데 메모리가 필요하다. 가비지 콜렉터는 메모리를 확보해준다.
  • Android의 경우 런타임에서 메모리가 부족한 경우에 가비지 콜렉터를 트리거해서 어플리케이션을 원활히 동작할 메모리를 다시 확보해 준다.

✅ GC 과정

  1. Stop The World
    GC을 위해 JVM이 애플리케이션의 실행을 멈추는 작업이다.
    GC가 실행될 때는 GC를 실행하는 쓰레드를 제외한 모든 쓰레드들의 작업이 중단되고, GC가 완료되면 작업이 재개된다.
    모든 쓰레드들의 작업이 중단되면 애플리케이션이 멈추기 때문에, GC의 성능 개선을 위해 튜닝을 한다고 하면 보통 stop-the-world의 시간을 줄이는 작업을 뜻한다. JVM 또한 이러한 문제를 해결하기 위해 다양한 실행 옵션을 제공하고 있다.

  2. Mark and Sweep
    Stop The World를 통해 모든 작업이 중단된 뒤,
    GC는 스택의 모든 변수 또는 Reachable객체가 각각 어떤 객체를 참조하는지 탐색하여 사용되는 메모리와 사용되지 않는 메모리로 구분하는 Mark과정을 진행한다.
    이후에 Mark되지 않은 즉, 사용되지 않는 객체들을 메모리에서 제거하는데 이러한 과정을 Sweep이라고 한다.

✅ 즉, 가비지 콜렉터 수집 3단계

  1. 메모리에 있는 모든 객체 참조를 나열해서 참조가 있는 활성 객체 표시
  2. 1에서 표시가 되지 않은 객체들을 메모리에서 제거
  3. 활성 객체 재정렬

📌 메모리 릭(memory leak)이란?

애플리케이션이 더 이상 필요하지 않은 객체에 대한 참조를 유지함으로써 결과적으로 해당 객체에 할당 된 메모리를 회수 할 수 없어 나타나는 오류이다.

한글 자막 지원, 대략 9:31까지 보면 됨.
참고 자료: https://youtu.be/v4kCRZ_O4Lc

✅ 예를 들면

실생활에 비유해보자. 일반적으로 락커룸에서는 사용자가 락커 이용을 마친 뒤 키를 꽂아두고 나오거나 반납을 해야한다. 하지만 고의 또는 실수로 키를 가지고 집으로 돌아오게 되면 다른 사용자는 해당 락커를 이용할 수 없다. 키를 반납하지 않는 사례들이 누적되면 이용할 수 있는 락커는 없어지게 된다. 다른 이용자가 락커룸에 들어왔는데 사용할 수 있는 락커가 없게 되는 것이다. 이런 상태를 컴퓨터 메모리 관점에서는 OOM(Out Of Memory)이라고 부른다. 사용하지 않은 메모리 양이 계속 증가하게 되면 최악의 경우 Out Of Memory(OOM)를 발생시키고 앱이 강제 종료되면서 할당되었던 메모리가 시스템으로 회수된다.

✅ 메모리 릭 사례

  1. Activity 메모리 누수 사례
class MainViewModel : ViewModel() {

    var activity:Activity? = null

}
class MainActivity : AppCompatActivity() {

    private val viewModel:MainViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        if(savedInstanceState==null){
            viewModel.activity = this // 하지 말라는 것 해보기
        }
    }

}

이제 Activity 재생성을 위해 화면회전 옵션을 활성화 하고 가로모드, 세로모드로 1회 왕복 한다.
다음과 같이 메모리 누수가 발생하는 것을 확인할 수 있다.

  1. View Binding
    참고

  2. RecyclerView
    RecyclerView.Adapter가 RecyclerView보다 더 수명이 긴 경우에 Adapter에서 View를 참조하면서 메모리 누수가 발생한다는 메시지를 LeakCanary를 통해 확인할 수 있다.
    그럴때는 다음과 같이 RecyclerView가 참조하고 있는 Adapter를 null로 초기화해주면 간단히 해결할 수 있다.

override fun onDestroyView() {
    recyclerView.adapter = null
    super.onDestroyView()
}
  1. 다른 사례들

✅ 메모리 누수가 지속된다면?

  1. 앱 버벅거림
    가비지 콜렉터는 Android 런타임에서 메모리가 부족할 때 작동된다.
    회수할 수 없는 메모리가 많으면 많을수록 Android 런타임에서 메모리가 자주 부족해 가비지 콜렉터를 많이 호출하게 된다.

    가비지 콜렉터가 발생하는 순간 안드로이드의 UI렌더링이나 이벤트 처리를 중지 시킨다. 사용자와 앱의 상호작용이 원활하게 이루어지도록 하려면 앱이 16ms 미만으로 프레임을 렌더링하여 초당 60프레임을 달성해야한다.

    즉, Android 런타임에 메모리가 부족하여 가비지 콜렉터가 자주 호출돼서 UI 렌더링을 연속적으로 멈추면 16ms를 유지하지 못하게 된다.

  2. 앱 중단
    안드로이드 앱의 응답을 액티비티 매니저윈도우 매니저 시스템 서비스로 모니터링되고 있다. 5초 이내에 입력 이벤트가 없거나 BroadcastReceiver10초내에 실행을 완료하지 못할 때 ANR 다이얼로그를 앱에 띄운다.

📌 메모리 릭 해결

✅ LeakCanary

LeakCanary란 Square사에서 만든 오픈소스 라이브러리이다. 안드로이드 프레임워크 내부 이해를 통해 메모리 누수의 원인을 줄이는 기능을 제공하여, 개발자가 OOM(Out Of Memory)에러로 인한 크래시를 줄일 수 있도록 도와준다.


참고
https://gift123.tistory.com/30
https://www.charlezz.com/?p=44748
https://leveloper.tistory.com/197
https://jade314.tistory.com/entry/%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%88%84%EC%88%98-Memory-leak
https://enumclass.tistory.com/9#toc-Inner%20Class%20%EB%88%84%EC%88%98
http://sjava.net/2016/05/%EB%B2%88%EC%97%AD-%EC%95%88%EB%93%9C%EB%A1%9C%EC%9D%B4%EB%93%9C-%EC%95%B1%EC%9D%B4-%EB%A9%94%EB%AA%A8%EB%A6%AC-%EB%88%84%EC%88%98leak%EB%A5%BC-%EB%A7%8C%EB%93%9C%EB%8A%94-8%EA%B0%80%EC%A7%80/
https://www.charlezz.com/?p=44748
https://velog.io/@dhwltnoooh/Memory-leak
profile
"왜" 사용하며, "어떻게" 사용하는지에 대해

0개의 댓글