轉載請註明本文出自大苞米的博客(http://blog.csdn.net/a396901990),謝謝支持!
html
寫在最前:java
本文的思路主要借鑑了2014年AnDevCon開發者大會的一個演講PPT,加上把網上搜集的各類內存零散知識點進行彙總、挑選、簡化後整理而成。android
因此我將本文定義爲一個工具類的文章,若是你在Android開發中遇到關於內存問題,或者立刻要參加面試,或者就是單純的學習或複習一下內存相關知識,都歡迎閱讀。(本文最後我會盡可能列出所參考的文章)。git
通常Java在內存分配時會涉及到如下區域:程序員
寄存器(Registers):速度最快的存儲場所,由於寄存器位於處理器內部,咱們在程序中沒法控制github
棧(Stack):存放基本類型的數據和對象的引用,但對象自己不存放在棧中,而是存放在堆中面試
堆(Heap):堆內存用來存放由new建立的對象和數組。在堆中分配的內存,由Java虛擬機的自動垃圾回收器(GC)來管理。正則表達式
靜態域(static field): 靜態存儲區域就是指在固定的位置存放應用程序運行時一直存在的數據,Java在內存中專門劃分了一個靜態存儲區域來管理一些特殊的數據變量如靜態的數據變量算法
常量池(constant pool):虛擬機必須爲每一個被裝載的類型維護一個常量池。常量池就是該類型所用到常量的一個有序集和,包括直接常量(string,integer和floating point常量)和對其餘類型,字段和方法的符號引用。shell
非RAM存儲:硬盤等永久存儲空間
堆棧特色對比:
因爲篇幅緣由,下面只簡單的介紹一下堆棧的一些特性。
棧:當定義一個變量時,Java就在棧中爲這個變量分配內存空間,當該變量退出該做用域後,Java會自動釋放掉爲該變量所分配的內存空間,該內存空間能夠當即被另做他用。
堆:當堆中的new產生數組和對象超出其做用域後,它們不會被釋放,只有在沒有引用變量指向它們的時候才變成垃圾,不能再被使用。即便這樣,所佔內存也不會當即釋放,而是等待被垃圾回收器收走。這也是Java比較佔內存的緣由。
棧:存取速度比堆要快,僅次於寄存器。但缺點是,存在棧中的數據大小與生存期必須是肯定的,缺少靈活性。
堆:堆是一個運行時數據區,能夠動態地分配內存大小,所以存取速度較慢。也正由於這個特色,堆的生存期沒必要事先告訴編譯器,並且Java的垃圾收集器會自動收走這些再也不使用的數據。
棧:棧中的數據能夠共享, 它是由編譯器完成的,有利於節省空間。
例如:須要定義兩個變量int a = 3;int b = 3;
編譯器先處理int a = 3;首先它會在棧中建立一個變量爲a的引用,而後查找棧中是否有3這個值,若是沒找到,就將3存放進來,而後將a指向3。接着處理int b = 3;在建立完b的引用變量後,由於在棧中已經有3這個值,便將b直接指向3。這樣,就出現了a與b同時均指向3的狀況。這時,若是再讓a=4;那麼編譯器會從新搜索棧中是否有4值,若是沒有,則將4存放進來,並讓a指向4;若是已經有了,則直接將a指向這個地址。所以a值的改變不會影響到b的值。
堆:例如上面棧中a的修改並不會影響到b, 而在堆中一個對象引用變量修改了這個對象的內部狀態,會影響到另外一個對象引用變量。
內存耗用名詞解析:
VSS - Virtual Set Size 虛擬耗用內存(包含共享庫佔用的內存)
RSS - Resident Set Size 實際使用物理內存(包含共享庫佔用的內存)
PSS - Proportional Set Size 實際使用的物理內存(比例分配共享庫佔用的內存)
USS - Unique Set Size 進程獨自佔用的物理內存(不包含共享庫佔用的內存)
通常來講內存佔用大小有以下規律:VSS >= RSS >= PSS >= USS
OOM:
內存泄露能夠引起不少的問題:
1.程序卡頓,響應速度慢(內存佔用高時JVM虛擬機會頻繁觸發GC)
2.莫名消失(當你的程序所佔內存越大,它在後臺的時候就越可能被幹掉。反以內存佔用越小,在後臺存在的時間就越長)
3.直接崩潰(OutOfMemoryError)
ANDROID內存面臨的問題:
1.有限的堆內存,原始只有16M
2.內存大小消耗等根據設備,操做系統等級,屏幕尺寸的不一樣而不一樣
3.程序不能直接控制
4.支持後臺多任務處理(multitasking)
5.運行在虛擬機之上
5R:
本文主要經過以下的5R方法來對ANDROID內存進行優化:
1.Reckon(計算)
首先須要知道你的app所消耗內存的狀況,知己知彼才能百戰不殆
2.Reduce(減小)
消耗更少的資源
3.Reuse(重用)
當第一次使用完之後,儘可能給其餘的使用
5.Recycle(回收)
返回資源給生產流
4.Review(檢查)
回顧檢查你的程序,看看設計或代碼有什麼不合理的地方。
Reckon (計算):
瞭解本身應用的內存使用狀況是頗有必要的。若是當內存使用太高的話就須要對其進行優化,由於更少的使用內存能夠減小ANDROID系統終止咱們的進程的概率,也能夠提升多任務執行效率和體驗效果。
下面從系統內存(system ram)和堆內存(heap)兩個方面介紹一些查看和計算內存使用狀況的方法:
System Ram(系統內存):
觀察和計算系統內存使用狀況,可使用Android提供給咱們的兩個工具procstats,meminfo。他們一個側重於後臺的內存使用,另外一個是運行時的內存使用。
Heap(堆內存):
在程序中可使用以下的方法去查詢內存使用狀況
ActivityManager#getMemoryClass()
查詢可用堆內存的限制
3.0(HoneyComb)以上的版本能夠經過largeHeap=「true」來申請更多的堆內存(不過這算做「做弊」)
android.os.Debug#getMemoryInfo(Debug.MemoryInfo memoryInfo)
獲得的MemoryInfo中能夠查看以下Field的屬性:
android.os.Debug#getNativeHeapSize()
返回的是當前進程navtive堆自己總的內存大小
android.os.Debug#getNativeHeapAllocatedSize()
返回的是當前進程navtive堆中已使用的內存大小
android.os.Debug#getNativeHeapFreeSize()
返回的是當前進程navtive堆中已經剩餘的內存大小
Memory Analysis Tool(MAT):
一般內存泄露分析被認爲是一件頗有難度的工做,通常由團隊中的資深人士進行。不過,今天咱們要介紹的 MAT(Eclipse Memory Analyzer)被認爲是一個「傻瓜式「的堆轉儲文件分析工具,你只須要輕輕點擊一下鼠標就能夠生成一個專業的分析報告。
以下圖:
關於詳細的MAT使用我推薦下面這篇文章:使用 Eclipse Memory Analyzer 進行堆轉儲文件分析
OOM:
內存泄露能夠引起不少的問題:
1.程序卡頓,響應速度慢(內存佔用高時JVM虛擬機會頻繁觸發GC)
2.莫名消失(當你的程序所佔內存越大,它在後臺的時候就越可能被幹掉。反以內存佔用越小,在後臺存在的時間就越長)
3.直接崩潰(OutOfMemoryError)
ANDROID內存面臨的問題:
1.有限的堆內存,原始只有16M
2.內存大小消耗等根據設備,操做系統等級,屏幕尺寸的不一樣而不一樣
3.程序不能直接控制
4.支持後臺多任務處理(multitasking)
5.運行在虛擬機之上
5R:
本文主要經過以下的5R方法來對ANDROID內存進行優化:
1.Reckon(計算)
首先須要知道你的app所消耗內存的狀況,知己知彼才能百戰不殆
2.Reduce(減小)
消耗更少的資源
3.Reuse(重用)
當第一次使用完之後,儘可能給其餘的使用
5.Recycle(回收)
返回資源給生產流
4.Review(檢查)
回顧檢查你的程序,看看設計或代碼有什麼不合理的地方。
Reckon:
關於內存簡介,和Reckon(內存計算)的內容請看上一篇文章:ANDROID內存優化(大彙總——上)
Reduce :
Reduce的意思就是減小,直接減小內存的使用是最有效的優化方式。
下面來看看有哪些方法能夠減小內存使用:
圖片顯示:
咱們須要根據需求去加載圖片的大小。
例如在列表中僅用於預覽時加載縮略圖(thumbnails )。
只有當用戶點擊具體條目想看詳細信息的時候,這時另啓動一個fragment/activity/對話框等等,去顯示整個圖片
圖片大小:
直接使用ImageView顯示bitmap會佔用較多資源,特別是圖片較大的時候,可能致使崩潰。
使用BitmapFactory.Options設置inSampleSize, 這樣作能夠減小對系統資源的要求。
屬性值inSampleSize表示縮略圖大小爲原始圖片大小的幾分之一,即若是這個值爲2,則取出的縮略圖的寬和高都是原始圖片的1/2,圖片大小就爲原始大小的1/4。
圖片像素:
圖片回收:
使用Bitmap事後,就須要及時的調用Bitmap.recycle()方法來釋放Bitmap佔用的內存空間,而不要等Android系統來進行釋放。
下面是釋放Bitmap的示例代碼片斷。
捕獲異常:
通過上面這些優化後還會存在報OOM的風險,因此下面須要一道最後的關卡——捕獲OOM異常:
修改對象引用類型:
引用類型:
引用分爲四種級別,這四種級別由高到低依次爲:強引用>軟引用>弱引用>虛引用。
強引用(strong reference)
如:Object object=new Object(),object就是一個強引用了。當內存空間不足,Java虛擬機寧願拋出OutOfMemoryError錯誤,使程序異常終止,也不會靠隨意回收具備強引用的對象來解決內存不足問題。
軟引用(SoftReference)
只有內存不夠時纔回收,經常使用於緩存;當內存達到一個閥值,GC就會去回收它;
弱引用(WeakReference)
弱引用的對象擁有更短暫的生命週期。在垃圾回收器線程掃描它 所管轄的內存區域的過程當中,一旦發現了只具備弱引用的對象,無論當前內存空間足夠與否,都會回收它的內存。
虛引用(PhantomReference)
"虛引用"顧名思義,就是形同虛設,與其餘幾種引用都不一樣,虛引用並不會決定對象的生命週期。若是一個對象僅持有虛引用,那麼它就和沒有任何引用同樣,在任什麼時候候均可能被垃圾回收。
軟引用和弱引用的應用實例:
注意:對於SoftReference(軟引用)或者WeakReference(弱引用)的Bitmap緩存方案,如今已經不推薦使用了。自Android2.3版本(API Level 9)開始,垃圾回收器更着重於對軟/弱引用的回收,因此下面的內容能夠選擇忽略。
在Android應用的開發中,爲了防止內存溢出,在處理一些佔用內存大並且聲明週期較長的對象時候,能夠儘可能應用軟引用和弱引用技術。
下面以使用軟引用爲例來詳細說明(弱引用的使用方式與軟引用是相似的):
假設咱們的應用會用到大量的默認圖片,並且這些圖片不少地方會用到。若是每次都去讀取圖片,因爲讀取文件須要硬件操做,速度較慢,會致使性能較低。因此咱們考慮將圖片緩存起來,須要的時候直接從內存中讀取。可是,因爲圖片佔用內存空間比較大,緩存不少圖片須要不少的內存,就可能比較容易發生OutOfMemory異常。這時,咱們能夠考慮使用軟引用技術來避免這個問題發生。
首先定義一個HashMap,保存軟引用對象。
須要注意的是,在垃圾回收器對這個Java對象回收前,SoftReference類所提供的get方法會返回Java對象的強引用,一旦垃圾線程回收該Java對象以後,get方法將返回null。因此在獲取軟引用對象的代碼中,必定要判斷是否爲null,以避免出現NullPointerException異常致使應用崩潰。
到底何時使用軟引用,何時使用弱引用呢?
我的認爲,若是隻是想避免OutOfMemory異常的發生,則可使用軟引用。若是對於應用的性能更在乎,想盡快回收一些佔用內存比較大的對象,則可使用弱引用。
還有就是能夠根據對象是否常用來判斷。若是該對象可能會常用的,就儘可能用軟引用。若是該對象不被使用的可能性更大些,就能夠用弱引用。
另外,和弱引用功能相似的是WeakHashMap。WeakHashMap對於一個給定的鍵,其映射的存在並不阻止垃圾回收器對該鍵的回收,回收之後,其條目從映射中有效地移除。WeakHashMap使用ReferenceQueue實現的這種機制。
其餘小tips:
對常量使用static final修飾符
讓咱們來看看這兩段在類前面的聲明:
static int intVal = 42;
static String strVal = "Hello, world!";
編譯器會生成一個叫作clinit的初始化類的方法,當類第一次被使用的時候這個方法會被執行。方法會將42賦給intVal,而後把一個指向類中常量表 的引用賦給strVal。當之後要用到這些值的時候,會在成員變量表中查找到他們。 下面咱們作些改進,使用「final」關鍵字:
static final int intVal = 42;
static final String strVal = "Hello, world!";
如今,類再也不須要clinit方法,由於在成員變量初始化的時候,會將常量直接保存到類文件中。用到intVal的代碼被直接替換成42,而使用strVal的會指向一個字符串常量,而不是使用成員變量。
將一個方法或類聲明爲final不會帶來性能的提高,可是會幫助編譯器優化代碼。舉例說,若是編譯器知道一個getter方法不會被重載,那麼編譯器會對其採用內聯調用。
你也能夠將本地變量聲明爲final,一樣,這也不會帶來性能的提高。使用「final」只能使本地變量看起來更清晰些(可是也有些時候這是必須的,好比在使用匿名內部類的時候)。
靜態方法代替虛擬方法
若是不須要訪問某對象的字段,將方法設置爲靜態,調用會加速15%到20%。這也是一種好的作法,由於你能夠從方法聲明中看出調用該方法不須要更新此對象的狀態。
減小沒必要要的全局變量
儘可能避免static成員變量引用資源耗費過多的實例,好比Context
避免建立沒必要要的對象
最多見的例子就是當你要頻繁操做一個字符串時,使用StringBuffer代替String。
對於全部全部基本類型的組合:int數組比Integer數組好,這也歸納了一個基本事實,兩個平行的int數組比 (int,int)對象數組性能要好不少。
整體來講,就是避免建立短命的臨時對象。減小對象的建立就能減小垃圾收集,進而減小對用戶體驗的影響。
避免使用浮點數
一般的經驗是,在Android設備中,浮點數會比整型慢兩倍。
使用實體類比接口好
假設你有一個HashMap對象,你能夠將它聲明爲HashMap或者Map:
Map map1 = new HashMap(); HashMap map2 = new HashMap();
哪一個更好呢?
按照傳統的觀點Map會更好些,由於這樣你能夠改變他的具體實現類,只要這個類繼承自Map接口。傳統的觀點對於傳統的程序是正確的,可是它並不適合嵌入式系統。調用一個接口的引用會比調用實體類的引用多花費一倍的時間。若是HashMap徹底適合你的程序,那麼使用Map就沒有什麼價值。若是有些地方你不能肯定,先避免使用Map,剩下的交給IDE提供的重構功能好了。(固然公共API是一個例外:一個好的API經常會犧牲一些性能)
避免使用枚舉
枚舉變量很是方便,但不幸的是它會犧牲執行的速度和並大幅增長文件體積。
使用枚舉變量可讓你的API更出色,並能提供編譯時的檢查。因此在一般的時候你毫無疑問應該爲公共API選擇枚舉變量。可是當性能方面有所限制的時候,你就應該避免這種作法了。
for循環
訪問成員變量比訪問本地變量慢得多,以下面一段代碼:
永遠不要在for的第二個條件中調用任何方法,以下面一段代碼:
對上面兩個例子最好改成:
瞭解並使用類庫
選擇Library中的代碼而非本身重寫,除了一般的那些緣由外,考慮到系統空閒時會用匯編代碼調用來替代library方法,這可能比JIT中生成的等價的最好的Java代碼還要好。
當你在處理字串的時候,不要吝惜使用String.indexOf(),String.lastIndexOf()等特殊實現的方法。這些方法都是使用C/C++實現的,比起Java循環快10到100倍。
System.arraycopy方法在有JIT的Nexus One上,自行編碼的循環快9倍。
android.text.format包下的Formatter類,提供了IP地址轉換、文件大小轉換等方法;DateFormat類,提供了各類時間轉換,都是很是高效的方法。
TextUtils類,對於字符串處理Android爲咱們提供了一個簡單實用的TextUtils類,若是處理比較簡單的內容不用去思考正則表達式不妨試試這個在android.text.TextUtils的類
高性能MemoryFile類,不少人抱怨Android處理底層I/O性能不是很理想,若是不想使用NDK則能夠經過MemoryFile類實現高性能的文件讀寫操做。MemoryFile適用於哪些地方呢?對於I/O須要頻繁操做的,主要是和外部存儲相關的I/O操做,MemoryFile經過將 NAND或SD卡上的文件,分段映射到內存中進行修改處理,這樣就用高速的RAM代替了ROM或SD卡,性能天然提升很多,對於Android手機而言同時還減小了電量消耗。該類實現的功能不是不少,直接從Object上繼承,經過JNI的方式直接在C底層執行。
Reuse:
Bitmap緩存分爲兩種:
一種是內存緩存,一種是硬盤緩存。
內存緩存(LruCache):
以犧牲寶貴的應用內存爲代價,內存緩存提供了快速的Bitmap訪問方式。系統提供的LruCache類是很是適合用做緩存Bitmap任務的,它將最近被引用到的對象存儲在一個強引用的LinkedHashMap中,而且在緩存超過了指定大小以後將最近不常使用的對象釋放掉。
注意:之前有一個很是流行的內存緩存實現是SoftReference(軟引用)或者WeakReference(弱引用)的Bitmap緩存方案,然而如今已經不推薦使用了。自Android2.3版本(API Level 9)開始,垃圾回收器更着重於對軟/弱引用的回收,這使得上述的方案至關無效。
硬盤緩存(DiskLruCache):
一個內存緩存對加速訪問最近瀏覽過的Bitmap很是有幫助,可是你不能侷限於內存中的可用圖片。GridView這樣有着更大的數據集的組件能夠很輕易消耗掉內存緩存。你的應用有可能在執行其餘任務(如打電話)的時候被打斷,而且在後臺的任務有可能被殺死或者緩存被釋放。一旦用戶從新聚焦(resume)到你的應用,你得再次處理每一張圖片。
在這種狀況下,硬盤緩存能夠用來存儲Bitmap並在圖片被內存緩存釋放後減少圖片加載的時間(次數)。固然,從硬盤加載圖片比內存要慢,而且應該在後臺線程進行,由於硬盤讀取的時間是不可預知的。
注意:若是訪問圖片的次數很是頻繁,那麼ContentProvider可能更適合用來存儲緩存圖片,例如Image Gallery這樣的應用程序。
更多關於內存緩存和硬盤緩存的內容請看Google官方教程 https://developer.android.com/develop/index.html1. Android-Universal-Image-Loader 圖片緩存
目前使用最普遍的圖片緩存,支持主流圖片緩存的絕大多數特性。
項目地址:https://github.com/nostra13/Android-Universal-Image-Loader
2. picasso square開源的圖片緩存
項目地址:https://github.com/square/picasso
特色:(1)能夠自動檢測adapter的重用並取消以前的下載
(2)圖片變換
(3)能夠加載本地資源
(4)能夠設置佔位資源
(5)支持debug模式
3. ImageCache 圖片緩存,包含內存和Sdcard緩存
項目地址:https://github.com/Trinea/AndroidCommon
特色:
(1)支持預取新圖片,支持等待隊列
(2)包含二級緩存,可自定義文件名保存規則
(3)可選擇多種緩存算法(FIFO、LIFO、LRU、MRU、LFU、MFU等13種)或自定義緩存算法
(4)可方便的保存及初始化恢復數據
(5)支持不一樣類型網絡處理
(6)可根據系統配置初始化緩存等
對象池:
對象池使用的基本思路是:將用過的對象保存起來,等下一次須要這種對象的時候,再拿出來重複使用,從而在必定程度上減小頻繁建立對象所形成的開銷。 並不是全部對象都適合拿來池化――由於維護對象池也要形成必定開銷。對生成時開銷不大的對象進行池化,反而可能會出現「維護對象池的開銷」大於「生成新對象的開銷」,從而使性能下降的狀況。可是對於生成時開銷可觀的對象,池化技術就是提升性能的有效策略了。
線程池的基本思想仍是一種對象池的思想,開闢一塊內存空間,裏面存放了衆多(未死亡)的線程,池中線程執行調度由池管理器來處理。當有線程任務時,從池中取一個,執行完成後線程對象歸池,這樣能夠避免反覆建立線程對象所帶來的性能開銷,節省了系統的資源。
好比:一個應用要和網絡打交道,有不少步驟須要訪問網絡,爲了避免阻塞主線程,每一個步驟都建立個線程,在線程中和網絡交互,用線程池就變的簡單,線程池是對線程的一種封裝,讓線程用起來更加簡便,只須要創一個線程池,把這些步驟像任務同樣放進線程池,在程序銷燬時只要調用線程池的銷燬函數便可。
java提供了ExecutorService和Executors類,咱們能夠應用它去創建線程池。
一般能夠創建以下4種:
更多關於線程池的內容我推薦這篇文章: http://www.xuanyusong.com/archives/2439
要根據狀況適度使用緩存,由於內存有限。
能保存路徑地址的就不要存放圖片數據,不常用的儘可能不要緩存,不用時就清空。
OOM:
內存泄露能夠引起不少的問題:
1.程序卡頓,響應速度慢(內存佔用高時JVM虛擬機會頻繁觸發GC)
2.莫名消失(當你的程序所佔內存越大,它在後臺的時候就越可能被幹掉。反以內存佔用越小,在後臺存在的時間就越長)
3.直接崩潰(OutOfMemoryError)
ANDROID內存面臨的問題:
1.有限的堆內存,原始只有16M
2.內存大小消耗等根據設備,操做系統等級,屏幕尺寸的不一樣而不一樣
3.程序不能直接控制
4.支持後臺多任務處理(multitasking)
5.運行在虛擬機之上
5R:
本文主要經過以下的5R方法來對ANDROID內存進行優化:
1.Reckon(計算)
首先須要知道你的app所消耗內存的狀況,知己知彼才能百戰不殆
2.Reduce(減小)
消耗更少的資源
3.Reuse(重用)
當第一次使用完之後,儘可能給其餘的使用
5.Recycle(回收)
回收資源
4.Review(檢查)
回顧檢查你的程序,看看設計或代碼有什麼不合理的地方。
內存簡介,Reckon(計算):
關於內存簡介,和Reckon的內容請看:ANDROID內存優化(大彙總——上)
Reduce(減小) ,Reuse(重用):
關於Reduce,和Reuse的內容請看:ANDROID內存優化(大彙總——中)
Recycle(回收):
Recycle(回收),回收能夠說是在內存使用中最重要的部分。由於內存空間有限,不管你如何優化,如何節省內存總有用完的時候。而回收的意義就在於去清理和釋放那些已經閒置,廢棄再也不使用的內存資源和內存空間。
由於在Java中有垃圾回收(GC)機制,因此咱們平時都不會太關注它,下面就來簡單的介紹一下回收機制:
垃圾回收(GC):
Java垃圾回收器:
在C,C++或其餘程序設計語言中,資源或內存都必須由程序員自行聲明產生和回收,不然其中的資源將消耗,形成資源的浪費甚至崩潰。但手工回收內存每每是一項複雜而艱鉅的工做。
因而,Java技術提供了一個系統級的線程,即垃圾收集器線程(Garbage Collection Thread),來跟蹤每一塊分配出去的內存空間,當Java 虛擬機(Java Virtual Machine)處於空閒循環時,垃圾收集器線程會自動檢查每一快分配出去的內存空間,而後自動回收每一快能夠回收的無用的內存塊。
做用:
1.清除不用的對象來釋放內存:
採用一種動態存儲管理技術,它自動地釋放再也不被程序引用的對象,按照特定的垃圾收集算法來實現資源自動回收的功能。當一個對象再也不被引用的時候,內存回收它佔領的空間,以便空間被後來的新對象使用。
2.消除堆內存空間的碎片:
因爲建立對象和垃圾收集器釋放丟棄對象所佔的內存空間,內存會出現碎片。碎片是分配給對象的內存塊之間的空閒內存洞。碎片整理將所佔用的堆內存移到堆的一端,JVM將整理出的內存分配給新的對象。
垃圾回收器優勢:
1.減輕編程的負擔,提升效率:
使程序員從手工回收內存空間的繁重工做中解脫了出來,由於在沒有垃圾收集機制的時候,可能要花許多時間來解決一個難懂的存儲器問題。在用Java語言編程的時候,靠垃圾收集機制可大大縮短期。
2.它保護程序的完整性:
所以垃圾收集是Java語言安全性策略的一個重要部份。
垃圾回收器缺點:
1.佔用資源時間:
Java虛擬機必須追蹤運行程序中有用的對象, 並且最終釋放沒用的對象。這一個過程須要花費處理器的時間。
2.不可預知:
垃圾收集器線程雖然是做爲低優先級的線程運行,但在系統可用內存量太低的時候,它可能會突發地執行來挽救內存資源。固然其執行與否也是不可預知的。
3.不肯定性:
不能保證一個無用的對象必定會被垃圾收集器收集,也不能保證垃圾收集器在一段Java語言代碼中必定會執行。
一樣也沒有辦法預知在一組均符合垃圾收集器收集標準的對象中,哪個會被首先收集。
4.不可操做
垃圾收集器不能夠被強制執行,但程序員能夠經過調用System. gc方法來建議執行垃圾收集器。
finalize():
每個對象都有一個finalize方法,這個方法是從Object類繼承來的。
當垃圾回收肯定不存在對該對象的更多引用時,由對象的垃圾回收器調用此方法。
Java 技術容許使用finalize方法在垃圾收集器將對象從內存中清除出去以前作必要的清理工做。一旦垃圾回收器準備好釋放對象佔用的空間,將首先調用其finalize()方法,而且在下一次垃圾回收動做發生時,纔會真正回收對象佔用的內存。
簡單的說finalize方法是在垃圾收集器刪除對象以前對這個對象調用的
System.gc():
咱們能夠調用System.gc方法,建議虛擬機進行垃圾回收工做(注意,是建議,但虛擬機會不會這樣幹,咱們也沒法預知!)
下面來看一個例子來了解finalize()和System.gc()的使用:
答案是:不必定!
由於不管是設置gc的引用爲null仍是調用System.gc()方法都只是"建議"垃圾回收器進行垃圾回收,可是最終全部權還在垃圾回收器手中,它會不會進行回收咱們沒法預知!
垃圾回收面試題:
最後經過網上找到的3道面試題來結束垃圾回收的內容。
面試題一:
問:這段代碼中,第幾行的fobj 符合垃圾收集器的收集標準?
資源的回收:
剛纔講了一堆理論的東西,下面來點實際能用上的,資源的回收:
Thread(線程)回收:
線程中涉及的任何東西GC都不能回收(Anything reachable by a thread cannot be GC'd ),因此線程很容易形成內存泄露。
以下面代碼所示:
最後的結果是線程並不會被回收,它會一直的運行下去。
由於運行中的線程是稱之爲垃圾回收根(GC Roots)對象的一種,不會被垃圾回收。當垃圾回收器判斷一個對象是否可達,老是使用垃圾回收根對象做爲參考點。
Cursor(遊標)回收:
Cursor是Android查詢數據後獲得的一個管理數據集合的類,在使用結束之後。應該保證Cursor佔用的內存被及時的釋放掉,而不是等待GC來處理。而且Android明顯是傾向於編程者手動的將Cursor close掉,由於在源代碼中咱們發現,若是等到垃圾回收器來回收時,會給用戶以錯誤提示。
因此咱們使用Cursor的方式通常以下:
Receiver(接收器)回收
Stream/File(流/文件)回收:
主要針對各類流,文件資源等等如:
InputStream/OutputStream,SQLiteOpenHelper,SQLiteDatabase,Cursor,文件,I/O,Bitmap圖片等操做等都應該記得顯示關閉。
和以前介紹的Cursor道理相似,就很少說了。
Review:
Review(回顧,檢查),你們都知道Code Review的重要性。而這裏我說的Review和Code Review差很少,主要目的就是檢查代碼中存在的不合理和能夠改進的地方,固然這個Review須要你們本身來作啦。
Code Review(代碼檢查):
Code Review主要檢查代碼中存在的一些不合理或能夠改進優化的地方,你們能夠參考以前寫的Reduce,Reuse和Recycle都是側重講解這方面的。
UI Review(視圖檢查):
Android對於視圖中控件的佈局渲染等會消耗不少的資源和內存,因此這部分也是咱們須要注意的。
減小視圖層級:
如上圖你們能夠看到,hierarchyviewer能夠很是清楚的看到當前視圖的層級結構,而且能夠查看視圖的執行效率(視圖上的小圓點,綠色表示流暢,黃色和紅色次之),因此咱們能夠很方便的查看哪些view可能會影響咱們的性能從而去進一步優化它。
hierarchyviewer還提供另一種列表式的查看方式,能夠查看詳細的屏幕畫面,具體到像素級別的問題均可以經過它發現。
ViewStub標籤
此標籤可使UI在特殊狀況下,直觀效果相似於設置View的不可見性,可是其更大的意義在於被這個標籤所包裹的Views在默認狀態下不會佔用任何內存空間。
include標籤
能夠經過這個標籤直接加載外部的xml到當前結構中,是複用UI資源的經常使用標籤。
merge標籤
它在優化UI結構時起到很重要的做用。目的是經過刪減多餘或者額外的層級,從而優化整個Android Layout的結構。
(注意:靈活運用以上3個標籤能夠有效減小視圖層級,具體使用你們能夠上網搜搜)
佈局用Java代碼比寫在XML中快
通常狀況下對於Android程序佈局每每使用XML文件來編寫,這樣能夠提升開發效率,可是考慮到代碼的安全性以及執行效率,能夠經過Java代碼執行建立,雖然Android編譯過的XML是二進制的,可是加載XML解析器的效率對於資源佔用仍是比較大的,Java處理效率比XML快得多,可是對於一個複雜界面的編寫,可能須要一些套嵌考慮,若是你思惟靈活的話,使用Java代碼來佈局你的Android應用程序是一個更好的方法。
重用系統資源:
1. 利用系統定義的id
好比咱們有一個定義ListView的xml文件,通常的,咱們會寫相似下面的代碼片斷。
這裏咱們定義了一個ListView,定義它的id是"@+id/mylist"。實際上,若是沒有特別的需求,就能夠利用系統定義的id,相似下面的樣子。
2. 利用系統的圖片資源
這樣作的好處,一個是美工不須要重複的作一份已有的圖片了,能夠節約很多工時;另外一個是能保證咱們的應用程序的風格與系統一致。
3. 利用系統的字符串資源
若是使用系統的字符串,默認就已經支持多語言環境了。如上述代碼,直接使用了@android:string/yes和@android:string/no,在簡體中文環境下會顯示「肯定」和「取消」,在英文環境下會顯示「OK」和「Cancel」。
4. 利用系統的Style
假設佈局文件中有一個TextView,用來顯示窗口的標題,使用中等大小字體。可使用下面的代碼片斷來定義TextView的Style。
5. 利用系統的顏色定義
除了上述的各類系統資源之外,還可使用系統定義好的顏色。在項目中最經常使用的,就是透明色的使用。
除了上面介紹的之外還有不少其餘Android系統自己自帶的資源,它們在應用中均可以直接使用。具體的,能夠進入android-sdk的相應文件夾中去查看。例如:能夠進入$android-sdk$\platforms\android-8\data\res,裏面的系統資源就盡收眼底了。
開發者須要花一些時間去熟悉這些資源,特別是圖片資源和各類Style資源,這樣在開發過程當中,能重用的儘可能重用,並且有時候使用系統提供的效果可能會更好。
其餘小tips:
1. 分辨率適配-ldpi,-mdpi, -hdpi配置不一樣精度資源,系統會根據設備自適應,包括drawable, layout,style等不一樣資源。
2.儘可能使用dp(density independent pixel)開發,不用px(pixel)。
3.多用wrap_content, match_parent
4.永遠不要使用AbsoluteLayout
5.使用9patch(經過~/tools/draw9patch.bat啓動應用程序),png格式
6.將Acitivity中的Window的背景圖設置爲空。getWindow().setBackgroundDrawable(null);android的默認背景是否是爲空。
7.View中設置緩存屬性.setDrawingCache爲true。
Desgin Review(設計檢查):
Desgin Review主要側重檢查一下程序的設計是否合理,包括框架的設計,界面的設計,邏輯的設計(其實這些東西開發以前就應該想好了)。
框架設計:
是否認義了本身的Activity和fragment等經常使用控件的基類去避免進行重複的工做
是否有完善的異常處理機制,即便真的出現OOM也不會直接崩潰致使直接退出程序
界面設計:
1.在視圖中加載你所須要的,而不是你所擁有。由於用戶不可能同時看到全部東西。最典型的例子就是ListView中的滑動加載。
2.若是數據特別大,此時應該暗示用戶去點擊加載,而不是直接加載。
3.合理運用分屏,轉屏等,它是個雙刃劍,由於它便可以使程序更加美觀功能更加完善,但也相應增長了資源開銷。
邏輯設計:
避免子類直接去控制父類中內容,可使用監聽等方式去解決