안 드 로 이 드 응용 메모리 최적화 및 Handler 메모리 누 출 문제

8553 단어 Android메모리
1.안 드 로 이 드 메모리 기반
물리 적 메모리 와 프로 세 스 메모리
물리 적 메모리 즉 모 바 일 장치 의 RAM 입 니 다.안 드 로 이 드 프로그램 을 시작 할 때 Dalvik VM 프로 세 스 를 시작 합 니 다.시스템 은 고정된 메모리 공간(16M,32M 부정)을 할당 합 니 다.이 메모리 공간 은 RAM 의 한 영역 에 매 핑 됩 니 다.그리고 이 안 드 로 이 드 프로그램 은 이 공간 에서 실 행 됩 니 다.자바 에 서 는 이 공간 을 Stack 스 택 메모리 와 Heap 메모리 로 나 눌 것 입 니 다.stack 에 저 장 된 대상 의 인용,heap 에 실제 대상 데 이 터 를 저장 합 니 다.
프로그램 이 실 행 될 때 대상 을 만 들 수 있 습 니 다.만약 에 메모 리 를 합 리 적 으로 관리 하지 않 으 면 잘못된 공간 을 제때에 회수 하지 않 으 면 메모리 가 유출 될 수 있 습 니 다.심각 하면 메모리 가 시스템 할당 메모 리 를 초과 할 수 있 습 니 다.즉,메모리 가 OOM 이 넘 쳐 서 프로그램 이 중단 되 거나 직접 종 료 될 수 있 습 니 다.
메모리 유출(메모리 유출)
자바 메모리 누 출 은 프로 세 스 의 일부 대상(쓰레기 대상)이 사용 가치 가 없 지만 gc roots 에 직접 또는 간접 적 으로 인용 되 어 GC 에서 회수 할 수 없습니다.Dalvik VM 이 갖 춘 GC 메커니즘(쓰레기 수 거 메커니즘)은 메모리 사용량 이 너무 많 을 때 자동 으로 회수 되 며,심 할 경우 메모리 넘 침 OOM 을 초래 할 수 있 습 니 다.
메모리 넘 침 OOM
응용 프로그램 이 요청 한 자바 힙 공간 이 Dalvik VM HeapGrowthLimit 을 초과 할 때 넘 칩 니 다.
메모:OOM 은 메모리 부족 을 의미 하지 않 습 니 다.요청 한 힙 이 Dalvik VM HeapGrowthLimit 을 초과 하면 메모리 가 충분 하 더 라 도 넘 칠 수 있 습 니 다.효 과 는 많은 프로 세 스 를 메모리 에 상주 시 킬 수 있 습 니 다.
RAM 이 부족 할 때 시스템 은 무엇 을 합 니까?
Android 의 Memory Killer 는 우선 순위 가 낮은 프로 세 스 를 죽 이 고 높 은 우선 순위 프로 세 스 가 더 많은 메모 리 를 가 져 올 수 있 도록 합 니 다.
Android 시스템 기본 메모리 회수 메커니즘
프로 세 스 우선 순위:Foreground 프로 세 스,Visible 프로 세 스,Service 프로 세 스,Background 프로 세 스,Empty 프로 세 스;
사용자 가 홈 키 를 눌 러 데스크 톱 으로 돌아 가면 이 앱 은 Background 프로 세 스 가 됩 니 다.Back 을 누 르 면,Empty 프로 세 스 가 됩 니 다.
Activity Manager Service 는 모든 프로 세 스 의 메모리 자원 분 배 를 직접 관리 합 니 다.모든 프로 세 스 가 메모 리 를 신청 하거나 방출 하려 면 Activity Manager Service 대상 을 통과 해 야 합 니 다.
쓰레기 수 거 부정 기 집행.메모리 가 부족 할 때 힙 공간 을 옮 겨 다 니 며 쓰레기 대상 을 삭제 합 니 다.
메모리 가 클 수록 GC 의 시간 이 길 어 집 니 다.
201621162057205.png (439×249)
최적화
비트 맵 최적화
비트 맵 은 메모 리 를 매우 소모 하고 안 드 로 이 드 에서 비트 맵 을 읽 을 때 보통 가상 컴퓨터 에 배 치 된 그림 스 택 은 8M 에 불과 하기 때문에 OOM 문제 가 자주 발생 합 니 다.그래서 Bitmap 의 사용 을 최적화 할 필요 가 있 습 니 다.
그림 표시:미리 보기 그림 을 표시 하 는 곳 에 큰 그림 을 불 러 오지 마 십시오.
사진 회수:bitmap 를 사용 하고 Bitmap.recycle()을 사용 하여 회수 합 니 다.
문제:안 드 로 이 드 는 자체 적 으로 쓰레기 회수 체 제 를 갖 추고 있 지 않 습 니까?여 기 는 왜 수 동 으로 회수 해 야 합 니까?
비트 맵 대상 은 뉴 가 생 성 한 것 이 아니 라 비트 맵 팩 토 리 를 통 해 생산 됐다.또한 원본 코드 를 통 해 JNI 를 호출 하여 Bitmap 대상(nativeDecodeStream()을 만 드 는 방법 등)을 발견 할 수 있 습 니 다.그래서 메모리 에 bitmap 를 불 러 오 는 것 은 Dalvik 메모리 와 Linux kernel 메모리 두 부분 을 포함 합 니 다.전 자 는 가상 컴퓨터 에 의 해 자동 으로 회수 된다.후 자 는 반드시 recycle()방법 을 통 해 내부 에서 nativeRecycle()을 호출 하여 Liux kernel 을 회수 해 야 한다.
OOM 이상 캡 처:프로그램 에서 OOM 이 발생 하면 응급 처리 방식 을 설정 합 니 다.
그림 캐 시:메모리 캐 시,하 드 디스크 캐 시 등
그림 압축:ImageView 를 직접 사용 하여 Bitmap 를 표시 할 때 많은 자원 을 차지 합 니 다.특히 그림 이 크 면 OOM 이 발생 하기 쉽 습 니 다.BitMapFactory.Options 를 사용 하여 그림 을 압축 할 수 있 습 니 다.
픽 셀:android 기본 색상 모드 는 ARGB8888,디 스 플레이 품질 이 가장 높 고 메모리 사용량 이 가장 큽 니 다.요구 가 높 지 않 을 경우 RGB 사용 가능565 등 모델.그림 크기:그림 길이*너비*단위 픽 셀 이 차지 하 는 바이트 수
ARGB_4444:픽 셀 당 2byte 메모리 사용
ARGB_8888:픽 셀 당 4byte 메모리 사용(기본 값)
RGB_565:픽 셀 당 2byte 메모리 사용
대상 참조 형식
strong:Object object=new Object()를 강하 게 참조 합 니 다.메모리 가 부족 할 때 자바 가상 기 는 OOM 메모리 에 이상 이 넘 칠 지 언 정 강 한 인용 대상 을 쉽게 회수 하여 메모리 부족 문 제 를 해결 하지 않 습 니 다.
소프트 인용 soft:메모리 가 특정한 한도 값 에 이 르 렀 을 때 만 회수 할 수 있 고 캐 시 에 자주 사 용 됩 니 다.
약 인용 weak:GC 스 레 드 에 스 캔 되면 회수 합 니 다.
거짓 인용
OOM 발생 을 피 하려 면 메모리 가 부족 할 때 회수 하 는 소프트 인용 대상 을 사용 합 니 다.메모 리 를 많이 사용 하 는 대상,예 를 들 어 bitmap 를 빨리 회수 하려 면 약 한 인용 을 사용 하여 빠르게 회수 할 수 있 습 니 다.그러나 비트 맵 을 캐 시 하려 면 약 한 인용 을 사용 하지 마 십시오.곧 GC 에서 회수 되 기 때문에 캐 시가 실 패 했 습 니 다.
자바 대상 인용 유형 에 대하 여 구체 적 으로 본인 의 다른 글 에 참가 할 수 있 습 니 다.
수영장
대상 풀:대상 을 만 들 때 큰 자원 비용 이 필요 하 다 면 대상 풀 에 넣 고 대상 을 저장 할 수 있 습 니 다.다음 에 필요 할 때 직접 꺼 내 서 사용 할 수 있 습 니 다.대상 을 다시 만 들 지 않 아 도 됩 니 다.물론 유지 대상 탱크 도 비용 이 필요 하기 때문에 평가 해 야 한다.
스 레 드 탱크:대상 탱크 와 차이 가 많 지 않 습 니 다.스 레 드 대상 을 탱크 에 두 고 반복 적 으로 사용 할 수 있 도록 하고 반복 적 으로 스 레 드 를 만 드 는 비용 을 줄 입 니 다.
3.Handler 메모리 누설 분석 및 해결
1.소개
우선,아래 handler 코드 를 보십시오.

