Android進(jìn)程系列第八篇---LowmemoryKiller機(jī)制分析(下)

目錄概覽.png

前面進(jìn)程系列已經(jīng)更新了七篇,本文(基于kernel 3.18)悠垛,基于前兩篇博客油航,繼續(xù)梳理LMK殺進(jìn)程機(jī)制下篇,主要總結(jié)LowmemoryKiller的中kernel的原理部分梳玫。
Android進(jìn)程系列第一篇---進(jìn)程基礎(chǔ)
Android進(jìn)程系列第二篇---Zygote進(jìn)程的創(chuàng)建流程
Android進(jìn)程系列第三篇---SystemServer進(jìn)程的創(chuàng)建流程
Android進(jìn)程系列第四篇---SystemServer進(jìn)程的啟動(dòng)流程
Android進(jìn)程系列第五篇---應(yīng)用進(jìn)程的創(chuàng)建流程
Android進(jìn)程系列第六篇---LowmemoryKiller機(jī)制分析(上)
Android進(jìn)程系列第七篇---LowmemoryKiller機(jī)制分析(中)

上文說(shuō)到如果lmkd.c中的use_inkernel_interface等于1,那么就執(zhí)行kernel空間的邏輯右犹,lmkd中數(shù)據(jù)結(jié)構(gòu)也不用更新提澎,也不用lmkd中殺進(jìn)程的邏輯,全部都交給LowmemoryKiller完成念链。在正式進(jìn)入之前盼忌,思考幾個(gè)問(wèn)題。

  • LowmemoryKiller殺進(jìn)程的策略具體是怎么樣的掂墓??jī)?nèi)存低到什么情況下谦纱,LowmemoryKiller開(kāi)始干活呢?
  • 有沒(méi)有永遠(yuǎn)也殺不死的進(jìn)程君编?
  • minfree水位線和對(duì)應(yīng)的adj跨嘉,應(yīng)用開(kāi)發(fā)者能不能擅自修改,讓自己不易被殺死吃嘿?
  • lmkd擔(dān)當(dāng)著AMS到LowmemoryKiller的橋梁祠乃,那lmkd進(jìn)程會(huì)不會(huì)被自己或者LowmemoryKiller殺了呢窘游?
  • 應(yīng)用開(kāi)發(fā)者如果使得進(jìn)程活的更好?
    下面先整體過(guò)一遍L(zhǎng)owmemoryKiller的機(jī)制跳纳,在回頭整理這些問(wèn)題忍饰。

一、lowmemorykiller低內(nèi)存時(shí)觸發(fā)進(jìn)程查殺

1.1寺庄、基本原理

在linux中艾蓝,有一個(gè)名為kswapd的內(nèi)核線程,當(dāng)linux回收存放分頁(yè)的時(shí)候斗塘,kswapd線程將會(huì)遍歷一張shrinker鏈表赢织,并執(zhí)行回調(diào),或者某個(gè)app啟動(dòng)馍盟,發(fā)現(xiàn)可用內(nèi)存不足時(shí)于置,則內(nèi)核會(huì)阻塞請(qǐng)求分配內(nèi)存的進(jìn)程分配內(nèi)存的過(guò)程,并在該進(jìn)程中去執(zhí)行l(wèi)owmemorykiller來(lái)釋放內(nèi)存贞岭。雖然之前沒(méi)有接觸過(guò)八毯,大體的理解就是向系統(tǒng)注冊(cè)了這個(gè)shrinker回調(diào)函數(shù)之后,當(dāng)系統(tǒng)空閑內(nèi)存頁(yè)面不足時(shí)會(huì)調(diào)用這個(gè)回調(diào)函數(shù)瞄桨。 struct shrinker的定義在linux/kernel/include/linux/shrinker.h中:

