Android 에서 흔히 볼 수 있 는 몇 가지 메모리 누 출 소결
최근 프로젝트 의 버 전 교체 과정 에서 메모리 문제 에 대한 에피소드 가 생 긴 다음 에 자신 이 앱 이 실 행 될 때 메모리 크기 에 대한 문 제 를 최적화 하 는 데 시간 을 들 였 습 니 다.이에 정 리 를 해서 여러분 과 공유 하 겠 습 니 다.
소개
안 드 로 이 드 프로그램 개발 에 서 는 한 대상 이 더 이상 사용 할 필요 가 없습니다.회수 되 어야 할 때 다른 대상 이 인용 을 가지 고 있어 서 회수 되 지 못 합 니 다.이 로 인해 회수 되 어야 할 대상 이 회수 되 지 못 하고 메모리 에 머 물 러 메모리 누 출 이 발생 합 니 다.메모리 누 출 은 어떤 영향 이 있 습 니까?그것 은 응용 프로그램 OOM 을 만 드 는 주요 원인 중의 하나 이다.안 드 로 이 드 시스템 이 모든 프로그램 에 분 배 된 메모리 에 한계 가 있 기 때문에 한 응용 프로그램 에서 발생 하 는 메모리 누 출 이 비교적 많 을 때 응용 에 필요 한 메모리 가 이 시스템 에서 분 배 된 메모리 한 도 를 초과 하 는 것 을 피하 기 어렵다.이 로 인해 메모리 가 넘 쳐 서 Crash 를 사용 하 게 된다.메모리 누 출 의 원인 과 영향 을 알 게 된 후에 우리 가 해 야 할 일 은 흔히 볼 수 있 는 메모리 누 출 을 파악 하고 앞으로 안 드 로 이 드 프로그램 개발 에서 가능 한 한 피 하 는 것 이다.
1.단일 사례 로 인 한 메모리 유출
안 드 로 이 드 의 단일 모드 는 개발 자 들 의 사랑 을 받 지만 잘못 사용 하면 메모리 가 새 어 나 갈 수 있 습 니 다.단일 사례 의 정적 특성 으로 인해 단일 사례 의 생명 주기 와 응용 수명 주기 가 똑 같이 길 기 때문에 한 대상 이 사용 할 필요 가 없고 단일 대상 이 이 대상 의 인용 을 가지 고 있다 면 이 대상 은 정상적으로 회수 되 지 못 해 메모리 가 새 게 된다 는 것 을 의미한다.
public class SingleInstance {
private static SingleInstance instance;
private Context context;
private SingleInstance(Context context) {
this.context = context;
}
public synchronized static SingleInstance getInstance(Context context) {
if (instance != null) {
instance = new SingleInstance(context);
}
return instance;
}
}
이것 은 일반적인 단일 모드 입 니 다.정적 변수의 가장 큰 특징 이 무엇 인지 모두 가 알 고 있 습 니 다.상주 메모리 입 니 다.즉,앱 의 프로 세 스 가 죽 이지 않 았 다 면 메모리 에 있 었 습 니 다.이 사례 를 만 들 때 하나의 Context 가 들 어 와 야 하기 때문에 이 Context 의 생명주기 의 길 이 는 매우 중요 하 다.만약 에 들 어 오 는 것 이 Activity 의 Context 라면 이 Context 에 대응 하 는 Activity 가 물 러 날 때 이 Context 는 Activity 의 생명주기 와 똑 같이 길 기 때문이다(Activity 가 간접 적 으로 Context 에 계승 된다).따라서 현재 Activity 가 종료 되 었 을 때 메모리 가 회수 되 지 않 습 니 다.단일 대상 이 이 Activity 의 인용 을 가지 고 있 기 때 문 입 니 다.그래서 정확 한 단 례 는 이런 자 세 를 취해 야 한다.
public class SingleInstance {
private static SingleInstance instance;
private Context context;
private SingleInstance(Context context) {
this.context = context.getApplicationContext();
}
public synchronized static SingleInstance getInstance(Context context) {
if (instance != null) {
instance = new SingleInstance(context);
}
return instance;
}
}
이렇게 하면 어떤 Context 가 들 어 오 든 최종 적 으로 애플 리 케 이 션 의 Context 를 사용 할 것 이 며,하나의 수명 주기 가 응용 과 똑 같이 길 어서 메모리 누 출 을 방지 할 수 있다.2.비 정적 내부 클래스(예 를 들 어 내부 클래스,익명 내부 클래스)가 정적 인 스 턴 스 로 인 한 메모리 누 출 을 만 듭 니 다.
때때로 우 리 는 잦 은 Activity 를 시작 할 때 같은 데이터 자원 을 반복 적 으로 만 드 는 것 을 피하 기 위해 다음 과 같은 쓰기 가 나타 날 수 있 습 니 다.
public class MainActivity extends AppCompatActivity {
private InnerClass innerClass;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if(innerClass == null){
innerClass = new InnerClass();
}
}
class InnerClass{
}
}
이렇게 해서 Activity 내부 에 비정 상 내부 클래스 의 단일 예 를 만 들 었 습 니 다.Activity 를 시작 할 때마다 이 단일 사례 의 데 이 터 를 사용 합 니 다.그러면 자원 의 중복 생 성 을 피 할 수 있 지만 이런 쓰기 방법 은 메모리 누 출 을 초래 할 수 있 습 니 다.비정 상 내부 클래스 는 기본적으로 외부 클래스 의 인용 이 있 고 이 비정 상 내부 클래스 를 사용 하여 정적 인 인 스 턴 스 를 만 들 었 기 때 문 입 니 다.이 인 스 턴 스 의 수명 주기 가 응용 과 같이 길 기 때문에 이 정적 인 스 턴 스 는 이 Activity 의 인용 을 가지 고 있어 Activity 의 메모리 자원 을 정상적으로 회수 하지 못 합 니 다.정확 한 방법 은 이 내부 클래스 를 정적 내부 클래스 로 설정 하거나 이 내부 클래스 를 추출 하여 하나의 예 로 밀봉 하 는 것 입 니 다.Context 를 사용 하려 면 applicationContext 를 사용 하 십시오.
3.Handler 로 인 한 메모리 유출
Handler 의 사용 으로 인 한 메모리 누 출 문 제 는 가장 흔히 볼 수 있 습 니 다.평소에 네트워크 작업 을 처리 하거나 리 셋 을 요청 하 는 등 api 는 Handler 를 통 해 처리 해 야 합 니 다.Handler 의 사용 코드 에 대해 규범 에 맞지 않 으 면 메모리 누 출 이 발생 할 수 있 습 니 다.다음 과 같은 예 입 니 다.
public class MainActivity extends AppCompatActivity {
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
//...
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
loadData();
}
private void loadData(){
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
}
이러한 Handler 를 만 드 는 방식 은 메모리 누 출 을 초래 할 수 있 습 니 다.mHandler 는 Handler 의 비정 상 익명 내부 클래스 의 인 스 턴 스 이기 때문에 외부 클래스 Activity 의 인용 을 가지 고 있 습 니 다.우 리 는 메시지 대기 열 이 Looper 스 레 드 에서 메 시 지 를 계속 문의 하고 처리 하 는 것 을 알 고 있 습 니 다.그러면 이 Activity 가 종료 되 었 을 때 메시지 대기 열 에 처리 되 지 않 은 메시지 가 있 거나 메 시 지 를 처리 하고 있 습 니 다.한편,메시지 대기 열 에 있 는 Message 는 mHandler 인 스 턴 스 의 인용 을 가지 고 있 고 mHandler 는 Activity 의 인용 을 가지 고 있 기 때문에 이 Activity 의 메모리 자원 을 제때에 회수 하지 못 하고 메모리 누 출 을 야기 하기 때문에 다른 방법 은'소프트 참조'입 니 다.
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference<Context> reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText("");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
loadData();
}
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
}
정적 Handler 내부 클래스 를 만 든 다음 Handler 가 가지 고 있 는 대상 에 대해 약 한 인용 을 사용 합 니 다.그러면 회수 할 때 Handler 가 가지 고 있 는 대상 을 회수 할 수 있 습 니 다.그러면 Activity 누 출 을 피 할 수 있 지만 Looper 스 레 드 의 메시지 큐 에서 처리 해 야 할 메시지 가 있 을 수 있 습 니 다.따라서 저 희 는 Activity 의 Destroy 나 Stop 에서 메시지 큐 에 있 는 메 시 지 를 제거 해 야 합 니 다.더 정확 한 방법 은 다음 과 같다.
public class MainActivity extends AppCompatActivity {
private MyHandler mHandler = new MyHandler(this);
private TextView mTextView ;
private static class MyHandler extends Handler {
private WeakReference<Context> reference;
public MyHandler(Context context) {
reference = new WeakReference<>(context);
}
@Override
public void handleMessage(Message msg) {
MainActivity activity = (MainActivity) reference.get();
if(activity != null){
activity.mTextView.setText("");
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView)findViewById(R.id.textview);
loadData();
}
//
private void loadData() {
//...request
Message message = Message.obtain();
mHandler.sendMessage(message);
}
@Override
protected void onDestroy() {
super.onDestroy();
//
mHandler.removeCallbacksAndMessages(null);
}
}
mHandler.removeCallbacksAndMessages(null)사용 하기;메시지 큐 의 모든 메시지 와 모든 Runnable 을 제거 합 니 다.물론 mHandler.removeCallbacks()도 사용 할 수 있 습 니 다.또는 mHandler.removeMessages();지정 한 Runnable 과 Message 를 제거 합 니 다.물론 간단 하 죠.그냥 그 럴 수도 있어 요.
@Override
protected void onDestroy() {
super.onDestroy();
//
mHandler.removeCallbacksAndMessages(null);
}
테스트4.스 레 드 로 인 한 메모리 유출
스 레 드 로 인 한 메모리 누 출 도 평소에 흔히 볼 수 있 습 니 다.다음 과 같은 두 가지 예 는 모든 사람 이 이렇게 썼 을 수 있 습 니 다.
//――――――test1
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null;
}
}.execute();
//――――――test2
new Thread(new Runnable() {
@Override
public void run() {
SystemClock.sleep(10000);
}
}).start();
위의 비동기 작업 과 Runnable 은 모두 익명 의 내부 클래스 이기 때문에 현재 Activity 에 대해 암시 적 인 참조 가 있 습 니 다.만약 에 Activity 가 소각 하기 전에 작업 이 완성 되 지 않 으 면 Activity 의 메모리 자원 을 회수 하지 못 하고 메모리 가 누 출 될 것 입 니 다.정확 한 방법 은 정적 내부 클래스 를 사용 하 는 것 입 니 다.다음 과 같 습 니 다.
static class MyAsyncTask extends AsyncTask<Void, Void, Void> {
private WeakReference<Context> weakReference;
public MyAsyncTask(Context context) {
weakReference = new WeakReference<>(context);
}
@Override
protected Void doInBackground(Void... params) {
SystemClock.sleep(10000);
return null;
}
@Override
protected void onPostExecute(Void aVoid) {
super.onPostExecute(aVoid);
MainActivity activity = (MainActivity) weakReference.get();
if (activity != null) {
//...
}
}
}
static class MyRunnable implements Runnable{
@Override
public void run() {
SystemClock.sleep(10000);
}
}
//――――――
new Thread(new MyRunnable()).start();
new MyAsyncTask(this).execute();
이렇게 하면 Activity 의 메모리 자원 유출 을 피 할 수 있 습 니 다.물론 Activity 가 소각 할 때 도 해당 하 는 작업 인 AsyncTask:cancel()을 취소 하고 작업 이 배경 에서 자원 을 낭비 하지 않도록 해 야 합 니 다.5.자원 이 닫 히 지 않 아 발생 하 는 메모리 유출
BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap 등 자원 을 사용 한 경우 Activity 가 소각 할 때 즉시 닫 거나 로그아웃 해 야 합 니 다.그렇지 않 으 면 이 자원 들 이 회수 되 지 않 아 메모리 가 새 지 않 습 니 다.
6.WebView,또는 바 이 두,고 덕 지도의 MapView 로 인 한 메모리 누 출 은 제 글 을 참고 할 수 있 습 니 다.
WebView 로 인 한 메모리 누 출:https://www.jb51.net/article/79372.htm
이상 이 바로 본 고의 모든 내용 입 니 다.여러분 의 학습 에 도움 이 되 고 저 희 를 많이 응원 해 주 셨 으 면 좋 겠 습 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
Kotlin의 기초 - 2부지난 글에서는 Kotlin이 무엇인지, Kotlin의 특징, Kotlin에서 변수 및 데이터 유형을 선언하는 방법과 같은 Kotlin의 기본 개념에 대해 배웠습니다. 유형 변환은 데이터 변수의 한 유형을 다른 데이터...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.