메모리 유출이 무엇인지, 흔히 메모리 유출을 일으키는 원인과 해결 방법

5484 단어
1. 메모리 유출이란 메모리 유출이 무엇인지를 말한다. 메모리 유출은'메모리 유출'이라고도 부른다. 동적 저장 분배 함수로 동적 공간을 개척한 후 사용이 끝난 후에 방출되지 않아 결국 이 메모리 단원을 차지하게 된다.프로그램이 끝날 때까지(사실 말하자면 이 메모리 공간을 사용한 후 회수하지 않은 것이다) 이른바 메모리 유출이다.
2. 흔히 볼 수 있는 메모리 유출의 원인 1. 한 사례에 의한 메모리 유출은 한 사례의 정적 특성 때문에 그 생명주기가 응용의 생명주기와 같이 길다. 만약에 한 대상이 더 이상 사용할 필요가 없고 한 사례의 대상이 이 대상의 인용을 가지고 있으면 이 대상이 정상적으로 회수되지 못하고 메모리 유출을 초래할 수 있다.예: 메모리 유출을 방지하는 단일 사례의 실례
//  
public class AppManager {
    private static AppManager instance;
    private Context context;
    private AppManager(Context context) {
        this.context = context;
    }
    public static AppManager getInstance(Context context) {
        if (instance != null) {
            instance = new AppManager(context);
        }
        return instance;
    }
}

2. 비정상적인 내부 클래스가 정적 실례를 만드는 데 발생하는 메모리 유출
  public class MainActivity extends AppCompatActivity {

    private static TestResource mResource = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(mResource == null){
            mResource = new TestResource();
        }
        //...
    }
    
    class TestResource {
    //...
    }
}   

3. Handler로 인한 메모리 유출 예: 익명 내부 클래스의 정적 대상 만들기
public class MainActivity extends AppCompatActivity {

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

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new Runnable() {
            @Override
            public void run() {
                // ...
                handler.sendEmptyMessage(0x123);
            }
        });
    }
}

1. 안드로이드 각도에서 안드로이드 프로그램이 시작되면 이 프로그램의 메인 라인은 자동으로 Looper 대상과 그와 관련된 MessageQueue를 생성합니다.주 스레드에Handler 대상을 실례화하면 자동으로 주 스레드 Looper의MessageQueue와 연결됩니다.MessageQueue에 보내는 모든 Messag는Handler의 인용을 가지고 있기 때문에 Looper는Handle의handleMessage () 방법을 리셋해서 메시지를 처리합니다.MessageQueue에서 처리되지 않은 메시지만 있으면 Looper는 끊임없이 그 중에서 꺼내서Handler에 맡깁니다.또한 마스터 스레드의 Looper 객체는 애플리케이션의 전체 라이프 사이클을 수반합니다.2. 자바 각도는 자바에서 비정상적인 내부 클래스와 익명 클래스 내부 클래스는 그들이 속한 외부 클래스의 인용을 잠재적으로 가지고 있지만 정적 내부 클래스는 가지고 있지 않다.상기 예시를 분석하면MainActivity가 끝날 때 처리되지 않은 메시지는handler의 인용을 가지고 있고handler는 자신이 속한 외부 클래스인MainActivity의 인용을 가지고 있다.이 인용 관계는 메시지가 처리될 때까지 유지되어 MainActivity가 쓰레기 수거기에서 회수되는 것을 막아 메모리 유출을 초래했다.해결 방법:Handler 클래스를 독립시키거나 정적 내부 클래스를 사용하면 메모리 유출을 피할 수 있다.
4. 스레드로 인한 메모리 누출 예: AsyncTask와 Runnable
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new Thread(new MyRunnable()).start();
        new MyAsyncTask(this).execute();
    }

    class MyAsyncTask extends AsyncTask {

        // ...

        public MyAsyncTask(Context context) {
            // ...
        }

        @Override
        protected Void doInBackground(Void... params) {
            // ...
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            // ...
        }
    }

    class MyRunnable implements Runnable {
        @Override
        public void run() {
            // ...
        }
    }
}