http://androidxref.com/kernel_3.18/xref/include/linux/shrinker.h
48struct shrinker {
49  unsigned long (*count_objects)(struct shrinker *,
50                     struct shrink_control *sc);
51  unsigned long (*scan_objects)(struct shrinker *,
52                    struct shrink_control *sc);
53
54  int seeks;  /* seeks to recreate an obj */
55  long batch; /* reclaim batch size, 0 = default */
56  unsigned long flags;
57
58  /* These are for internal use */
59  struct list_head list;
60  /* objs pending delete, per node */
61  atomic_long_t *nr_deferred;
62};
63#define DEFAULT_SEEKS 2 /* A good number if you don't know better. */
64
65/* Flags */
66#define SHRINKER_NUMA_AWARE (1 << 0)
67
68extern int register_shrinker(struct shrinker *);
69extern void unregister_shrinker(struct shrinker *);
70#endif
71

shrinker的注冊(cè)與反注冊(cè)

http://androidxref.com/kernel_3.18/xref/drivers/staging/android/lowmemorykiller.c
189static struct shrinker lowmem_shrinker = {
190 .scan_objects = lowmem_scan,
191 .count_objects = lowmem_count,
192 .seeks = DEFAULT_SEEKS * 16
193};

http://androidxref.com/kernel_3.18/xref/drivers/staging/android/lowmemorykiller.c
195static int __init lowmem_init(void)
196{
197 register_shrinker(&lowmem_shrinker);
198 return 0;
199}
200
201static void __exit lowmem_exit(void)
202{
203 unregister_shrinker(&lowmem_shrinker);
204}

注冊(cè)完成之后话速,就回調(diào)lowmem_scan,這個(gè)基本上是lmk核心的代碼