public class SampleActivity extends Activity {
 private final Handler mLeakyHandler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
   // ... 
  }
 }
}
handler 를 사용 할 때 흔히 볼 수 있 는 코드 입 니 다.하지만 심각 한 메모리 유출 문 제 를 일 으 킬 수 있다.실제 작성 에서 우 리 는 다음 과 같은 경 고 를 받 을 수 있 습 니 다.
  In Android, Handler classes should be static or leaks might occur.
그렇다면 handler 는 어떻게 메모리 누 출 을 일 으 켰 을 까?
2.분석
(1)Android 각도
안 드 로 이 드 프로그램 이 시 작 될 때 프레임 워 크 는 이 프로그램의 메 인 스 레 드 에 Looper 대상 을 만 듭 니 다.이 Looper 대상 은 간단 한 메시지 대기 열 Message Queue 를 포함 하고 대기 열 에 있 는 메 시 지 를 순환 적 으로 처리 할 수 있 습 니 다.이 메 시 지 는 대부분의 응용 프로그램 framework 사건 을 포함 합 니 다.예 를 들 어 Activity 수명 주기 방법 호출,button 클릭 등 이 있 습 니 다.이 메 시 지 는 메시지 큐 에 추가 되 고 하나씩 처 리 됩 니 다.
또한 메 인 스 레 드 의 Looper 대상 은 이 프로그램의 전체 수명 주 기 를 수반 합 니 다.
그 다음 에 메 인 라인 에서 Handler 대상 을 예화 하면 메 인 라인 Looper 의 메시지 큐 와 자동 으로 연 결 됩 니 다.메시지 큐 에 보 내 는 모든 메시지 Message 는 Handler 에 대한 참조 가 있 기 때문에 Looper 가 메 시 지 를 처리 할 때 이 를 바탕 으로[Handler\#handleMessage(Message)](http://developer.android.com/reference/android/os/Handler.html#handleMessage(android.os.Message)방법 으로 메 시 지 를 처리 합 니 다.
(2)자바 각도
자바 에 서 는 비정 상 내부 류 와 익명 류 가 속 한 외부 류 를 잠재 적 으로 참조 합 니 다.그러나 정적 내부 류 는 그렇지 않다.
(3)누설 원
다음 코드 를 찾 으 십시오:

public class SampleActivity extends Activity {

 private final Handler mLeakyHandler = new Handler() {
  @Override
  public void handleMessage(Message msg) {
   // ...
  }
 }

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // Post a message and delay its execution for 10 minutes.
  mLeakyHandler.postDelayed(new Runnable() {
   @Override
   public void run() { /* ... */ }
  }, 1000 * 60 * 10);

  // Go back to the previous Activity.
  finish();
 }
}

