출처: http://developer.android.com/intl/ko/guide/components/processes-and-threads.html
프로세스 및 스레드

애플리케이션 구성 요소가 시작되고 애플리케이션에 실행 중인 다른 구성 요소가 없으면 Android 시스템은 하나의 실행 스레드로 애플리케이션의 Linux 프로세스를 시작합니다. 기본적으로 같은 애플리케이션의 모든 구성 요소는 같은 프로세스와 스레드에서 실행됩니다 ("기본" 스레드라고 합니다). 애플리케이션 구성 요소가 시작되고 (애플리케이션의 다른 구성 요소가 존재해서) 해당 애플리케이션의 프로세스가 이미 존재하면 해당 구성 요소는 프로세스 내에서 시작되고 같은 실행 스레드를 사용합니다. 하지만 애플리케이션 내의 여러 가지 구성 요소가 각자 별도의 프로세스에서 실행되도록 할 수도 있고, 어느 프로세스에든 추가 스레드를 만들 수 있습니다.

이 문서는 프로세스와 스레드가 Android 애플리케이션에서 작동하는 방식을 설명합니다.

프로세스


기본적으로 같은 애플리케이션의 모든 구성 요소는 같은 프로세스와 스레드에서 실행되고, 대부분의 애플리케이션은 이를 바꿔서는 안 됩니다. 그러나 어느 프로세스가 특정 구성 요소에 속하는지 확인해야 할 경우 매니페스트 파일에서 확인할 수 있습니다.

구성 요소 —<activity>와 <service><receiver> 및 <provider>—의 각 유형에 대한 매니페스트 항목은 구성 요소를 실행할 프로세스를 지정하는 android:process 속성을 지원합니다. 이러한 속성을 설정하여 각 구성 요소를 자체 프로세스에서 실행시키거나 다른 구성 요소를 제외한 일부 구성 요소만 프로세스를 공유하게 할 수 있습니다 또한, android:process를 설정하여 다른 애플리케이션의 구성 요소를 같은 프로세스에서 실행시킬 수 있습니다. 단, 이는 애플리케이션이 같은 Linux 사용자 ID를 공유하고 같은 인증서에 서명되었을 경우에 한합니다.

<application> 요소도 android:process 속성을 지원하여, 모든 구성 요소에 적용되는 기본값을 설정합니다.

Android는 어느 시점엔가 프로세스를 종료하기로 결정할 수도 있습니다. 즉 메모리가 부족하거나, 사용자에게 더욱 즉각적인 서비스를 제공하는 다른 프로세스가 이 프로세스의 중단을 필요로 하는 경우 등입니다. 그러면 중단된 프로세스에서 실행되고 있던 애플리케이션 구성 요소도 따라서 소멸됩니다. 그와 같은 구성 요소가 할 작업이 다시 생기면 그에 대한 프로세스도 다시 시작됩니다.

어느 프로세스를 삭제할지 결정할 때, Android 시스템은 사용자에 대한 이들의 상대적 중요성을 가늠합니다. 예를 들어, 눈에 보이는 액티비티를 호스팅하는 프로세스와 비교하여 화면에 보이지 않는 액티비티를 호스팅하는 프로세스를 쉽게 종료할 수 있습니다. 프로세스 종료 결정은 해당 프로세스에서 실행되는 구성 요소의 상태에 따라 달라집니다. 종료할 프로세스를 결정하는 데 사용하는 규칙은 아래에 설명되어 있습니다.

프로세스 수명 주기

Android 시스템은 최대한 오래 애플리케이션 프로세스를 유지하려고 시도하지만, 결국 오래된 프로세스를 제거하고 새 프로세스나 더 중요한 프로세스에 사용할 메모리를 확보해야 합니다. 유지할 프로세스와 종료할 프로세스를 결정하기 위해 시스템은 프로세스에서 실행되는 구성 요소와 해당 구성 요소의 상태에 기초하여 각 프로세스에 "중요 계층"을 부여합니다. 중요도가 낮은 프로세스가 먼저 제거되고, 그 다음으로 중요도가 낮은 프로세스를 제거하는 식으로 필요에 따라 시스템 리소스를 회복하는 것입니다.