http://androidxref.com/kernel_3.18/xref/drivers/staging/android/lowmemorykiller.c
80static unsigned long lowmem_scan(struct shrinker *s, struct shrink_control *sc)
81{     
        //tsk進(jìn)程結(jié)構(gòu)體對(duì)象
82  struct task_struct *tsk;
  //我們需要選擇一個(gè)進(jìn)程殺掉芯侥,這個(gè)selected用來(lái)保存不幸中獎(jiǎng)的那個(gè)進(jìn)程
83  struct task_struct *selected = NULL;
84  unsigned long rem = 0;
85  int tasksize;
86  int i;
        // OOM_SCORE_ADJ_MAX = 1000
87  short min_score_adj = OOM_SCORE_ADJ_MAX + 1;
88  int minfree = 0;
  //中獎(jiǎng)的進(jìn)程的內(nèi)存占用大小
89  int selected_tasksize = 0;
  //中獎(jiǎng)的進(jìn)程的oom_score_adj值
90  short selected_oom_score_adj;
91  int array_size = ARRAY_SIZE(lowmem_adj);
  //global_page_state可以獲取當(dāng)前系統(tǒng)可用的(剩余)內(nèi)存大小
92  int other_free = global_page_state(NR_FREE_PAGES) - totalreserve_pages;
93  int other_file = global_page_state(NR_FILE_PAGES) -
94                      global_page_state(NR_SHMEM) -
95                      total_swapcache_pages();
96
97  if (lowmem_adj_size < array_size)
98      array_size = lowmem_adj_size;
99  if (lowmem_minfree_size < array_size)
100     array_size = lowmem_minfree_size;
        // 遍歷lowmem_minfree數(shù)組找出相應(yīng)的最小adj值泊交,目的就是根據(jù)剩余內(nèi)存的大小,確定當(dāng)前剩余內(nèi)存的級(jí)別的adj
101 for (i = 0; i < array_size; i++) {
102     minfree = lowmem_minfree[i];
103     if (other_free < minfree && other_file < minfree) {
104         min_score_adj = lowmem_adj[i];
105         break;
106     }
107 }
108
109 lowmem_print(3, "lowmem_scan %lu, %x, ofree %d %d, ma %hd\n",
110         sc->nr_to_scan, sc->gfp_mask, other_free,
111         other_file, min_score_adj);
112     //系統(tǒng)的空閑內(nèi)存數(shù)柱查,根據(jù)上面的邏輯判斷出廓俭,low memory killer需要對(duì)adj高于多少(min_adj)的進(jìn)程進(jìn)行分析是否釋放。
        //發(fā)現(xiàn)min_score_adj值為OOM_SCORE_ADJ_MAX + 1了唉工,說(shuō)明當(dāng)前系統(tǒng)很好研乒,不需要?dú)⑦M(jìn)程來(lái)釋放內(nèi)存了
113 if (min_score_adj == OOM_SCORE_ADJ_MAX + 1) {
114     lowmem_print(5, "lowmem_scan %lu, %x, return 0\n",
115              sc->nr_to_scan, sc->gfp_mask);
116     return 0;
117 }
118
119 selected_oom_score_adj = min_score_adj;
120     //內(nèi)核一種同步機(jī)制 -- RCU同步機(jī)制
121 rcu_read_lock();
        //遍歷所有進(jìn)程
122 for_each_process(tsk) {
123     struct task_struct *p;
124     short oom_score_adj;
125             //內(nèi)核線程kthread
126     if (tsk->flags & PF_KTHREAD)
127         continue;
128
129     p = find_lock_task_mm(tsk);
130     if (!p)
131         continue;
132
133     if (test_tsk_thread_flag(p, TIF_MEMDIE) &&
134         time_before_eq(jiffies, lowmem_deathpending_timeout)) {
135         task_unlock(p);
136         rcu_read_unlock();
137         return 0;
138     }
139     oom_score_adj = p->signal->oom_score_adj;
                // 如果當(dāng)前找到的進(jìn)程的oom_score_adj比當(dāng)前需要?dú)⒌淖钚?yōu)先級(jí)還低,不殺
140     if (oom_score_adj < min_score_adj) {
141         task_unlock(p);
142         continue;
143     }
       /獲取進(jìn)程的占用內(nèi)存大小(rss值)酵紫,也就是進(jìn)程獨(dú)占內(nèi)存 + 共享庫(kù)大小
144     tasksize = get_mm_rss(p->mm);
145     task_unlock(p);
146     if (tasksize <= 0)
147         continue;
       //第一次循環(huán)告嘲,selected一定是null的
148     if (selected) {
       //如果這個(gè)進(jìn)程的oom_score_adj小于我們已經(jīng)選中的那個(gè)進(jìn)程的oom_score_adj,
              //或者這個(gè)進(jìn)程的oom_score_adj等于我們已經(jīng)選中的那個(gè)進(jìn)程的oom_score_adj奖地,
              // 但其所占用的內(nèi)存大小tasksize小于我們已經(jīng)選中的那個(gè)進(jìn)程所占用內(nèi)存大小,則繼續(xù)尋找下一個(gè)進(jìn)程
149         if (oom_score_adj < selected_oom_score_adj)
150             continue;
151         if (oom_score_adj == selected_oom_score_adj &&
152             tasksize <= selected_tasksize)
153             continue;
154     }
          //已經(jīng)找到了需要尋找的進(jìn)程赋焕,更新它的tasksize與oom_score_adj
155     selected = p;
156     selected_tasksize = tasksize;
157     selected_oom_score_adj = oom_score_adj;
158     lowmem_print(2, "select '%s' (%d), adj %hd, size %d, to kill\n",
159              p->comm, p->pid, oom_score_adj, tasksize);
160 }
    //selected非null参歹,說(shuō)明已經(jīng)找到了
161 if (selected) {
162     long cache_size = other_file * (long)(PAGE_SIZE / 1024);
163     long cache_limit = minfree * (long)(PAGE_SIZE / 1024);
164     long free = other_free * (long)(PAGE_SIZE / 1024);
165     trace_lowmemory_kill(selected, cache_size, cache_limit, free);
    //關(guān)鍵打印
166     lowmem_print(1, "Killing '%s' (%d), adj %hd,\n" \
167             "   to free %ldkB on behalf of '%s' (%d) because\n" \
168             "   cache %ldkB is below limit %ldkB for oom_score_adj %hd\n" \
169             "   Free memory is %ldkB above reserved\n",
170              selected->comm, selected->pid,
171              selected_oom_score_adj,
172              selected_tasksize * (long)(PAGE_SIZE / 1024),
173              current->comm, current->pid,
174              cache_size, cache_limit,
175              min_score_adj,
176              free);
    //更新lowmem_deathpending_timeout
177     lowmem_deathpending_timeout = jiffies + HZ;
     //設(shè)置進(jìn)程的標(biāo)記是TIF_MEMDIE
178     set_tsk_thread_flag(selected, TIF_MEMDIE);
     //發(fā)送SIGKILL信號(hào),殺死這個(gè)進(jìn)程
179     send_sig(SIGKILL, selected, 0);
     //更新一下rem值隆判,殺死了一個(gè)進(jìn)程所釋放的內(nèi)存加上去
180     rem += selected_tasksize;
181 }
182
183 lowmem_print(4, "lowmem_scan %lu, %x, return %lu\n",
184          sc->nr_to_scan, sc->gfp_mask, rem);
185 rcu_read_unlock();
186 return rem;
187}