activity 가 끝 날 때(finish)안에 있 는 지연 메 시 지 는 처리 되 기 전에 메 인 스 레 드 메시지 큐 에 10 분 동안 저 장 됩 니 다.그리고 위의 글 에서 알 수 있 듯 이 이 메 시 지 는 handler 에 대한 인용 을 가지 고 있 으 며,handler 는 외부 클래스(여기 서 Sample Activity)에 대한 잠재 적 인 인용 을 가지 고 있다.이 인용 관 계 는 메시지 가 처 리 될 때 까지 유지 되 기 때문에 Sample Activity 가 쓰레기 회수 기 에 의 해 회수 되 는 것 을 막 고 응용 프로그램의 누 출 을 막는다.
위의 코드 에 있 는 Runnable 류-비 정적 익명 류-역시 외부 류 에 대한 인용 을 가지 고 있 습 니 다.누 출 로 이 어 졌 다.
3.누설 해결 방안
우선,위 에 메모리 유출 원 이 명확 하 게 밝 혀 졌 습 니 다.
처리 되 지 않 은 메시지 가 있 으 면 메 시 지 는 handler 를 참조 하고 비정 상 handler 는 외부 클래스,즉 Activity 를 참조 하여 Activity 가 회수 되 지 못 하고 누 출 될 수 있 습 니 다.
Runnable 클래스 는 비정 상 익명 클래스 로 외부 클래스 를 참조 합 니 다.
발생 하 는 문 제 를 해결 하기 위해 서 우 리 는 정적 내부 류 는 외부 류 에 대한 인용 을 가지 지 않 는 다 는 것 을 명확 하 게 해 야 한다.따라서 우 리 는 handler 류 를 단독 클래스 파일 에 넣 거나 정적 내부 클래스 를 사용 하면 누설 을 피 할 수 있 습 니 다.
또한 handler 내부 에서 있 는 외부 클래스 Activity 를 호출 하려 면 handler 내부 에서 약 한 인용 방식 으로 있 는 Activity 를 가리 키 면 메모리 가 새 지 않 습 니 다.
익명 클래스 Runnable 에 대해 서도 정적 클래스 로 설정 할 수 있 습 니 다.정적 익명 클래스 는 외부 클래스 에 대한 인용 을 가지 고 있 지 않 기 때문이다.