중요 계층에는 다섯 가지 단계가 있습니다. 다음 목록은 중요도 순서에 따른 프로세스 유형을 나타낸 것입니다(첫 번째 프로세스가 가장 중요하고 마지막으로 종료됩니다).

  1. 전경 프로세스

    사용자가 현재 진행하는 작업에 필요한 프로세스입니다. 다음 조건 중 하나가 참일 경우 프로세스가 전경에 있는 것으로 간주합니다.

    일반적으로, 주어진 시간에 존재하는 전경 프로세스는 몇 개밖에 되지 않습니다. 이들은 최후의 수단으로서만 종료됩니다. 즉, 메모리가 너무 부족해 계속 실행할 수 없는 경우를 말합니다. 일반적으로 그 시점이 되면 기기가 메모리 페이징 상태에 도달한 것이므로 전경 프로세스 몇 개를 중단해야만 사용자 인터페이스의 반응성을 유지할 수 있습니다.

  2. 가시적 프로세스

    전경 구성 요소는 없지만 사용자가 화면에서 보는 것에 영향을 미칠 수 있는 프로세스입니다. 다음 조건 중 하나가 참이면 가시적 프로세스로 간주합니다.

    • 전경에 있지는 않지만 사용자에게 보이는 Activity를 호스팅할 경우 (onPause() 메서드가 호출되었을 경우). 예를 들어 이것은 전경 액티비티가 대화를 시작하여 이전 액티비티가 그 뒤에 보일 경우 발생합니다.
    • 눈에 보이는(또는 전경) 액티비티에 바인딩된 Service를 호스팅할 경우.

    가시적인 프로세스는 매우 중요도가 높은 것으로 취급하고 모든 전경 프로세스를 실행하는 데 필요할 경우에만 종료됩니다.

  3. 서비스 프로세스

    startService() 메서드로 시작되었지만 두 개의 상위 계층 분류에 들어가지 않는 서비스를 실행하는 프로세스입니다. 서비스 프로세스는 사용자가 보는 것과 직접 연결되어 있지는 않지만, 일반적으로 사용자가 신경 쓰는 작업을 하므로(배경에서 음악 재생 또는 네트워크에서 데이터 다운로드) 시스템은 모든 전경 및 가시적 프로세스와 함께 실행할 만큼 메모리가 충분하지 않을 경우에만 프로세스를 중단합니다.

  4. 배경 프로세스

    현재 사용자에게 보이지 않는 액티비티를 보유한 프로세스입니다(액티비티의 onStop() 메서드가 호출되었습니다). 이와 같은 프로세스는 사용자 환경에 직접적 영향을 미치지 않고, 시스템은 언제든 이 프로세스를 중단시켜 전경, 가시적 또는 서비스 프로세스를 위한 메모리를 확보할 수 있습니다. 보통 한번에 실행 중인 배경 프로세스가 많은 편이므로 이들은 LRU(최저 사용 빈도) 목록에 보관하여 사용자가 가장 최근에 본 액티비티가 있는 프로세스가 가장 마지막에 중단되도록 합니다. 액티비티가 수명 주기 메서드를 올바르게 구현하고 자신의 현재 상태를 저장할 경우, 사용자가 액티비티로 다시 이동할 때 액티비티가 모든 가시적 상태를 복원하므로 프로세스를 중단시키더라도 사용자 환경에는 눈에 띄게 영향을 미치지 않습니다. 상태 저장과 복원에 관한 정보는 액티비티문서를 참조하십시오.

  5. 빈 프로세스

    활성 애플리케이션 구성 요소를 보유하지 않은 프로세스입니다. 이런 프로세스를 유지하는 유일한 이유는 다음에 내부 구성 요소를 실행할 때 시작 시간을 절약하기 위한 캐싱 때문입니다. 시스템은 프로세스 캐시와 기본 커널 캐시 사이에서 전반적인 시스템 리소스의 균형을 맞추기 위해 이 프로세스를 중단시키는 경우가 많습니다.

Android는 프로세스에서 현재 활성 상태인 구성 요소의 중요도에 따라 프로세스에 가장 높은 수준을 부여합니다. 예를 들어, 프로세스가 서비스와 가시적 액티비티를 호스팅할 경우, 해당 프로세스는 서비스 프로세스가 아니라 가시적 프로세스 등급이 부여됩니다.

또한, 프로세스의 등급은 다른 프로세스가 이에 의존할 경우 상승할 수 있습니다. 즉, 다른 프로세스에 서비스를 제공하는 프로세스가 서비스 제공 대상 프로세스보다 등급이 낮은 경우는 있을 수 없습니다. 예를 들어 프로세스 A의 콘텐츠 제공자가 프로세스 B의 클라이언트에 서비스를 제공하거나 프로세스 A의 서비스가 프로세스 B의 구성 요소에 바인딩되어 있을 경우 프로세스 A는 항상 중요도가 프로세스 B와 같거나 그보다 높습니다.

서비스를 실행하는 프로세스가 배경 액티비티가 포함된 프로세스보다 높으므로, 장기 작업을 시작하는 액티비티는 작업자 스레드만 생성하기보다는 해당 작업에 대한 서비스를 시작하는 것이 좋습니다. 이는 특히 작업이 해당 액티비티보다 오래 지속될 경우 더욱 중요합니다. 예를 들어, 웹사이트에 사진을 업로드하는 액티비티가 업로드를 수행하는 서비스를 시작해야 사용자가 액티비티를 떠나더라도 배경에서 업로드를 지속할 수 있습니다. 서비스를 사용하면 액티비티에 어떤 일이 발생하든 해당 작업에 반드시 "서비스 프로세스" 우선 순위 이상이 부여됩니다. 이것이 브로드캐스트 수신기가 시간이 오래 걸리는 작업을 스레드에 넣기보다는 서비스를 사용해야 하는 것과 같은 이유입니다.

스레드


애플리케이션이 시작되면 시스템이 애플리케이션에 대한 실행의 스레드를 생성하며, 이를 "기본"이라고 합니다. 이 스레드는 드로어블 이벤트를 포함하여 적절한 사용자 인터페이스 위젯에 이벤트를 발송하는 역할을 맡기 때문에 중요합니다. 이것은 Android UI 도구 키트의 구성 요소(android.widget과 android.view 패키지의 구성 요소)와 개발자의 애플리케이션이 상호 작용하는 스레드이기도 합니다. 따라서 기본 스레드는 UI 스레드라고 불릴 때도 있습니다.

시스템은 구성 요소의 각 인스턴스에 대해 별도의 스레드를 생성하지 않습니다. 같은 프로세스에서 실행되는 모든 구성 요소는 UI 스레드에서 시작되고, 각 구성 요소를 호출하는 시스템이 해당 스레드에서 발송됩니다. 따라서 시스템 콜백에 응답하는 메서드(사용자 작업을 보고하는 onKeyDown() 또는 수명 주기 콜백 메서드)는 항상 프로세스의 UI 스레드에서 실행됩니다.