上面代碼就是lowmemorykiller核心原理的實(shí)現(xiàn)犬庇,可以小總結(jié)一下僧界。

  • 首先調(diào)用global_page_state,可以獲取當(dāng)前系統(tǒng)可用的(剩余)內(nèi)存大小
  • 遍歷lowmem_minfree數(shù)組,根據(jù)other_free和other_file的剩余內(nèi)存的大小臭挽,確定當(dāng)前剩余內(nèi)存的最小級(jí)別的min_score_adj
  • 有了上面的adj之后捂襟,遍歷所有進(jìn)程,獲取每個(gè)進(jìn)程的rss大小,然后不斷循環(huán)欢峰,每次比較進(jìn)程占用的內(nèi)存大小tasksize以及小于oom_score_adj葬荷,就能確定最終殺死哪個(gè)進(jìn)程了
  • 確定的進(jìn)程保存在selected變量中,對(duì)他發(fā)送SIGKILL信號(hào)殺死纽帖,并且更新rem大小
    所以通過(guò)上面的總結(jié)已經(jīng)可以回答我們第一個(gè)問(wèn)題宠漩,“LowmemoryKiller殺進(jìn)程的策略具體是怎么樣的??jī)?nèi)存低到什么情況下懊直,LowmemoryKiller開(kāi)始干活呢扒吁?”
1.2、拓展思考
1.2.1有沒(méi)有永遠(yuǎn)也殺不死的進(jìn)程呢室囊?

要回答這個(gè)問(wèn)題要看從哪個(gè)角度了雕崩,如果從native進(jìn)程的角度回答,確實(shí)是存在的融撞,我們通過(guò)前面幾篇的總結(jié)了解到晨逝,AMS自己會(huì)殺死進(jìn)程,在內(nèi)存緊張的時(shí)候也會(huì)通過(guò)lmkd請(qǐng)求lmk來(lái)殺進(jìn)程懦铺。如果我們寫一個(gè)native進(jìn)程同樣做到不死忙捉貌,比如我們給測(cè)試寫一個(gè)內(nèi)存加壓的程序。因?yàn)橐o對(duì)手機(jī)內(nèi)存加壓冬念,要保證這個(gè)加壓進(jìn)程不被殺死趁窃,即使低內(nèi)存,即使上層systemui執(zhí)行一鍵清理急前,都能存活醒陆,如何做到呢?

int main(int argc, char *argv[]) {
   char text[100];  
   unsigned int  size;
   int percentage = atoi(argv[1]);
  //外面?zhèn)饕粋€(gè)參數(shù)進(jìn)來(lái)裆针,占用百分之多少的內(nèi)存刨摩,最高門檻是60%
   if (percentage >= 0 && percentage <= 60) {
       printf("Memory footprint %d%%\n", percentage);
   } else {
       printf("Memory footprint %d%% error!! must be in range of 0-60%%\n", percentage);
       return 0;
   }
       //修改oom_score_adj為-1000
   sprintf(text, "/proc/%d/oom_score_adj", getpid());    
   int fd = open(text, O_WRONLY);  
   if (fd >= 0) {  
       sprintf(text, "%d", -1000);  //讓自己不被殺死
       write(fd, text, strlen(text));  
       close(fd);  
   }
   
   char task_name[50];
   char *pid = (char*)calloc(10,sizeof(char));
   strcpy(task_name, "logcat");
   sprintf(text, "/proc/%s/oom_score_adj", pid); 
   fd = open(text, O_WRONLY);  
   if (fd >= 0) {  
       sprintf(text, "%d", -1000);   //讓logcat進(jìn)程不被殺死
       write(fd, text, strlen(text));  
       close(fd);  
   }
   size = (unsigned int)mygetsize();
   mallocflag(percentage, size); //占用內(nèi)存
   while(1) {//等待正常退出
       sleep(3);
       if((access("/sdcard/finishflag",F_OK)) == 0) {
           printf("create memroy process now end.......\n");
           free(addr);
           addr = NULL;
           break;
       }   
   }
   free(pid);
   return 0;
}

