android 소스 코드 분석 (25) -- > onLowMemory 실행 프로 세 스

위의 글 에서 우 리 는 Activity 의 onSave InstanceState 방법 집행 시 기 를 분석 한 결과 Activity 가 일반적인 상황 에서 onPause 방법 만 실행 하면 onSave InstanceState 방법 을 실행 하지 않 고 onStop 방법 을 실행 하면 onSave InstanceState 방법, 구체 적 인 정 보 를 집행 한 다 는 것 을 알 게 되 었 다.onSave InstanceState 방법 실행 시 기 를 참조 할 수 있 습 니 다.http://blog.csdn.net/qq_23547831 / article / details / 51464535 이 글 에서 똑 같은 우리 가 Actvity (물론 Activity 뿐만 아니 라 Servier, ContentProvider, Application 등) 의 또 다른 내부 방법 인 onLowMemory 를 분석 해 보 자.이 방법 은 현재 시스템 에서 사용 할 수 있 는 메모리 가 비교적 낮 을 때 되 돌려 사용 하 는 데 주로 사용 된다.
여기 서 간단하게 안 드 로 이 드 시스템 의 메모리 분배 메커니즘 을 소개 합 니 다.안 드 로 이 드 시스템 에서 하나의 App 은 서로 다른 응용 프로 세 스 입 니 다. 각자 의 JVM 과 실행 을 가지 고 있 을 때 모든 App 프로 세 스 가 사용 할 수 있 는 메모리 크기 가 고정 되 어 있 습 니 다. 시스템 에서 App 을 너무 많이 열 면 안 드 로 이 드 시스템 의 사용 가능 한 메모리 가 줄 어 들 것 입 니 다. 현재 사용 하고 있 는 App 에 있어 서 시스템 메모 리 를 계속 신청 해 야 할 수도 있 습 니 다.그리고 우리 의 나머지 시스템 메모 리 는 현재 App 에 의 해 신청 되 기 에 부족 합 니 다. 이 럴 때 시스템 은 배경 프로 세 스 를 자동 으로 정리 하고 사용 가능 한 메모 리 를 풀 어 프론트 프로 세 스 의 사용 에 사용 합 니 다. 물론 이 시스템 이 배경 프로 세 스 를 청소 하 는 알고리즘 은 우리 가 토론 하 는 중심 이 아 닙 니 다.여기 서 우 리 는 안 드 로 이 드 시스템 이 Activity 를 되 돌 리 는 onLowMemory 방법 을 대충 분석 하 는 절차 일 뿐이다.
앞에서 Activity 에 대한 시작 프로 세 스 분석 을 통 해 알 수 있 듯 이 Activity Manager Service 는 전체 안 드 로 이 드 시스템 의 관리 중추 이 고 Activity, Servier 등 4 대 구성 요소 의 시작 과 소각 등 업 무 를 담당 하 며 똑 같이 응용 프로 세 스에 대한 관리 업무 도 Activity Maanger Servier 에서 이 루어 졌 다.안 드 로 이 드 시스템 에는 두 개의 중요 한 프로 세 스 가 있 습 니 다. Zygote 프로 세 스 와 SystemServer 프로 세 스 가 있 습 니 다. 그 중에서 Zygote 프로 세 스 는 전체 안 드 로 이 드 시스템 의 루트 프로 세 스 이 고 다른 모든 프로 세 스 는 Zygote 프로 세 스 fork 를 통 해 나 왔 습 니 다.한편, SystemServer 프로 세 스 는 각종 서 비 스 를 실행 하고 다른 응용 프로 세 스에 각종 기능 인 터 페 이 스 를 제공 하 는 데 사 용 됩 니 다. 앞에서 SystemServer 프로 세 스 의 시작 프로 세 스 를 분 석 했 습 니 다 (참고:http://blog.csdn.net/qq_23547831/article/details/51105171) 그 중에서 SystemServer 의 startBootService 방법 에서 다음 과 같이 호출 되 었 습 니 다.
// Set up the Application instance for the system process and get started. mActivityManagerService.setSystemProcess();

방법, 설명 을 보십시오. System 프로 세 스 를 위 한 응용 프로그램 인 스 턴 스 를 초기 화 하 는 것 입 니 다. 이 방법의 구체 적 인 실현 을 볼 수 있 습 니 다.
public void setSystemProcess() {
        try {
            ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
            ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
            ServiceManager.addService("meminfo", new MemBinder(this));
            ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
            ServiceManager.addService("dbinfo", new DbBinder(this));
            if (MONITOR_CPU_USAGE) {
                ServiceManager.addService("cpuinfo", new CpuBinder(this));
            }
            ServiceManager.addService("permission", new PermissionController(this));
            ServiceManager.addService("processinfo", new ProcessInfoService(this));

            ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                    "android", STOCK_PM_FLAGS);
            mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());

            synchronized (this) {
                ProcessRecord app = newProcessRecordLocked(info, info.processName, false, 0);
                app.persistent = true;
                app.pid = MY_PID;
                app.maxAdj = ProcessList.SYSTEM_ADJ;
                app.makeActive(mSystemThread.getApplicationThread(), mProcessStats);
                synchronized (mPidsSelfLocked) {
                    mPidsSelfLocked.put(app.pid, app);
                }
                updateLruProcessLocked(app, false, null);
                updateOomAdjLocked();
            }
        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException(
                    "Unable to find android system package", e);
        }
    }