예를 들어, 사용자가 화면의 버튼을 터치하면, 앱 UI 스레드가 위젯에 터치 이벤트를 발송하고, 위젯은 눌린 상태를 설정하고 이벤트 대기열에 무효화 요청을 게시합니다. UI 스레드가 이 요청을 대기열에서 해제하고 위젯에 스스로를 다시 그려야 한다고 알립니다.

앱이 사용자 상호작용에 응답하여 집약적인 작업을 수행할 때는 이 단일 스레드 모델은 애플리케이션을 제대로 구현하지 않으면 낮은 성능을 보일 수 있습니다. 특히, 모든 것이 UI 스레드에서 발생하고 네트워크 액세스나 데이터 베이스 쿼리 등의 긴 작업을 수행하면 UI가 통째로 차단됩니다. 스레드가 차단되면 드로잉 이벤트를 포함하여 모든 이벤트가 발송되지 않습니다. 사용자가 보기에는 애플리케이션이 중단된 것처럼 보입니다. 더 나쁜 경우, UI 스레드가 몇 초 이상 차단되어 있으면 (현재 약 5초) 사용자에게 악명 높은 "애플리케이션이 응답하지 않습니다"(ANR) 대화가 표시됩니다. 그러면 사용자가 여러분의 애플리케이션을 종료 할 수도 있고, 불만족한 경우 앱을 제거할 수도 있습니다.

또한, Andoid UI 도구 키트는 스레드로부터 안전하지 않습니다. 따라서 UI를 작업자 스레드에서 조작해서는 안 됩니다. 사용자 인터페이스 조작 작업은 모두 UI 스레드에서 해야만 합니다. 결론적으로, Android의 단일 스레드 모델에는 두 가지 단순한 규칙이 있습니다.

  1. UI 스레드를 차단하지 마십시오.
  2. Ui 스레드 외부에서 Android UI 도구 키트에 액세스하지 마십시오.

작업자 스레드

위에 설명한 단일 스레드 모델로 인해, 애플리케이션 UI의 반응성을 위해서는 UI 스레드를 차단하지 않는 것이 매우 중요합니다. 수행해야 할 작업이 있는데 이들이 즉각적인 조치를 요하지 않는 경우, 이런 작업은 반드시 별도의 스레드에서 수행해야 합니다("배경" 또는 "작업자" 스레드).

예를 들어, 아래는 별도의 스레드에서 이미지를 다운로드하고 이를 ImageView에 표시하는 클릭 수신기에 대한 몇 가지 코드입니다.

public void onClick(View v) {
   
new Thread(new Runnable() {
       
public void run() {
           
Bitmap b = loadImageFromNetwork("http://example.com/image.png");
            mImageView
.setImageBitmap(b);
       
}
   
}).start();
}

처음에는 네트워크 작업을 처리하기 위한 새 스레드를 생성하므로 아무 문제 없이 작동하는 것처럼 보입니다. 하지만, 이것은 단일 스레드된 모델의 두 번째 규칙 즉, 'UI 스레드 외부에서 Android UI 도구 키트에 액세스하지 마세요.'를 위반합니다. 이 샘플은 UI 스레드 대신 작업자 스레드에서 ImageView를 수정합니다. 이렇게 되면 정의되지 않고 예기치 못한 동작이 발생하는 결과를 초래할 수 있고, 이는 추적하기 어려워 시간도 오래 걸립니다.

이 문제를 해결하기 위해 Android는 다른 스레드에서 UI 스레드에 액세스하는 여러 가지 방식을 제안합니다. 다음은 몇 가지 유용한 메서드 목록입니다.

예를 들어 위의 코드를 수정하려면 View.post(Runnable) 메서드를 사용하면 됩니다.

public void onClick(View v) {
   
new Thread(new Runnable() {
       
public void run() {
           
final Bitmap bitmap = loadImageFromNetwork("http://example.com/image.png");
            mImageView
.post(new Runnable() {
               
public void run() {
                    mImageView
.setImageBitmap(bitmap);
               
}
           
});
       
}
   
}).start();
}

이 구현은 이제 스레드로부터 안전합니다. 네트워크 작업은 별도의 스레드에서 수행된 반면 ImageView는 UI 스레드에서 조작되었기 때문입니다.

그러나, 작업이 복잡해질수록 이런 종류의 코드가 더 복잡해질 수 있고 유지 관리하기 까다로워질 수 있습니다. 더 복잡한 상호 작용을 작업자 스레드로 처리하려면, 작업자 스레드에서 Handler를 사용하여 UI 스레드에서 전달 받은 메시지를 처리하는 방안을 고려해보십시오. 하지만 최선의 해결책은 AsyncTask 클래스를 확장하는 방법일 것입니다. 이것은 UI와 상호 작용해야 하는 작업자 스레드 작업의 실행을 단순화합니다.

AsyncTask 사용

AsyncTask를 사용하면 사용자 인터페이스에서 비동기식 작업을 수행할 수 있게 해줍니다. 이것은 작업자 스레드에서 차단 작업을 수행하고 그런 다음 그 결과를 UI 스레드에 게시하며, 개발자가 직접 스레드 및/또는 처리기를 처리할 필요가 없습니다.