void mallocflag(int percentage, unsigned int size) {
   int i = 0;
   if(addr != NULL) {
       free(addr);
       addr = NULL;
   }
   printf("size= %d kb percentage=%d\n",size,percentage);
   float s=(float) size/1024/1024;
   printf("phone  mem size %.2f G \n",s);
   float p =(float)percentage/100;
   printf("p %.2f rate\n",p);
   int sum=s*p*1204*1024*1024;
   printf("sum %d bye",sum);
   printf(" will malloc %d%% size = %d kb\n",percentage, sum);
   addr = (char *)malloc(sum);
   printf("malloc %d%% size = %dM\n",percentage, sum/1024/1024);
   system("echo 2 start >> /sdcard/memory_log");
   if(addr == NULL) {
       printf("malloc %d%% fail \n", percentage);
       system("echo 2 fail >> /sdcard/memory_log");
       exit(0);
       //如果申請(qǐng)失敗,嘗試申請(qǐng)一般的內(nèi)存
   }
   else {
       myMalloc(addr, sum);
       printf("malloc %d%% success \n", percentage);
       system("echo 2 success >> /sdcard/memory_log");
   }
       
}

然后寫Android.mk在源碼下面編譯就行了世吨,其實(shí)這么長(zhǎng)一段代碼澡刹,核心的地方就幾行,即把oom_score_adj修改為-1000

       //修改oom_score_adj為-1000
   sprintf(text, "/proc/%d/oom_score_adj", getpid());    
   int fd = open(text, O_WRONLY);  
   if (fd >= 0) {  
       sprintf(text, "%d", -1000);  //讓自己不被殺死
       write(fd, text, strlen(text));  
       close(fd);  
   }

因?yàn)?1000是最小的adj值耘婚,即使lmk把其他的進(jìn)程都?xì)⒐饬税战剑疾粫?huì)輪到自己,即使自己的占用的內(nèi)存很多,其次AMS也監(jiān)控不到嚷闭,因?yàn)閚ative程序是kernel管理的攒岛。當(dāng)我們把編譯好的程序放到system/bin下面就能被上層的APP所使用的,當(dāng)然這需要Root權(quán)限胞锰。所以從另外一個(gè)角度來(lái)講灾锯,對(duì)于市面上沒(méi)有Root權(quán)限的APP來(lái)說(shuō)存活手段就比較難了,因?yàn)槟銢](méi)有修改adj值的機(jī)會(huì)嗅榕。唉顺饮,要修改oom_score_adj結(jié)點(diǎn)同樣需要Root權(quán)限,且下次開(kāi)機(jī)就沒(méi)有效果了誊册。在系統(tǒng)中领突,有對(duì)一些APP保駕護(hù)航,比如Home案怯。

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
24049        if (app == mHomeProcess) {
24050            if (adj > ProcessList.HOME_APP_ADJ) {
24051                // This process is hosting what we currently consider to be the
24052                // home app, so we don't want to let it go into the background.
24053                adj = ProcessList.HOME_APP_ADJ;
24054                schedGroup = ProcessList.SCHED_GROUP_BACKGROUND;
24055                app.cached = false;
24056                app.adjType = "home";
24057                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
24058                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise adj to home: " + app);
24059                }
24060            }
24061            if (procState > ActivityManager.PROCESS_STATE_HOME) {
24062                procState = ActivityManager.PROCESS_STATE_HOME;
24063                app.adjType = "home";
24064                if (DEBUG_OOM_ADJ_REASON || logUid == appUid) {
24065                    reportOomAdjMessageLocked(TAG_OOM_ADJ, "Raise procstate to home: " + app);
24066                }
24067            }
24068        }

