標籤:mon max clear raw 請求 net toc exception 範圍
本文為慕課網《App效能最佳化之記憶體最佳化》課程的學習筆記,視頻地址 (http://www.imooc.com/video/13670)
## 如何查看一個app在安卓系統中的記憶體配置情況?
方法一:
1.啟動android studio和虛擬機器,建立串連。
2.開啟cmd視窗,輸入adb shell。
3.輸入ps。
4.可以看到有一個name為應用程式套件名的進程,這就是我們的app所在的進程
5.為了具體查看app所在進程的記憶體使用量情況,需輸入dumpsys meminfo +包名。
方法二:
float total_memory= Runtime.getRuntime().totalMemory()*1.0f/1024/1024; float free_memory= Runtime.getRuntime().freeMemory()*1.0f/1024/1024; float max_memory= Runtime.getRuntime().maxMemory()*1.0f/1024/1024;
方法三:
開啟android studio的android monitor。
方法四:
開啟android studio的Tools→Android→Android Device Monitor。
android記憶體配置與回收方式
- 一個App通常就是一個進程,對應一個虛擬機器。
- GC(記憶體回收行程)只在Heap剩餘空間不足時才觸發記憶體回收。(當GC回收垃圾後Heap剩餘空間仍不足,GC會發起系統請求,若GC有很多變數,且GC回收會佔用處理器時間,如果處理時間很長,影響app響應)。
- GC觸發時,所有線程都會暫停,極端情況下發生線程抖動(後面會說)。
App記憶體限制機制
- 每個app分配的最大記憶體限制,隨不同裝置而不同。查看方式:`
ActivityManager manager= (ActivityManager)getSystemService(ACTIVITY_SERVICE);int memory=manager.getMemoryClass();int large=manager.getLargeMemoryClass();//大部分情況下二者相同
- 吃記憶體大戶:圖片
切換應用時後台App清理機制
- app切換時的LRU cache (LRU演算法,最近使用的排在最前面,最少可能的被清理掉)
- 系統清理(或記憶體變化)時會回調應用裡activity的onTrimMemory(int level)方法。這時我們可以判斷系統記憶體是否不足了,如果是就清理掉應用的一些不用的記憶體來使應用的佔用記憶體變小,減少被系統清理掉的可能性。level對應資訊
App記憶體最佳化方法
- 資料結構最佳化
1.頻繁的字串拼接採用StringBuilder而不是通過+的方式(會產生無用的中間字串記憶體塊,視頻中二者拼接同樣字串的總耗時為3ms和8000ms!!!)。
2.ArrayMap,SparseMap替換HashMap(HashMap效率不高,佔用記憶體大)。
3.記憶體抖動(變數使用不當引起,比如突然產生很多變數或申請很多記憶體空間,但很快就做完事情棄之不用了,過了一會又進行上述操作,如果此時Heap不夠,GC觸發記憶體回收,此時所有線程暫停,記憶體使用量情況會像抖動一樣忽高忽低)。
4.再小的Class也要消耗0.5KB。
5.HashMap的每個entry需要佔用額外的32B。
- 對象複用
1.複用系統內建的資源。
2.ListView/GridView的ConvertView複用(ViewHolder)。
3.避免在onDraw方法裡執行對象的建立(onMeasure也會調用多次,推薦在onSizeChanged方法內操作)。
- 避免記憶體泄露
記憶體泄露:由於代碼瑕疵,導致這塊記憶體雖然停止不用了,但依然被其他東西引用著,導致GC無法對其進行回收。
1.記憶體泄露會導致剩餘Heap越來越少,GC頻繁觸發。(視頻中在activity中點擊啟動線程(簡單的休眠5分鐘),然後退出進入該activity,啟動線程,重複多次,再進入Android Device Monitor,多次點擊Cause GC啟動GC回收,發現byte-array的count會有所減少,重複上述操作,count停止減少時的值不斷增加,說明發生了記憶體泄露。 原因是線程是自訂內部類,會隱含的引用activity對象,且該線5min內會一直執行,如果換成休眠較短時間會有所改善。解決方案:放在service裡執行)。
2.尤其是Activity泄露
3.用Application Context而不是Activity Context(可能會經常退出),某些View如Dialog一定要Activity Context(Token)。
4.Cursor對象用完要及時關閉。
OOM問題最佳化1.OOM問題分析
- OOM的必然性與可解決性,不再贅述。
- OOM的絕大部分發生在圖片。
強引用、軟引用的意義
強引用就是平時的寫法。
軟引用的用法。(虛引用與之類似)
private Map<String, SoftReference<Bitmap>> imageCache = new HashMap<String, SoftReference<Bitmap>>(); public void addBitmapToCache(String path) { // 強引用的Bitmap對象 Bitmap bitmap = BitmapFactory.decodeFile(path); // 軟引用的Bitmap對象 SoftReference<Bitmap> softBitmap = new SoftReference<Bitmap>(bitmap); //WeakReference<Bitmap> weakBitmap=new WeakReference<Bitmap>(bitmap); TranslateAnimation animation; // 添加該對象到Map中使其緩衝 imageCache.put(path, softBitmap); } public Bitmap getBitmapByPath(String path) { // 從緩衝中取軟引用的Bitmap對象 SoftReference<Bitmap> softBitmap = imageCache.get(path); // 判斷是否存在軟引用 if (softBitmap == null) { return null; } // 取出Bitmap對象,如果由於記憶體不足Bitmap被回收,將取得空 return softBitmap.get(); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
考慮如下情景:有些成員變數使用幾次後就不使用了,但仍佔據著記憶體空間,它們隨著Activity的銷毀而被回收,即使GC觸發記憶體回收也不會對其進行回收,此時可用把它們放在SoftReference中,放入與讀取見上述代碼,GC觸發記憶體回收時就可對其進行回收了。
2.最佳化OOM問題的方法
1.BitmapFactory.Options類
BitmapFactory.Options options = new BitmapFactory.Options();options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(),R.drawable.yin,options);//不直接載入,擷取bitmap圖片寬高BitmapFactory.Options options2 = new BitmapFactory.Options(); options2.inSampleSize = scale;Bitmap bitmap1=BitmapFactory.decodeResource(getResources(),R.drawable.yin,options2);//scale越大,圖片越模糊,所佔記憶體越小
//RGB_565讓ARGB只佔兩個位元組,大小縮小一倍,且變化不明顯BitmapFactory.Options options=new BitmapFactory.Options(); options.inPreferredConfig= Bitmap.Config.RGB_565;Bitmap bitmap1=BitmapFactory.decodeResource(getResources(),R.drawable.yin,options);
//BitmapRegionDecoder類可以實現範圍選取圖片細節BitmapRegionDecoder decoder=BitmapRegionDecoder.newInstance(,false);BitmapFactory.Options options2 = new BitmapFactory.Options();bitmap=decoder.decodeRegion(new Rect(width/2-SCREEN_WIDTH/2+shiftpx, height/2-SCREEN_HEIGHT/2,width/2+SCREEN_WIDTH/2+shiftpx, height/2+SCREEN_HEIGHT/2),options2);
2.通過軟引用。優點是讓系統在記憶體不足時可以直接回收,缺點是回收沒有優先順序,可能回收的不是用過的而是將要顯示的。
public class BitmapCache { static private BitmapCache cache; private ArrayMap<String,MySoftRe> hashRef; //軟引用被回收後,回收對象放在這,可以查看哪些被回收了 private ReferenceQueue<Bitmap> queue; private BitmapCache(){ hashRef=new ArrayMap<>(); queue=new ReferenceQueue<>(); } /* 繼承SoftReference,使得每一個執行個體都具有可識別的標識 */ private class MySoftRe extends SoftReference<Bitmap>{ private String key=""; public MySoftRe(Bitmap referent, ReferenceQueue<? super Bitmap> q,String key) { super(referent, q); this.key=key; } } public static BitmapCache getInstance(){ if (cache==null){ cache=new BitmapCache(); } return cache; } /* 以軟引用的方式對一個bitmap對象的執行個體進行引用並儲存該引用 */ public void addCacheBitmap(String key, Bitmap bitmap){ cleanCache(); MySoftRe msf=new MySoftRe(bitmap,queue,key); hashRef.put(key,msf); } public Bitmap getBitmap(String key){ Bitmap bitmap=null; try { if (hashRef.containsKey(key)){ MySoftRe msf=hashRef.get(key); bitmap=msf.get(); } return bitmap; }catch (NullPointerException e){ return null; } } private void cleanCache() { MySoftRe msf=null; while ((msf= (MySoftRe) queue.poll())!=null){ hashRef.remove(msf.key); } } public void clearCache(){ cleanCache(); hashRef.clear(); System.gc(); System.runFinalization(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
3.使用LRU Cache。
public class MemoryCache { private static final String TAG="jason"; //LinkedHashMap專門用來構建LRU演算法,但是線程不安全 private Map<String,Bitmap> cache= Collections.synchronizedMap( new LinkedHashMap<String, Bitmap>(8,0.75f,true)); private long size=0;//MemoryCache已經分配的大小 private long limit=1000000; public MemoryCache(){ setLimit(Runtime.getRuntime().maxMemory()/4); } private void setLimit(long l) { limit=l; Log.d(TAG,"MemoryCache will use up to"+limit/1024/1024+"MB"); } public Bitmap get(String id){ try { if (!cache.containsKey(id)){ return null; } return cache.get(id); }catch (NullPointerException e){ return null; } } public void put(String id,Bitmap bitmap){ try { if (cache.containsKey(id)){ size-=getSizeInBytes(cache.get(id)); } cache.put(id,bitmap); size+=getSizeInBytes(bitmap); checkSize(); }catch (Throwable th){ th.printStackTrace(); } } private void checkSize() { Log.i(TAG,"cache size="+size+"length="+cache.size()); if (size>limit){ Iterator<Map.Entry<String,Bitmap>> iterator=cache.entrySet().iterator(); while (iterator.hasNext()){ Map.Entry<String,Bitmap> entry=iterator.next(); size-=getSizeInBytes(entry.getValue()); iterator.remove(); if (size<=limit){ break; } } Log.d(TAG,"Clean cache,new size="+cache.size()); } } private long getSizeInBytes(Bitmap bitmap) { if (bitmap==null) { return 0; } return bitmap.getRowBytes()*bitmap.getHeight(); } public void clear(){ cache.clear(); }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
App效能最佳化之記憶體最佳化