AsyncTask와 Runnable는 모두 익명 내부 클래스를 사용합니다. 이 클래스는Activity에 대한 은밀한 인용을 가지고 있습니다.Activity가 제거되기 전에 작업이 완료되지 않으면 Activity의 메모리 자원이 회수되지 않아 메모리 유출이 발생합니다.해결 방법: AsyncTask와 Runnable 클래스를 독립시키거나 정적 내부 클래스를 사용하면 메모리 유출을 피할 수 있습니다.
5. 자원이 닫히지 않아 발생한 메모리 유출은BraodcastReceiver,ContentObserver,File,Cursor,Stream,Bitmap 등 자원을 사용했을 때Activity가 소각할 때 제때에 닫거나 취소해야 한다. 그렇지 않으면 이 자원들은 회수되지 않고 메모리 유출을 초래할 수 있다.1) 예를 들어Activity에서register가 BraodcastReceiver를 만들었지만Activity가 끝난 후에는unregister가BraodcastReceiver가 없습니다.2) 자원적 대상인 커서,Stream,File 파일 등은 버퍼링을 사용하는 경우가 많으므로 사용하지 않을 때는 버퍼링이 메모리를 회수할 수 있도록 제때에 닫아야 한다.그들의 버퍼는 자바 가상 기기 안에 존재할 뿐만 아니라 자바 가상 기기 밖에도 존재한다.만약 우리가 인용을null로 설정하고 닫지 않는다면 메모리 유출을 초래할 수 있습니다.3) 자원성 대상이 사용되지 않을 때에는 close () 함수를 호출하여 닫은 다음null로 설정해야 한다.프로그램이 종료될 때 자원 대상이 닫혔는지 확인해야 합니다.4) Bitmap 객체가 사용되지 않을 때 recycle()를 호출하여 메모리를 해제합니다.2.3 이후의bitmap은 수동recycle이 필요하지 않을 것입니다. 메모리는 이미 자바층에 있습니다.
6.ListView를 사용할 때 발생하는 메모리 유출이 초기에 ListView는 BaseAdapter에서 현재 화면 레이아웃에 따라 일정 수량의 View 대상을 실례화하고 ListView는 이러한 View 대상을 캐시합니다.ListView를 위로 스크롤하면 맨 위에 있던 Item의 View 객체가 재확보되어 아래에 새로 나타나는 Item을 구성하는 데 사용됩니다.이 구조 과정은 getView () 방법으로 이루어졌고 getView () 의 두 번째 형참convertView는 캐시된 Item의 View 대상입니다. (초기화할 때 캐시에 View 대상이 없으면 convertView는null)어댑터를 구성할 때 캐시를 사용하는 convertView가 없습니다.해결 방법: 어댑터를 구성할 때 캐시된 convertView를 사용합니다.
7. 집합 용기의 메모리 유출은 우리가 보통 일부 대상의 인용을 집합 용기(예를 들어 Array List)에 넣는다. 우리가 이 대상을 필요로 하지 않을 때 그 인용을 집합에서 제거하지 않으면 이 집합은 점점 커진다.만약 이 집합이 static이라면 상황은 더욱 심각해질 것이다.해결 방법: 프로그램을 종료하기 전에 집합에 있는clear를null로 설정하고 프로그램을 종료합니다.
8. WebView가 초래한 유출은 우리가 WebView 대상을 사용하지 않을 때 Destory () 함수를 호출하여 그것을 없애고 차지한 메모리를 방출해야 한다. 그렇지 않으면 장기적으로 차지한 메모리도 회수되지 않아 메모리 유출을 초래할 수 있다.해결 방법: WebView에 다른 프로세스를 열고 AIDL을 통해 메인 라인과 통신을 하면 WebView가 있는 프로세스는 업무의 수요에 따라 적당한 시기를 선택하여 삭제함으로써 메모리의 완전한 방출을 실현할 수 있다.
3: 메모리 유출을 어떻게 피합니까?
1. 평소에 좋은 코드 작성 습관을 길러야 한다. 소각할 대상은 소각해야 한다. 예를 들어destory나 방송은 콘텐츠 상하문에 사용해야 하는 전체적인 상륙문 대상을 우선적으로 고려해야 한다.

좋은 웹페이지 즐겨찾기