이를 사용하려면 우선 AsyncTask를 하위 클래스로 한 다음 doInBackground() 콜백 메서드를 구현해야 합니다. 이것은 여러 가지 배경 스레드에서 실행됩니다. UI를 업데이트하려면 onPostExecute()를 구현해야 합니다. 이는doInBackground()로부터의 결과를 전달하며 UI 스레드에서 실행되므로, 안전하게 UI를 업데이트할 수 있습니다. 그런 다음 UI 스레드에서 execute()를 호출하여 해당 작업을 실행하면 됩니다.

예를 들어, 이런 방식으로 AsyncTask를 사용하여 이전의 예시를 구현할 수 있습니다.

public void onClick(View v) {
   
new DownloadImageTask().execute("http://example.com/image.png");
}

private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
   
/** The system calls this to perform work in a worker thread and
      * delivers it the parameters given to AsyncTask.execute() */

   
protected Bitmap doInBackground(String... urls) {
       
return loadImageFromNetwork(urls[0]);
   
}
   
   
/** The system calls this to perform work in the UI thread and delivers
      * the result from doInBackground() */

   
protected void onPostExecute(Bitmap result) {
        mImageView
.setImageBitmap(result);
   
}
}

이제 UI는 안전하고 코드는 더욱 단순해졌습니다. 작업을 작업자 스레드에서 수행되어야 하는 부분과 UI 스레드에서 수행되어야 하는 부분으로 구분하기 때문입니다.

이 클래스를 사용하는 법을 완전히 숙지하려면 AsyncTask 참조를 읽어보시는 것이 좋습니다. 개괄적인 작동 방식은 아래에 간략히 소개해 놓았습니다.

주의: 작업자 스레드를 사용할 때 마주칠 수 있는 또 한 가지 문제는 런타임 구성 변경으로 인해 액티비티가 예기치 못하게 다시 시작되는 것입니다 (예를 들어 사용자가 화면 방향을 바꾸는 경우). 이 경우 작업자 스레드를 소멸시킬 수 있습니다. 스레드가 재시작될 때 작업을 지속하는 방법과 액티비티가 제거되었을 때 작업을 적절히 취소하는 방법은 Shelves 샘플 애플리케이션의 소스 코드를 참조하십시오.

스레드로부터 안전한 메서드

어떤 경우에는 구현하는 메서드가 하나 이상의 스레드에서 호출되는 일도 있습니다. 따라서 이를 스레드로부터 안전하게 작성해야만 합니다.

이것은 주로 원격으로 호출할 수 있는 메서드에 대해 참입니다. 예를 들어 바인딩된 서비스 내의 메서드 등이 해당됩니다. IBinder에서 구현된 메서드가 IBinder가 실행되는 프로세스에서 호출될 경우, 해당 메서드는 발신자의 스레드에서 실행됩니다. 그러나 호출이 다른 프로세스에서 발생하면, 해당 메서드는 시스템이 IBinder와 같은 프로세스에 유지하는 스레드 풀에서 선택된 스레드에서 실행됩니다(프로세스의 UI 스레드에서 실행되지 않습니다). 예를 들어, 어느 서비스의 onBind() 메서드는 해당 서비스 프로세스의 UI 스레드에서 호출되고, onBind()가 반환하는 객체에서 구현된 메서드는(예: RPC 메서드를 구현하는 하위 클래스) 해당 풀 안의 여러 스레드에서 호출되게 됩니다. 서비스에 클라이언트가 하나 이상 있을 수 있으므로, 하나 이상의 풀이 동시에 같은 IBinder 메서드에 참여할 수 있습니다. 그러므로 IBinder 메서드는 스레드로부터 안전하게 구현되어야 합니다.

마찬가지로 콘텐츠 제공자는 다른 프로세스에서 발생한 데이터 요청을 수신할 수 있습니다. ContentResolverContentProvider 클래스가 세부적인 프로세스 간 통신의 관리 방식을 숨길 수는 있지만, 이러한 요청에 응답하는ContentProvider 메서드(—query()insert()delete()update() 및 getType() 메서드—)가 프로세스의 UI 스레드가 아니라 콘텐츠 제공자 프로세스의 스레드 풀에서 호출됩니다. 이러한 메서드가 동시에 몇 개의 스레드에서 호출될 수 있으므로, 스레드로부터 안전하게 구현되어야 합니다.

프로세스 간 통신


Android는 원격 프로시저 호출(RPC)을 사용한 프로세스 간 통신(IPC) 메커니즘을 제공합니다. 여기서 메서드는 액티비티나 다른 애플리케이션 구성 요소에 호출되지만 원격으로 (또 다른 프로세스에서) 실행되고, 결과는 모두 발신자에게 되돌려 보냅니다. 메서드 호출과 메서드의 데이터는 운영 체제가 이해할 수 있는 수준으로 분해되고, 로컬 프로세스와 주소 공간에서 원격 프로세스와 주소 공간으로 전송된 다음 다시 결합되어 여기서 호출에 다시 응답합니다. 그런 다음 반환 값이 반대 방향으로 전송됩니다. Android가 이와 같은 IPC 트랜잭션을 수행하는 데 필요한 모든 코드를 제공하므로, 개발자는 그저 RPC 프로그래밍 인터페이스를 정의하고 구현하는 데에만 집중하면 됩니다.

IPC를 수행하려면 애플리케이션이 반드시 서비스에 바인딩되어야만 하며, 이때 bindService()를 사용해야 합니다. 자세한 정보는 서비스 개발자 가이드를 참조하십시오.