여기 서 간단하게 Servier Manager 는 서 비 스 를 관리 하 는 서비스 이 고 addServier 방법 은 각종 서 비 스 를 등록 하 는 것 입 니 다 (서 비 스 는 JNI 층 에 등록 하고 JNI 층 에 어떻게 등록 하 는 지 에 대해 구체 적 으로 설명 하지 않 습 니 다).방법 체 에서 우 리 는 memInfo 라 는 서비스 MemBinder 를 등 록 했 습 니 다. MemBinder 는 Binder 형식의 서비스 로 주로 시스템 메모리 상황 을 검사 하 는 데 사 용 됩 니 다. 여기 서 구체 적 인 실현 논 리 를 볼 수 있 습 니 다.
static class MemBinder extends Binder {
        ActivityManagerService mActivityManagerService;
        MemBinder(ActivityManagerService activityManagerService) {
            mActivityManagerService = activityManagerService;
        }

        @Override
        protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
            if (mActivityManagerService.checkCallingPermission(android.Manifest.permission.DUMP)
                    != PackageManager.PERMISSION_GRANTED) {
                pw.println("Permission Denial: can't dump meminfo from from pid="
                        + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid()
                        + " without permission " + android.Manifest.permission.DUMP);
                return;
            }

            mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null);
        }
    }

원본 코드 를 보면 MemBinder 류 가 Binder 류 에 계승 되 어 있 음 을 알 수 있 습 니 다. 즉, 하나의 Binder 유형의 서비스 이 고 한 구성원 의 방법 dump 가 있 습 니 다. 이 방법 은 주로 셸 명령 을 수행 하 는 데 사 용 됩 니 다. 시스템 이 메모리 가 비교적 낮 을 때 이 방법 을 실 행 했 습 니 다. 그 다음 에 Activity Manager Service 의 kill AllBackground 방법 으로 되 돌 립 니 다.다음은 kill AllBackground 방법의 구체 적 인 실현 에 중점 을 두 고 살 펴 보 겠 습 니 다.
@Override
    public void killAllBackgroundProcesses() {
        ...
           doLowMemReportIfNeededLocked(null);
        ...
        } finally {
            Binder.restoreCallingIdentity(callingId);
        }
    }

이 방법 체 에서 doLowMemReportIfNeeded Locked 방법 이 실 행 됩 니 다. 이 방법 은 무엇 을 하 는 것 입 니까?doLowMemReportIfNeeded Locked 방법의 실현 을 계속 살 펴 보 겠 습 니 다.
final void doLowMemReportIfNeededLocked(ProcessRecord dyingProc) {
        ...
        scheduleAppGcsLocked();
        ...
    }

