>  기사  >  메모리 누수의 원인과 해결책은 무엇입니까?

메모리 누수의 원인과 해결책은 무엇입니까?

醉折花枝作酒筹
醉折花枝作酒筹원래의
2021-05-17 16:10:3329693검색

이유와 해결책은 다음과 같습니다. 1. 스레드로 인한 메모리 누수를 방지하기 위해 정적 내부 클래스를 사용합니다. 2. ListView로 인한 메모리 누수를 방지하기 위해 캐시된 ConvertView를 사용합니다. 3. 프로그램을 종료하기 전에 collection , 컬렉션 컨테이너의 메모리 누수를 방지하려면 null로 설정하세요.

메모리 누수의 원인과 해결책은 무엇입니까?

이 튜토리얼의 운영 환경: Windows 7 시스템, Dell G3 컴퓨터.

메모리 누수의 일반적인 원인

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 Android 애플리케이션에서 시작하면 애플리케이션의 메인 스레드가 자동으로 Looper 객체와 관련 MessageQueue를 생성합니다. 핸들러 객체가 메인 스레드에서 인스턴스화되면 자동으로 메인 스레드 Looper의 MessageQueue와 연결됩니다. MessageQueue로 전송된 모든 메시지는 핸들러에 대한 참조를 보유하므로 Looper는 그에 따라 메시지를 처리하기 위해 Handle의 handlerMessage() 메서드를 콜백합니다. MessageQueue에 처리되지 않은 메시지가 있는 한 Looper는 계속해서 해당 메시지를 꺼내어 처리를 위해 핸들러에 넘겨줍니다. 또한, 메인 스레드의 Looper 객체는 애플리케이션의 전체 수명 주기와 함께 제공됩니다.

2. Java 관점

Java에서는 비정적 내부 클래스와 익명 내부 클래스가 잠재적으로 자신이 속한 외부 클래스에 대한 참조를 보유하지만 정적 내부 클래스는 그렇지 않습니다.

위의 예를 분석해 보면 MainActivity가 종료되면 처리되지 않은 메시지는 핸들러에 대한 참조를 보유하고 핸들러는 해당 메시지가 속한 외부 클래스인 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<Void, Void, Void> {

        // ...

        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은 모두 익명의 내부 클래스를 사용하며 자신이 위치한 활동에 대한 암시적 참조를 보유합니다. 활동이 소멸되기 전에 작업이 완료되지 않으면 활동의 메모리 리소스가 재활용되지 않아 메모리 누수가 발생합니다.

해결책: AsyncTask 및 Runnable 클래스를 분리하거나 정적 내부 클래스를 사용하여 메모리 누수를 방지하세요.

5. 닫히지 않은 리소스로 인한 메모리 누수

BroadcastReceiver, ContentObserver, File, Cursor, Stream, Bitmap 등과 같은 리소스의 경우 활동이 소멸될 때 해당 리소스를 닫거나 로그오프해야 합니다. 리소스는 재활용되지 않으므로 메모리 누수가 발생합니다.

1) 예를 들어 BraodcastReceiver가 Activity에 등록되어 있지만 Activity가 끝난 후에도 BraodcastReceiver가 등록 해제되지 않습니다.

2) Cursor, Stream, File 등과 같은 리소스 개체는 일부 버퍼를 사용하는 경우가 많습니다. 사용하지 않을 때는 버퍼가 제때에 메모리를 회수할 수 있도록 제때에 닫아야 합니다. 해당 버퍼는 JVM(Java Virtual Machine) 내에 존재할 뿐만 아니라 JVM(Java Virtual Machine) 외부에도 존재합니다. 참조를 닫지 않고 null로 설정하면 메모리 누수가 자주 발생합니다.

3) 리소스 개체를 사용하지 않을 때는 해당 개체의 close() 함수를 호출하여 닫은 다음 null로 설정해야 합니다. 프로그램이 종료될 때 리소스 개체가 닫히는지 확인해야 합니다.

4) Bitmap 객체가 더 이상 사용되지 않을 때 recycle()을 호출하여 메모리를 해제합니다. 2.3 이후의 비트맵은 메모리가 이미 Java 계층에 있으므로 더 이상 수동으로 재활용할 필요가 없습니다.

6. ListView를 사용할 때 발생하는 메모리 누수

처음에 ListView는 현재 화면 레이아웃을 기반으로 BaseAdapter에서 특정 수의 View 개체를 인스턴스화하고 ListView는 이러한 View 개체를 캐시합니다. ListView를 위로 스크롤하면 원래 상단에 있던 Item의 View 객체가 재활용되어 아래에 나타나는 Item을 구성하는 데 사용됩니다. 이 구성 프로세스는 getView() 메소드에 의해 완료됩니다. getView()의 두 번째 공식 매개변수인 ConvertView는 캐시된 항목의 View 객체입니다(초기화 중에 캐시에 View 객체가 없으면 ConvertView는 null입니다).

어댑터를 구성할 때 캐시된 ConvertView는 사용되지 않습니다.

해결책: 어댑터를 구성할 때 캐시된 ConvertView를 사용하세요.

7. 컬렉션 컨테이너에서 메모리 누수

우리는 일반적으로 컬렉션 컨테이너(예: ArrayList)에 일부 개체 참조를 추가합니다. 개체가 더 이상 필요하지 않으면 컬렉션에서 해당 참조를 지우지 않으므로 컬렉션이 점점 더 커집니다. 이 컬렉션이 정적이라면 상황은 더욱 심각합니다.

해결책: 프로그램을 종료하기 전에 컬렉션의 항목을 지우고 null로 설정한 다음 프로그램을 종료하세요.

8. WebView로 인한 누수

WebView 개체를 사용하지 않을 경우 해당 개체의 destroy() 함수를 호출하여 개체가 차지한 메모리를 해제해야 합니다. 재활용할 수 없습니다. 이로 인해 메모리 누수가 발생합니다.

해결책: WebView에 대한 다른 프로세스를 열고 AIDL을 통해 메인 스레드와 통신합니다. WebView가 위치한 프로세스는 비즈니스 요구에 따라 적절한 소멸 시간을 선택하여 완전한 메모리 해제를 달성할 수 있습니다.

더 많은 컴퓨터 관련 지식을 알고 싶다면 FAQ 칼럼을 방문해주세요!

위 내용은 메모리 누수의 원인과 해결책은 무엇입니까?의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
이전 기사:코알라 라이브란?다음 기사:코알라 라이브란?