'Programing > Android' 카테고리의 다른 글

BroadcastReceiver  (0) 2016.05.06
ANR (Application Not Responding)  (0) 2016.05.06
Processes and Application Life Cycle  (0) 2016.05.04
Google I/O - Memory Management For Android  (0) 2016.05.03
Avoiding Memory Leaks  (0) 2016.05.03
  • Processes

    출처: 
    http://namsieon.com/289http://developer.android.com/intl/ko/guide/topics/processes/process-lifecycle.html


    안드로이드 시스템은 메모리가 부족하여 실행되어야 할 프로세스가 실행되지 않는 것을 방지하기 위해 오래된 프로세스를 제거하는 방식을 사용합니다.



    여기에서 어떤 프로세스를 유지하고 어떤 프로세스를 죽일것인지는 무엇을 보고 판단하는 걸까요?

    바로 각각의 프로세스내에서 실행되는 컴포넌트 상태의 '중요성 계층구조(importance hierarchy)' 를 이용합니다.


    가장 낮은 중요성을 가진 프로세스가 가장 먼저 제거되고, 그 다음순으로 진행되지요.

     

    그 계층구조에는 다섯 단계가 존재합니다.



    1. 포그라운드 ( foreground ) 프로세스


    포그라운드 프로세스는 현재 사용자와 커뮤니케이션하는 프로세스 입니다.

    아래와 같은 조건을 가지고 있다면 포그라운드 프로세스로 간주합니다.


    ● 프로세스가 사용자와 상호작용하는 액티비티를 실행중
    ● 사용자와 상호작용하는 액티비티에 연결된 서비스를 포함하고 있음
    ● 생명주기 메소드 ( onCreate(), onStart(), onDestroy() ) 중 하나를 실행중인 서비스 객체를 가지고 있음
    ● onReceive() 메소드를 실행중인 브로드캐스트리시버 객체를 포함하고 있음


    2. 비지블(visible) 프로세스


    비지블 프로세스는 포그라운드 프로세스를 보유하지 않았지만 사용자에게는 보일 수 있는 프로세스 입니다.

    아래와 같은 조건을 가지고 있다면 비지블 프로세스로 간주합니다.


    ● 포그라운드가 아니지만 여전히 보여지는 액티비티를 보유 중
    ● 비지블 액티비티에 연결된 서비스를 포함하고 있음


    ☞  비지블 프로세스는 포그라운드 프로세스가 더 이상 실행 될 수 없을 정도의 상황이 아니라면 강제 종료 되진 않습니다.

    3. 서비스 ( service ) 프로세스


    서비스프로세스는 startService() 메소드를 사용해서 시작된 서비스를 실행중이면서, 포그라운드 프로세스와 비지블 프로세스에 속하지 않는프로세스 입니다.


    서비스 프로세스는 사용자에게 보이진 않지만, 사용자에게 영향을 줄 수 있는 ( mp3 재생 등 ) 작업을 하고 있다고 볼 수 있는데요, 따라서 그라운드 프로세스 및 비지블 프로세스를 유지할 메모리가 없지 않은 이상에는 강제종료 되진 않겠지요.



    4. 백그라운드 ( background ) 프로세스

    백그라운드 프로세스는 사용자의 눈에 보이지 않는 액티비티를 가진 프로세스 입니다. 이것은 사용자에게 직접적인 영향을 주지 않고, 포그라운드 -> 비지블 -> 서비스 프로세스 유지를 위해 언제든지 강제종료 될 수가 있습니다. 

    이런 이유로, 가장 최근에 사용자에게 보여진 액티비티를 가진 프로세스가 가장 나중에 종료될 수 있도록 LRU(Least Recently Used : 가장 최근에 적게 사용되는) 라는 목록으로 관리됩니다.

    이것은 오래된 보여지지 않는 액티비티의 강제종료가 사용자에게 별다른 영향을 주지 않는다고 생각하는 것이지요.


    5. empty 프로세스


    empty 프로세스는 활성화된 것들을 보유하지 않은 프로세스입니다.
    이 프로세스는 캐쉬 역할을 제외하면 하는것이 없기 때문에 강제종료 됩니다. 
    (즉, 중요성이 가장 낮습니다)



    추가적으로, 프로세스의 중요도는 다른 프로레스에 의해 높아질 수 있습니다. 

    다른 프로세스를 지원하는 프로세스는 지원하는 프로세스보다 낮은 중요도를 가지면 안되겠지요.

    예를들어 "A" 프로세스에 있는 서비스가 "B"에 연결되어 있다면 "A"와 "B"는 같은 중요도를 가져야 합니다.


    서비스 프로세스가 백그라운드 프로세스보다 중요도가 높기 때문에 오래걸리는 작업처리는 별도의 스레드에 할당하기 보단 서비스를 이용하여 처리하는것이 좋겠네요. 서비스를 이용하면 액티비티와는 무관하게 최소한 '서비스 프로세스' 우선 순위는 보장된다는 뜻이니까요. 

    때문에 오래걸리는 작업처리 ( 네트워크 다운/업로드 등 ) 는 스레드보다 서비스로 구현해야 하는 이유가 됩니다.

  • Application Life Cycle
    출처: 
    http://starkapin.tistory.com/134

    1. 수명 주기


    메모리를 자동으로 할당해주고 자동으로 비워준다는 것은 가상머신을 사용하는 우리들에게 있어 약일 수도 있지만 오히려 반대로 따지고 보면 당장 반환해야 할 순간에 손만 빨고 있을 지도 모른다는 소리다. 하지만 여지껏 많은 개발자들은 그런 환경에서 완성도 높은 애플리케이션을 개발해 왔으니 이러한 주기를 잘 파악해서 프로그램을 작성해야 한다.

    안드로이드 애플리케이션은 수명 주기를 제어하는 것을 제한 하고 있다. 

    빠른 반응 처리를 위해 달빅 가상 머신은 애플리케이션을 바로 죽이지 않고 가지고 있는다. 종료한 애플리케이션임에도 불구하고 사용자가 다시 누를수 있다는 가정을 하고 있는 것이다. 

    안드로이드는 우선 순위가 높은 것 부터 낮은 순으로 관리해서 높은 순위에 있는 애플리케이션에게 메모리를 우선적으로 할당해준다. 제한된 메모리 관리를 효율적으로 운용하기 위해 낮은 순위에 있는 애플리케이션은 우선 순위가 높은 애플리케이션이 더 많은 메모리가 필요할 때 종료되고 메모리를 반환하게 되어있다.


    2. 프로세스 상태

    안드로이드 애플리케이션은 단일 프로세스로서 하나의 달빅 인스턴스 위에서 베타적으로 실행된다. 

    2-1. 활성 프로세스(Active processes)
    중요한 우선 순위, foreground 상태로 현재 실행중인 애플리케이션을 뜻한다. 

    2-2. 화면에 보이는 프로세스(Visible processes)
    높은 우선 순위, 화면에 보이고는 있으나 비활성화된 프로세스들을 뜻한다.

    2-3. 시작된 서비스 프로세스(Stated Service processes)
    높은 우선 순위, 화면에 보이는 인터페이스 없이도 계속 되어야 하는 지속적인 처리를 지원

    2-4. 백그라운드 프로세스(Background processes)
    낮운 우선 순위, 화면에 보이는 액티비티를 가지고 있지 않으면서, 동시에 실행중인 서비스를 가지고 있지 않는 프로세스

    2-5. 빈 프로세스(Empty processes)
    낮은 우선 순위, 안드로이드 애플리케이션이 다시 띄워질 때 구동 시간을 향상시키기 위하여 이 캐시를 유지.


    3. 애플리케이션 수명 주기 이벤트 

    □ onCreate : 애플리케이션 생성시 호출, 모든 상태 변수와 공유 리소스를 초기화 

    □ onTerminate : 애플리케이션 객체가 종료될 때 호출(항상 호출 된다는 보장을 할 수 없음)
    리소스 회수를 위한 목적으로 커널에 의해 종료되는 경우 이 함수는 호출되지 않는다.

    ★ 애플리케이션 생명주기와 프로세스 생명주기는 일치하지 않는다!

    □ onLowMemory : 시스템 리소스가 부족할 때 애플리케이션이 추가로 메모리를 해제하는 기회를 준다. 모든 백그라운드 프로세스가 종료되었는데도 메모리가 부족하면 호출됨

    □ onConfigurationChanged : 애플리케이션 차원에서 구성 변경을 다룰 필요가 있을 때

    import android.app.Application;
    import android.content.res.Configuration;
    
    public class MyApplication extends Application{
    	
    	private static MyApplication singleton;
    	
    	public static MyApplication getInstance() {
    		return singleton;
    	}
    	
    	@Override
    	public final void onCreate() {
    		super.onCreate();
    		singleton = this;
    	}
    	
    	@Override
    	public final void onTerminate() {
    		super.onTerminate();
    	}
    	
    	@Override
    	public final void onLowMemory() {
    		super.onLowMemory();
    	}
    	
    	@Override
    	public final void onConfigurationChanged(Configuration newConfig) {
    		super.onConfigurationChanged(newConfig);
    	}
    }