좋 습 니 다. 이 방법 에서 우 리 는 scheduleAppGcsLocked 방법 을 호출 했 습 니 다. 그러면 우 리 는 scheduleAppGcsLocked 방법의 실현 논 리 를 계속 살 펴 보 겠 습 니 다.
/** * Schedule the execution of all pending app GCs. */
    final void scheduleAppGcsLocked() {
        mHandler.removeMessages(GC_BACKGROUND_PROCESSES_MSG);

        if (mProcessesToGc.size() > 0) {
            // Schedule a GC for the time to the next process.
            ProcessRecord proc = mProcessesToGc.get(0);
            Message msg = mHandler.obtainMessage(GC_BACKGROUND_PROCESSES_MSG);

            long when = proc.lastRequestedGc + GC_MIN_INTERVAL;
            long now = SystemClock.uptimeMillis();
            if (when < (now+GC_TIMEOUT)) {
                when = now + GC_TIMEOUT;
            }
            mHandler.sendMessageAtTime(msg, when);
        }
    }

여기 서 실행 되 는 논 리 는 mHandler 를 통 해 msg. what 를 GC 로 보 내 는 것 임 을 알 수 있 습 니 다.BACKGROUND_PROCESSES_MSG 의 비동기 메시지 입 니 다. 그러면 메시지 체 는 최종 적 으로 mHandler 의 handleMessage 방법 에 의 해 실 행 됩 니 다. mHandler 의 handleMessage 방법의 실행 논 리 를 계속 보 세 요.
case GC_BACKGROUND_PROCESSES_MSG: {
                synchronized (ActivityManagerService.this) {
                    performAppGcsIfAppropriateLocked();
                }
            } break;

mHandler 의 handle Message 방법 에서 msg 의 what 가 GC 인지 먼저 판단 합 니 다.BACKGROUND_PROCESSES_MSG, 그리고 permAppGcsIf AppropriateLocked 방법 을 실행 합 니 다. 그러면 permAppGcsIf AppropriateLocked 방법의 실현 을 계속 살 펴 보 겠 습 니 다.
/** * If all looks good, perform GCs on all processes waiting for them. */
    final void performAppGcsIfAppropriateLocked() {
        if (canGcNowLocked()) {
            performAppGcsLocked();
            return;
        }
        // Still not idle, wait some more.
        scheduleAppGcsLocked();
    }

gc 작업 을 실행 할 수 있 는 지 여 부 를 먼저 판단 할 수 있 습 니 다. 위의 scheduleAppGcsLocked 방법 을 계속 실행 하지 못 하면 비동기 메 시 지 를 보 내 는 논 리 를 계속 실행 할 수 있 습 니 다. 변수 canGcNowLocked 가 true 일 때 까지 permAppGcsLocked 방법 을 실행 한 다음 return 을 실행 합 니 다. 그러면 우 리 는 코드 를 계속 추적 하여 permAppGcsLocked 방법의 실행 논 리 를 볼 수 있 습 니 다.
/** * Perform GCs on all processes that are waiting for it, but only * if things are idle. */
    final void performAppGcsLocked() {
        final int N = mProcessesToGc.size();
        if (N <= 0) {
            return;
        }
        if (canGcNowLocked()) {
            while (mProcessesToGc.size() > 0) {
                ProcessRecord proc = mProcessesToGc.remove(0);
                if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ || proc.reportLowMemory) {
                    if ((proc.lastRequestedGc+GC_MIN_INTERVAL)
                            <= SystemClock.uptimeMillis()) {
                        // To avoid spamming the system, we will GC processes one
                        // at a time, waiting a few seconds between each.
                        performAppGcLocked(proc);
                        scheduleAppGcsLocked();
                        return;
                    } else {
                        // It hasn't been long enough since we last GCed this
                        // process... put it in the list to wait for its time.
                        addProcessToGcListLocked(proc);
                        break;
                    }
                }
            }

            scheduleAppGcsLocked();
        }
    }