HOME_APP_ADJ的值是600君旦,這個(gè)相對(duì)來(lái)說(shuō)很小了,系統(tǒng)中很多的進(jìn)程是900+嘲碱,所以系統(tǒng)中對(duì)于重要的進(jìn)程一般都會(huì)加以保護(hù)金砍,比如Home進(jìn)程的adj與調(diào)度組都得到了一定的優(yōu)先。

1.2.2麦锯、lmkd會(huì)不會(huì)被自己殺了呢恕稠?

對(duì)于這個(gè)疑問(wèn),我們查看一下lmkd進(jìn)程的oom_score_adj文件就好了

2|sakura:/proc/589 # cat oom_adj                                                                                                                                                                           
-17
sakura:/proc/589 # cat oom_score_adj                                                                                                                                                                       
-1000

值也是-1000扶欣,顯然不能被自己殺死

1.2.3鹅巍、給應(yīng)用開(kāi)發(fā)者的建議

對(duì)于App開(kāi)發(fā)者來(lái)說(shuō),怎么來(lái)存活呢料祠,市面上甭媾酰活手段很多,我之前也總結(jié)過(guò)Android進(jìn)程彼枵溃活的一般套路敛苇,感興趣可以看一看。對(duì)于系統(tǒng)來(lái)說(shuō)顺呕,對(duì)這么多的APP枫攀。一碗水得端平了,不偏袒誰(shuí)株茶,資源的分配策略希望每一個(gè)app都能遵守来涨,不要在做什么其他的保活手段忌卤,在必要的情況一下扫夜,系統(tǒng)也給了一些措施,ActivityManagerService會(huì)根據(jù)系統(tǒng)內(nèi)存以及應(yīng)用的狀態(tài)通過(guò)app.thread.scheduleTrimMemory發(fā)送通知給應(yīng)用程序驰徊,App中的onTrimMemory(int level) 和onLowMemory() 就會(huì)被回調(diào)笤闯,而Activity, Service, ContentProvider和Application都實(shí)現(xiàn)了這個(gè)接口,在回調(diào)中我們可以做一些內(nèi)存釋放的操作棍厂,這樣在同adj的時(shí)候颗味,我們的進(jìn)程就不會(huì)被中獎(jiǎng)了。

應(yīng)用處于Runnig狀態(tài)可能收到的level級(jí)別
TRIM_MEMORY_RUNNING_MODERATE 表示系統(tǒng)內(nèi)存已經(jīng)稍低
TRIM_MEMORY_RUNNING_LOW 表示系統(tǒng)內(nèi)存已經(jīng)相當(dāng)?shù)?br> TRIM_MEMORY_RUNNING_CRITICAL 表示系統(tǒng)內(nèi)存已經(jīng)非常低牺弹,你的應(yīng)用程序應(yīng)當(dāng)考慮釋放部分資源

應(yīng)用的可見(jiàn)性發(fā)生變化時(shí)收到的級(jí)別
TRIM_MEMORY_UI_HIDDEN 表示應(yīng)用已經(jīng)處于不可見(jiàn)狀態(tài)浦马,可以考慮釋放一些與顯示相關(guān)的資源

應(yīng)用處于后臺(tái)時(shí)可能收到的級(jí)別
TRIM_MEMORY_BACKGROUND 表示系統(tǒng)內(nèi)存稍低,你的應(yīng)用被殺的可能性不大张漂。但可以考慮適當(dāng)釋放資源
TRIM_MEMORY_MODERATE 表示系統(tǒng)內(nèi)存已經(jīng)較低晶默,當(dāng)內(nèi)存持續(xù)減少,你的應(yīng)用可能會(huì)被殺死
TRIM_MEMORY_COMPLETE 表示系統(tǒng)內(nèi)存已經(jīng)非常低航攒,你的應(yīng)用即將被殺死磺陡,請(qǐng)釋放所有可能釋放的資源