'Programing > Android' 카테고리의 다른 글

ANR (Application Not Responding)  (0) 2016.05.06
Process and Thread  (0) 2016.05.04
Google I/O - Memory Management For Android  (0) 2016.05.03
Avoiding Memory Leaks  (0) 2016.05.03
Activity, Fragment Lifecycles  (0) 2016.05.03

Heap Size Limit


안드로이드 앱에는 Heap Size 의 Limit 가 존재합니다. Heap Size 의 Limit 은 Device dependent합니다. 단말마다 다른  heap size limit 을 가지고 있다는 것이죠. 최초의 안드로이드 단말 G1 의 경우는 어플리케이션 하나가 가질 수 있는 heap memory 는 16MB 였습니다. 다시 말해, 어플리케이션 하나가 16MB 이상의 Heap 을 사용하는 경우 OutOfMemoryError 가 발생하며, Application Crash 현상이 발생한다는 것입니다. 모바일 단말의 하드웨어 스펙이 발달하면서, Heap Size Limit 은 증가하기 시작했습니다. Droid 의 경우 24M, Nexus One 의 경우 32M, Xoom 의 경우 48M 까지 증가했으며, 현재 High-end 급의 단말들은 더 높은 메모리 사이즈를 지원하겠죠.

이 단말의 Heap Size Limit 은 API 코드로도 확인할 수 있습니다.

int ActivityManager.getMemoryClass()
// returns memory heap limit size in MB 





더 큰 Heap Memory 를 사용할 순 없는가?


Android SDK 3.0 HoneyComb 이상부터는 manifest 설정을 통해 기본 단말에서 제공하는 Heap memory size 보다 더 큰 용량을 사용할 수 있습니다. application tag 에 largerHeap = "true" 값을 넣어주면 됩니다.