이 방법 은 일련의 논리 적 판단 을 거 친 후에 performaAppGCLocked 방법 을 실행 할 것 임 을 알 수 있 습 니 다. 우 리 는 이 방법의 실현 을 계속 살 펴 보 겠 습 니 다.
/** * Ask a given process to GC right now. */
    final void performAppGcLocked(ProcessRecord app) {
        try {
            app.lastRequestedGc = SystemClock.uptimeMillis();
            if (app.thread != null) {
                if (app.reportLowMemory) {
                    app.reportLowMemory = false;
                    app.thread.scheduleLowMemory();
                } else {
                    app.thread.processInBackground();
                }
            }
        } catch (Exception e) {
            // whatever.
        }
    }

최종 적 으로 실 행 된 것 은 app. thread. scheduleLowMemory 방법 을 발견 할 수 있 습 니 다. 여기 app. thread 는 Activity Thread. ApplicationThread 대상 이기 때문에 여 기 는 최종 적 으로 Binder 프로 세 스 간 통신 을 통 해 Activity Thread. ApplicationThread 의 scheduleLowMemory 방법 을 실 행 했 습 니 다.그래 요. Activity Thread. applicationThread 의 scheduleLowMemory 방법의 실현 논 리 를...
@Override
        public void scheduleLowMemory() {
            sendMessage(H.LOW_MEMORY, null);
        }

Activity Thread 의 scheduleLowMemory 방법 에 서 는 추가 논 리 를 실행 하지 않 고 sendmessage 방법 을 직접 호출 하여 추적 방법 을 계속 수행 합 니 다.
private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
        if (DEBUG_MESSAGES) Slog.v(
            TAG, "SCHEDULE " + what + " " + mH.codeToString(what)
            + ": " + arg1 + " / " + obj);
        Message msg = Message.obtain();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.arg2 = arg2;
        if (async) {
            msg.setAsynchronous(true);
        }
        mH.sendMessage(msg);
    }

sendmessage 방법 에서 최종 적 으로 Handler 형식의 mH 구성원 변 수 를 통 해 비동기 메 시 지 를 보 내 는 것 을 발견 할 수 있 습 니 다. 그러면 비동기 메 시 지 는 결국 mH 의 handleMessage 방법 으로 실 행 됩 니 다...소스 코드 를 보면 mH 의 handle Message 방법 에서 최종 적 으로 handle LowMemory 방법 을 호출 한 것 을 알 수 있 습 니 다.
final void handleLowMemory() {
        ArrayList<ComponentCallbacks2> callbacks = collectComponentCallbacks(true, null);

        final int N = callbacks.size();
        for (int i=0; i<N; i++) {
            callbacks.get(i).onLowMemory();
        }

        // Ask SQLite to free up as much memory as it can, mostly from its page caches.
        if (Process.myUid() != Process.SYSTEM_UID) {
            int sqliteReleased = SQLiteDatabase.releaseMemory();
            EventLog.writeEvent(SQLITE_MEM_RELEASED_EVENT_LOG_TAG, sqliteReleased);
        }

        // Ask graphics to free up as much as possible (font/image caches)
        Canvas.freeCaches();

        // Ask text layout engine to free also as much as possible
        Canvas.freeTextLayoutCaches();

        BinderInternal.forceGc("mem");
    }

ComponentCallbacks 2 를 옮 겨 다 니 며 onLowMemory 방법 을 실행 한 것 을 발견 할 수 있 습 니 다. 그러면 이곳 의 ComponentCallBacks 2 는 무엇 입 니까?collectComponentCallbacks 방법의 실현 논 리 를 살 펴 보 겠 습 니 다.
ArrayList<ComponentCallbacks2> collectComponentCallbacks(
            boolean allActivities, Configuration newConfig) {
        ArrayList<ComponentCallbacks2> callbacks
                = new ArrayList<ComponentCallbacks2>();

        synchronized (mResourcesManager) {
            final int NAPP = mAllApplications.size();
            for (int i=0; i<NAPP; i++) {
                callbacks.add(mAllApplications.get(i));
            }
            final int NACT = mActivities.size();
            for (int i=0; i<NACT; i++) {
                ActivityClientRecord ar = mActivities.valueAt(i);
                Activity a = ar.activity;
                if (a != null) {
                    Configuration thisConfig = applyConfigCompatMainThread(
                            mCurDefaultDisplayDpi, newConfig,
                            ar.packageInfo.getCompatibilityInfo());
                    if (!ar.activity.mFinished && (allActivities || !ar.paused)) {
                        // If the activity is currently resumed, its configuration
                        // needs to change right now.
                        callbacks.add(a);
                    } else if (thisConfig != null) {
                        // Otherwise, we will tell it about the change
                        // the next time it is resumed or shown. Note that
                        // the activity manager may, before then, decide the
                        // activity needs to be destroyed to handle its new
                        // configuration.
                        if (DEBUG_CONFIGURATION) {
                            Slog.v(TAG, "Setting activity "
                                    + ar.activityInfo.name + " newConfig=" + thisConfig);
                        }
                        ar.newConfig = thisConfig;
                    }
                }
            }
            final int NSVC = mServices.size();
            for (int i=0; i<NSVC; i++) {
                callbacks.add(mServices.valueAt(i));
            }
        }
        synchronized (mProviderMap) {
            final int NPRV = mLocalProviders.size();
            for (int i=0; i<NPRV; i++) {
                callbacks.add(mLocalProviders.valueAt(i).mLocalProvider);
            }
        }

        return callbacks;
    }