那么我們一般需要釋放哪些資源呢?Android代碼內(nèi)存優(yōu)化建議-OnTrimMemory優(yōu)化

  • 緩存 緩存包括一些文件緩存漠畜,圖片緩存等币他,在用戶正常使用的時(shí)候這些緩存很有作用,但當(dāng)你的應(yīng)用程序UI不可見(jiàn)的時(shí)候憔狞,這些緩存就可以被清除以減少內(nèi)存的使用.比如第三方圖片庫(kù)的緩存.

  • 一些動(dòng)態(tài)生成動(dòng)態(tài)添加的View. 這些動(dòng)態(tài)生成和添加的View且少數(shù)情況下才使用到的View蝴悉,這時(shí)候可以被釋放,下次使用的時(shí)候再進(jìn)行動(dòng)態(tài)生成即可.比如原生桌面中瘾敢,會(huì)在OnTrimMemory的TRIM_MEMORY_MODERATE等級(jí)中拍冠,釋放所有AppsCustomizePagedView的資源,來(lái)保證在低內(nèi)存的時(shí)候簇抵,桌面不會(huì)輕易被殺掉.

  • 最好的辦法是用TraceView或者M(jìn)emrroy Monitor來(lái)看哪些對(duì)象占用內(nèi)存大庆杜,在決定是否釋放。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末正压,一起剝皮案震驚了整個(gè)濱河市欣福,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌焦履,老刑警劉巖拓劝,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異嘉裤,居然都是意外死亡郑临,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門屑宠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)厢洞,“玉大人,你說(shuō)我怎么就攤上這事√煞” “怎么了丧叽?”我有些...
    開(kāi)封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)公你。 經(jīng)常有香客問(wèn)我踊淳,道長(zhǎng),這世上最難降的妖魔是什么陕靠? 我笑而不...
    開(kāi)封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任迂尝,我火速辦了婚禮,結(jié)果婚禮上剪芥,老公的妹妹穿的比我還像新娘垄开。我一直安慰自己,他們只是感情好税肪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布溉躲。 她就那樣靜靜地躺著,像睡著了一般寸认。 火紅的嫁衣襯著肌膚如雪签财。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天偏塞,我揣著相機(jī)與錄音唱蒸,去河邊找鬼。 笑死灸叼,一個(gè)胖子當(dāng)著我的面吹牛神汹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播古今,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼屁魏,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了捉腥?” 一聲冷哼從身側(cè)響起氓拼,我...
    開(kāi)封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎抵碟,沒(méi)想到半個(gè)月后桃漾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拟逮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年撬统,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片敦迄。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡恋追,死狀恐怖凭迹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情苦囱,我是刑警寧澤嗅绸,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站沿彭,受9級(jí)特大地震影響朽砰,放射性物質(zhì)發(fā)生泄漏尖滚。R本人自食惡果不足惜喉刘,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望漆弄。 院中可真熱鬧睦裳,春花似錦、人聲如沸撼唾。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)倒谷。三九已至蛛蒙,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間渤愁,已是汗流浹背牵祟。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留抖格,地道東北人诺苹。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像雹拄,于是被迫代替她去往敵國(guó)和親收奔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,081評(píng)論 25 707
  • 用兩張圖告訴你滓玖,為什么你的 App 會(huì)卡頓? - Android - 掘金 Cover 有什么料坪哄? 從這篇文章中你...
    hw1212閱讀 12,714評(píng)論 2 59
  • 本篇文章是后臺(tái)殺死系列的最后一篇,主要探討一下進(jìn)程的北炼祝活棒卷,Android本身設(shè)計(jì)的時(shí)候是非常善良的顾孽,它希望進(jìn)程在不...
    看書的小蝸牛閱讀 11,665評(píng)論 10 66
  • 作者: 強(qiáng)波 (阿里云OS平臺(tái)部-Cloud Engine)博客: http://qiangbo.space/...
    菜刀文閱讀 1,856評(píng)論 3 9
  • 世界上的16個(gè)王國(guó): 亞洲5個(gè):不丹王國(guó)、尼泊爾王國(guó)比规、沙特阿拉伯王國(guó)若厚、泰王國(guó)、約旦哈希姆王國(guó) 非洲3個(gè):萊索托王...
    2401d閱讀 199評(píng)論 1 1