<application
        ....
        largerHeap="true"


이 largerHeap 의 값도 device dependent 하다고 하니, ActivityManager.getMemoryClass() 를 통해서 사용 가능한 메모리 용량을 확인해 보시면 되겠습니다.


largerHeap="true" 옵션은 주의해서 사용하셔야 합니다.
먼저 Larger Heap Size 는 그만큼 GC 시간을 더 소비한다는 의미이며, 다른 App들을 죽일 수 있습니다.
따라서 신중히 사용되어야 합니다. 





Garbage Collection.


2012/01/12 - [프로그래밍 놀이터/자바] - [Java] Garbage Collection ( GC ) 가 뭐 하는 녀석인지 한 번 해부해보자.

Pre-GingerBread


Android SDK 2.3 GingerBread 이전의 Garbage Collection 은 다음의 특징을 가집니다.

- Stop-the-world
- Full heap Collection
- Pause times often > 100ms.


From GingerBread


GingerBread을 포함하여 이후의 버전들의 Garbage Collection 은 다음의 특징을 가집니다.

- Concurrent ( mostly )
- Partial collections.
- Pause times usually < 5ms.


GC 의 개선은 매우 혁신적이라고 할 수 있습니다.
먼저 Stop-the-world 가 아닌 Concurrent 방식으로 바뀌었기 때문에, 반응성 측면이 상당히 좋아졌습니다.
게다가 partial collection 형태로 GC 시간이 단축되어 pause time 이 보통 5ms 라고 하네요.
이제 개발하면서, GC가 발생할까 걱정하는 시기는 지났다고 보여집니다.
( 물론 메모리 관리를 잘 해서 GC 는 가급적 불리지 않는 것이 가장 좋습니다. )







Bitmap 에 대한 이야기 ( Native Memory )


안드로이드 앱에서 Memory 를 가장 많이 소모하는 녀석이 바로 bitmap 이라고 할 수 있습니다.
Phone 용 App 에서는 보통 50% 가 BItmap 이 소모하였으며,
Tablet 과 같이 큰 화면에서는 더 많은 bitmap 을 display 하기 때문에 70% 이상을 소모한다고 하네요.

Pre-HoneyComb


Android SDK 3.0 HoneyComb 이전에는 Bitmap Object 를 실제 Bitmap 과 상관없이 Heap Memory 에 동일 사이즈로 할당을 합니다. 즉 Container 의 역할만 하는 것이지요. 그리고 실제 BItmap의 Pixel 값들은 Native 에 할당이 되어 있는 형태입니다.



- freed via recycle() or finalizer
- hard to debug
- full, stop-the-world GCs


From Honey Comb


HoneyComb 을 포함한 이후 버전에서는 Bitmap Object 를 다른 방식으로 관리하고 있습니다. HoneyComb 이전 버전과 마찬가지로, Bitmap Object 는 동일하게 생성하지만, Native 에 할당하던 Pixel memory 를 Davik Heap 으로 위치를 옮긴 것이지요.



- freed synchronously by GC
- easier to debug
- concurrent & partial GCs





Log 를 통한 메모리 분석


D/dalvikvm( 9050 ): GC_CONCURRENT freed 2049K, 65% free 3571K/9991K, external 4703K/5261K paused 2ms+2ms.


GC에 대한 로그정보를 통해 "개략적인" 메모리 사용 실태를 확인할 수 있습니다.


Reason For GC


위 로그의 "GC_CONCURRENT" 파트가 GC를 초래한 이유가 되겠습니다.
GC_CONCURRENT 이외에도 다른 이유로 GC 가 초래되는데 다음과 같은 이유들이 있습죠.

- GC_CONCURRENT
- GC_FOR_MALLOC : GC가 수행 or 끝나기 전에 malloc 이 불려서 memory 가 full 이 된 경우
- GC_EXTERNAL_ALLOC : external native memory alloc 이 발생하여 memory 가 full 이 된 경우
  ( HoneyComb 부터는 방식이 바뀌어, Bitmap 이나 NIO 의 ByteBuffer 류에는 이 GC 가 발생하지 않습니다. )
- GC_HPROF_DUM_HEAP
- GC_EXPLICIT


Amount Freed


"freed 2049K" 정보가 GC 를 통해 해제된 메모리 량입니다.
약 2MB 정도가 해제되었네요.


Heap Statistics


"65% free 3571K/9991K" 이 부분이 Heap의 통계를 보여줍니다.
위에서 Free 된 2049K 가 현재 사용하던 메모리 총 량의 65% 라는 것을 나타내며,
GC 후 남아있는 사용하는 메모리가 3571K 이고, total memory size 가 9991K 라는 것을 나타냅니다.


External Memory Statistics


"external 4703K/5261k" 부분이 external memory statistics 이며,
4703K 가 현재 alloc 된 memory, 5261K 가 soft limit 를 나타냅니다.


Pause Time


"paused 2ms + 2ms" 부분이 pause time을 나타냅니다.
이렇게 2개의 숫자가 더해지는 경우는 Concurrent GC 일 때 발생을 하며, Stop-the-world GC의 경우 1개의 숫자만 표시됩니다. 첫 2ms 는 GC의 준비시간을 의미하며, 두번째 GC는 실제 GC의 collection 작업의 수행 시간을 말합니다. 합하여 4ms 로 매우 짧은데, Stop-the-world GC 의 경우 훨씬 긴 pause time 을 갖는다고 합니다. ( 100ms 를 훌쩍 넘는다고 하네요. )