이 방법 은 최종 적 으로 ArrayList 형식의 callBacks 로 되 돌아 가 는 것 을 발견 할 수 있 습 니 다. 우리 의 callBacks 에는 우리 응용 프로 세 스 의 Activity, Service, Provider 가 이미 Application 등 을 저장 하고 있 습 니 다.어?Activity, Service, Provider, Application 은 모두 ComponentCallBacks 2 유형 입 니까?구체 적 인 정 의 를 살 펴 보 자.
Actvity 의 클래스 정의:
public class Activity extends ContextThemeWrapper implements LayoutInflater.Factory2, Window.Callback, KeyEvent.Callback, OnCreateContextMenuListener, ComponentCallbacks2, Window.OnWindowDismissedCallback

Service 클래스 정의:
public abstract class Service extends ContextWrapper implements ComponentCallbacks2

ContentProvider 의 클래스 정의:
public abstract class ContentProvider implements ComponentCallbacks2

응용 프로그램의 클래스 정의:
public class Application extends ContextWrapper implements ComponentCallbacks2

모두 계승 과 ComponentCalbacks 2 를 발견 할 수 있 기 때문에 모두 ComponentCallbacks 2 유형의 변수 로 여 겨 질 수 있다.같은 4 대 구성 요소 인 BroadcastReceiver 는 다음 과 같은 정 의 를 내 릴 수 있 습 니 다.
public abstract class BroadcastReceiver

ComponentCallbacks 2 와 계승 되 지 않 았 기 때문에 실행 되 지 않 았 음 을 알 수 있 습 니 다. 그래서 이러한 분석 을 통 해 우 리 는 최종 응용 프로그램의 Activity, Servier, ContentProvider, Application 의 onLowMemory 방법 이 실 행 될 것 이라는 것 을 알 게 되 었 습 니 다.한편, 우 리 는 시스템 메모리 가 부족 할 때 kill AllBackground 방법 을 실행 하고 Activity, Service, ContentProvider, Application 의 onLowMemory 방법 을 여러 겹 으로 실행 하기 때문에 우 리 는 이 구성 요소 들 의 onLowMemory 방법 에서 자원 을 정리 하 는 작업 을 수행 하고 메모 리 를 방출 하여 자신의 응용 프로 세 스 가 죽 이지 않도록 할 수 있 습 니 다.
요약:
  • 시스템 은 JNI 층 에서 메모리 변 수 를 자주 감지 합 니 다. 메모리 가 너무 낮 을 때 kiilbackground 방법 으로 배경 프로 세 스 를 청소 합 니 다.
  • 층 층 이 호출 되 는 과정 을 거 쳐 최종 적 으로 Activity, Service, ContentProvider, Application 의 onLowMemory 방법 을 집행 한다.
  • 구성 요소 의 onLowMemory 방법 에서 자원 을 정리 하 는 작업 을 수행 하여 메모리 방출 프로 세 스 가 죽 는 것 을 방지 할 수 있 습 니 다.
  • 좋은 웹페이지 즐겨찾기