public class SampleActivity extends Activity {

 /**
  * Instances of static inner classes do not hold an implicit
  * reference to their outer class.
  */
 private static class MyHandler extends Handler {
  private final WeakReference<SampleActivity> mActivity;

  public MyHandler(SampleActivity activity) {
   mActivity = new WeakReference<SampleActivity>(activity);
  }

  @Override
  public void handleMessage(Message msg) {
   SampleActivity activity = mActivity.get();
   if (activity != null) {
    // ...
   }
  }
 }

 private final MyHandler mHandler = new MyHandler(this);

 /**
  * Instances of anonymous classes do not hold an implicit
  * reference to their outer class when they are "static".
  */
 private static final Runnable sRunnable = new Runnable() {
   @Override
   public void run() { /* ... */ }
 };

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);

  // Post a message and delay its execution for 10 minutes.
  mHandler.postDelayed(sRunnable, 1000 * 60 * 10);

  // Go back to the previous Activity.
  finish();
 }
}

4.소결
정적 클래스 와 비 정적 클래스 간 의 차 이 는 크 지 않 지만 안 드 로 이 드 개발 자 에 게 는 이해 해 야 합 니 다.적어도 내부 클래스 인 스 턴 스 의 수명 주기 가 Activity 보다 길 면 우 리 는 비정 상 내부 클래스 를 사용 하지 말 아야 한 다 는 것 을 잘 알 아야 한다.가장 좋 은 방법 은 정적 내부 클래스 를 사용 한 다음 에 이 클래스 에서 약 한 인용 을 사용 하여 있 는 Activity 를 가리 키 는 것 이다.

좋은 웹페이지 즐겨찾기