MAT ( Memory Analyzer ) 와 Heap Dump


2012/02/24 - [프로그래밍 놀이터/안드로이드] - [Android/안드로이드] MAT ( Mermory Analyzer Tool ) Android, Eclipse 사용 방법.

Android 에서는 Heap Memory 정보를 얻을 수 있는 Heap Dump 를 제공합니다.
DDMS 상에서 Heap Dump 아이콘을 클릭하여 dump file 을 얻을 수도 있고, 
코드상에서 Debug.dumpHprofData() 를 통해 추출해낼 수도 있습니다.

이렇게 얻어진 heap dump file 은 heap memory information 을 가진 binary로, MAT 에서 읽기 위해서는 conversion이 필요합니다. command 창에서 다음과 같이 입력하면 되며, hprof 의 경우 android sdk 에 포함되어 있습니다.

> hprof -conv orig.hprof converted.hprof



Prerequisite


MAT 을 사용하기에 앞서 다음과 같은 용어들과 개념들을 이해하셔야 합니다.

 Leak : 사용하지 않는 Object 를 참조하고 있어 GC 를 방해하는 것. ( C, C++ 의 leak 과는 조금 차이가 있습니다. )
- Context 를 자체 생명주기보다 오래 참조하는 것은 leak 을 초래하는 지름길.
- Activity, Conterxt, View, Drawable 등을 long-live reference 유지하는 것은 매우 위험한 일.
- Non-static inner class와 Runnable 은 enclosing object 를 참조하기 때문에 생명주기와 synchronize 시키는 것이 중요.
- 무분별한 Caches 는 OutOfMemory 와 Leak 을 초래하기 쉬움.

Shallow heap : object 하나가 가지는 byte 용량을 나타냄.
Retained heap : GC가 어떤 object 를 해지했을 때, 해지되는 총 메모리량으로, 한 object 가 참조하는 녀석들을 tree 형태로 따라가며 더한 값으로, 매우 중요한 정보를 갖는다.


[특정 process id 정보만 log 보기]

> adb logcat | grab [Process ID]




MAT 사용하기.


Histogram View


사용하는 모든 메모리의 합을 object 별로 sum 하여 나타내준 graph 입니다. 
사실 graph 라고는 하지만 list 형태로 나오죠. 

Memory leak 을 추적할 때는 추적해보죠.

( 참고로, byte[] 가 최상단으로 올라오기 쉬운데, 이는 bitmap 이 이제 heap memory 에 assign 되기 때문입니다. )

object를 우클릭 -> [List Object] -> [with incoming reference] 


어떤 녀석이 해당 object 를 참조하고 있는지를 보여줍니다. with outgoing reference 는 해당 object 가 참조하는 녀석들이 나오겠죠?


object 우클릭 -> [Path To GC Roots] -> [Exclude Weak References]


Weak Reference 야 어차피 GC 와 크게 상관이 없으니, exclude 하고, 유용한 정보만 확인합니다. 이것을 통해 GC Root 를 찾아냄으로서, 의도하지 않은 Root 를 가지고 있지는 않은지 확인 할 수 있습니다.


Filter 기능


Activity나 Context 의 경우는 보통 하나를 유지합니다. 하지만, 경우에 따라 Leak 이 발생한 경우에는 Activity의 갯수가 2개 혹은 그 이상이 되는 경우를 발견할 수 있습니다. Activity 이름이나, 갯수를 명확히 알고 그것이 유지되어야 하는 경우에는 Histogram View의 상단에 Filter 기능을 사용하여 Filtering 하면, 해당 Object 의 갯수를 쉽게 파악할 수 있습니다.




Dominator tree


Dominator tree 는 어떤 object 가 어떤 object 를 참조하고 있는지를 보여주는 tree 입니다. 사실 이것이 실제 참조 그대로를 보여주는 것이 아니고, 살짝 fake 라고 할 수 있는 logic 으로 tree 를 구성하는데, 사실 이 fake 된 tree 가 memory leak 을 잡기에는 훨씬 수월합니다. 말로 설명은 어렵고, 아래 그림을 통해서 이해하시길 바랍니다.


안드로이드의 경우 resource 를 많이 사용하기 때문에, Dominator tree 에서는 resource 가 보통 가장 위로 올라오게 됩니다. "항상" 은 아니지만 대부분 이 Resource object 자체는 크게 신경쓰지 않아도 됩니다.

Object 우클릭 -> [Path To GC Roots] -> [exclude weak references]


Histogram 과 마찬가지로, GC Roots 를 탐색하여, 참조의 root 를 찾을 수 있습니다.





참고 사이트


구글 Video Clip 에서는 친절하게도, 참조 사이트까지도 알려주고 있습니다.
저도 아직 방문하지 않앗지만, 이 글을 포스팅 한 이후에 한번 방문해보려고 합니다.
제목부터가 상당히 attractive 합니다.

Markus Kohler's Java Performance Blog
http://kohlerm.blogspot.com/


출처 : http://aroundck.tistory.com/378

'Programing > Android' 카테고리의 다른 글

Process and Thread  (0) 2016.05.04
Processes and Application Life Cycle  (0) 2016.05.04
Avoiding Memory Leaks  (0) 2016.05.03
Activity, Fragment Lifecycles  (0) 2016.05.03
Handler, Looper  (0) 2016.04.19

+ Recent posts