Java記憶體機制以及Android記憶體優化
Java記憶體機制
1. 虛擬機器執行時資料區
基本概念
虛擬機器
模擬某種計算機體系結構,執行特定指令集的軟體。包括程序虛擬機器和系統虛擬機器(VMWare)
- 程序虛擬機器:JVM、Adobe Flash Player、FC模擬器
- 高階語言虛擬機器:JVM、.NET CLR、P-Code
- Java語言虛擬機器:JVM、Apache Harmony
- Java(TM)虛擬機器
Java(TM)虛擬機器並不是只能執行Java程式
三大商用JVM:Oracle Hotspot、Oracle JRockit Vm、IBM J9 VM- Oracle HotSpot虛擬機器
Oracle JDK自帶的虛擬機器。HotSpot命名來自它的“熱點程式碼探測”技術。
每一個Java程式都對應一個Java虛擬機器例項。
- Oracle HotSpot虛擬機器
- Java(TM)虛擬機器
- Java語言虛擬機器:JVM、Apache Harmony
- 高階語言虛擬機器:JVM、.NET CLR、P-Code
公有設計,私有實現
《Java虛擬機器規範》(JVMS)定義了概念模型,不約束虛擬機器的具體實現。
Java虛擬機器執行時資料區
在《Java虛擬機器規範》中定義了若干種程式執行期間會使用到的儲存不同型別資料的區域。
有一些區域是全域性共享的,隨著虛擬機器啟動而建立,隨著虛擬機器退出而銷燬。有一些區域是執行緒私有的,隨著執行緒開始和結束而建立和銷燬。
是所有Java虛擬機器共同的記憶體區域概念模型。
既然虛擬機器作為一個虛擬的計算機, 來執行我們的程式, 那麼在執行的過程中, 必然要有地方存放我們的程式碼(class檔案); 在執行的過程中, 總會建立很多物件, 必須有地方存放這些物件; 在執行的過程中, 還需要儲存一些執行的狀態, 比如, 將要執行哪個方法, 當前方法執行完成之後, 要返回到哪個方法等資訊, 所以, 必須有一個地方來保持執行的狀態。 上面的描述中, “地方”指的當然就是記憶體區域, 程式執行起來之後, 就是一個動態的過程, 必須合理的劃分記憶體區域, 來存放各種資料。 所以, 在本文中, 將會詳細介紹JVM的執行時資料區。
執行時資料區的劃分
分為程式計數器、Java堆、Java虛擬機器棧、本地方法棧、方法區
- 執行緒私有的資料區包含程式計數器、虛擬機器棧、本地方法棧。
- 全域性共享的是Java堆、方法區(包括常量池)和直接記憶體
- 需要自動記憶體管理(GC)的區域:Java堆、方法區(不規定但實現了GC)、直接記憶體
- 可能出現OOM的區域:Java堆、Java虛擬機器棧、本地方法棧、方法區、直接記憶體
- 可能出現StackOverFlow的區域:Java虛擬機器棧和本地方法棧
程式計數器
這是一塊較小的記憶體空間,它的作用可以看作是當前執行緒所執行的位元組碼的行號指示器。
如果當前執行緒正在執行Java方法,指數器記錄的是正在執行的虛擬機器位元組碼指令的地址;如果是native方法,指數器為空。
這是唯一一個沒有OOM規定的區域。
Java虛擬機器棧
虛擬機器棧描述的是Java方法執行的記憶體模型:每個方法(不包括native方法)在執行時都會建立一個棧幀,用於儲存區域性變數,運算元棧,動態連結,方法出口等資訊。每一個方法從呼叫到執行結束的過程,就對應著一個棧幀在虛擬機器棧中入棧到出棧的過程。
在Java虛擬機器規範中,對該區域記憶體規定了兩種異常狀況:
- StackOverFlowError:當執行緒請求棧深度超出虛擬機器棧所允許的深度時丟擲
- OutOfMemoryError:當Java虛擬機器動態擴充套件到無法申請足夠記憶體時丟擲
本地方法棧
本地方法棧則為虛擬機器使用到的Native方法提供記憶體空間。有些虛擬機器的實現直接把本地方法棧和虛擬機器棧合二為一,比如非常典型的Sun HotSpot虛擬機器。
和虛擬機器棧一樣,本地方法棧區域也會丟擲StackOverflowError和OutOfMemoryError異常。
棧幀
棧幀(Stack Frame)是用於支援虛擬機器進行方法呼叫和方法執行的資料結構,它是虛擬機器執行時資料區的虛擬機器棧(Virtual Machine Stack)的棧元素。
它被用於儲存資料和部分過程結果的資料結構,同時也被用來處理動態連結、方法返回值和異常分派。
在編譯程式碼的時候,棧幀中需要多大的區域性變量表,多深的運算元棧都已經完全確定了,並且寫入到了方法表的Code屬性中,因此一個棧幀需要分配多少記憶體,不會受到程式執行期變數資料的影響,而僅僅取決於具體虛擬機器的實現。
對於執行引擎來講,活動執行緒中,只有虛擬機器棧頂的棧幀才是有效的,稱為當前棧幀(Current Stack Frame),這個棧幀所關聯的方法稱為當前方法(Current Method)。執行引用所執行的所有位元組碼指令都只針對當前棧幀進行操作。
一個完整的棧幀包括:區域性變量表、運算元棧、動態連線資訊、方法正常完成和異常完成資訊
- 區域性變量表
是一組變數值儲存空間,用於存放方法引數和方法內部定義的區域性變數。其大小以slot為最小單位(32位)。在Java程式編譯為Class檔案時,就在方法表的Code屬性的max_locals資料項中確定了該方法需要分配的最大區域性變量表的容量。它存放了編譯期可知的各種基本資料型別(boolean,byte,char,short,int,float,long,double),物件引用,以及returnAddress型別(指向了一條位元組碼指令地址)
區域性變量表用於方法間引數傳遞,以及方法執行過程中儲存基礎資料型別的值和物件的引用。 - 運算元棧
運算元棧也常被稱為操作棧,它是一個後入先出棧,由若干個Entry組成。同區域性變量表一樣,運算元棧的最大深度也是編譯的時候被寫入到方法表的Code屬性的max_stacks資料項中。
在方法執行過程中,棧幀用於儲存計算引數和計算結果;在方法呼叫時,運算元棧也用來準備呼叫方法的引數以及接收方法返回結果。
舉例
Java堆
- 全域性共享
- 通常是Java虛擬機器中最大的一片記憶體區域
- 是Java物件的儲存區域
- 需要實現自動記憶體管理,即GC,但不限制實現方式
- 可能出現OOM異常
Java堆、棧的關聯過程:兩種方式
方式一的優點是訪問速度更快,因為方式二需要兩次指標定位。方式二的優點是,Java堆中的物件經常會變化(GC),此時只需要修改控制代碼池中的引用即可,比較方便。目前第一種方式比較流行。
方法區和執行時常量池
- 全域性共享
- 作用是儲存類的結構資訊
- JVMS不要求進行記憶體管理,但商用Java虛擬機器都實現了自動記憶體管理
- 執行時常量區是方法區的一部分,作用是儲存Java類檔案常量池中的符號資訊
- 可能出現OOM異常
直接記憶體
- 並非JVMS定義的標準記憶體區域
- 隨JDK1.4加入的NIO引入,目的是避免在Java堆和Native堆中來回複製資料帶來的效能損耗
- 全域性共享
- 能被自動管理,但檢測手段比較簡陋
- 可能出現OOM異常
2. 物件判定和回收演算法
可回收物件的判定方法
1. 引用計數演算法
給物件新增一個引用計數器,每當有一個地方引用它時,引用計數器的值加1;引用失效時,值減1;計數器為0的物件可以被回收。
缺點:迴圈引用
OC語言的解決方式:強引用和弱引用。弱引用不會增加引用計數。
2. 可達性分析演算法
由於引用計數演算法的缺陷,Java虛擬機器通常使用這種判定方法。
通過一系列稱為“GC Root”的物件作為起始點,從這些起點向下搜尋,搜尋路線稱為“引用鏈”,如果一個物件到GC Roots沒有任何引用鏈,則判定不可用。
Java中GC Roots:
- 虛擬機器棧中(本地變量表中)引用的物件
- 方法區的類靜態屬性引用的物件
- 方法區中的常量引用的物件
- 本地方法棧中JNI引用的物件
垃圾收集演算法
1. 標記-清除演算法
首先標記出所有需要回收的物件,標記完成後統一清除。
缺陷:產生大量不連續的記憶體碎片
2. 複製演算法
將可用記憶體分為相等的兩塊,每次只使用其中一塊,當一塊記憶體用完了,就將還活著的物件複製到另一塊記憶體中,再把這塊記憶體中的所有物件清除掉。
缺陷:將記憶體縮小了一半、複製開銷
改進:不一定需要分為大小相等的兩塊,可以適當提高可用記憶體比例。
3. 標記-整理演算法
標記過程與“標記-清理演算法”一樣,然後讓所有存活物件向一端移動,最後清理掉邊界以外的物件。
缺陷:系統停頓時間更長(移動消耗)
4. 分代演算法
當今商用Java虛擬機器共同採用的演算法。
根據物件存活週期的不同將記憶體劃分為幾塊。一般把Java堆分為新生代和老年代,然後在不同記憶體區域採用不同的垃圾收集演算法。比如新生代,會產生大量垃圾物件,適合採用複製演算法(需要複製的物件較少),而老年代可以採用標記-清理或者標記-整理演算法。
HotPot虛擬機器的演算法實現
GC Roots列舉
安全點和安全區
Android記憶體優化
1. Android記憶體管理機制
Android系統記憶體分配和回收
- 一個APP通常就是一個程序對應一個虛擬機器
- GC只有在Heap剩餘空間不夠時才觸發垃圾回收
- GC觸發時,所有執行緒都會被暫停(可能導致記憶體抖動)
APP記憶體限制機制
- 不同裝置分配的記憶體限制不同
- 吃記憶體大戶:圖片
切換應用時後臺APP清理機制
- APP切換的LRU cache:最近使用的APP最不可能被清理
- onTrimMemory()回撥
監控記憶體的方法
- Android方法
- AS monitor工具
2. Android記憶體優化方法
資料結構優化
- 字串拼接使用StringBuilder:時間和空間效能都極大優於String
- ArrayMap、SparseMap替換HashMap:時間和空間優化
- 記憶體抖動:重複大量申請物件
- 再小的class會耗費0.5kb
- HashMap一個entry需要額外佔用32b
物件複用
- 複用系統自帶的資源
- listview、GridView複用
3.避免在onDraw中建立物件(因為onDraw經常會呼叫,如果建立物件耗時、耗記憶體,會造成卡頓)
避免記憶體洩漏
記憶體洩漏會導致可用記憶體越來越少,頻繁造成GC
- 單例模式使用了Activity的context(單例物件常駐記憶體),應該使用Application的context
- 靜態變數引用Activity、view等物件沒有及時釋放(靜態變數常駐記憶體)
- 非靜態內部類和匿名內部類會持有外部類(Activity)物件,因此內部類裡不能有靜態引用或耗時任務。否則不能用這兩種內部類。
- Handler:message可能等待很長時間,在處理之前,message都持有handler引用,而handler持有Activity引用。導致洩漏。解決方式是將handler宣告為靜態,並將Activity的弱引用傳給它。或者在Activity結束時同時移除message。
- 資源未關閉造成的記憶體洩漏
對於使用了BraodcastReceiver,ContentObserver,File,遊標 Cursor,Stream,Bitmap等資源的使用,應該在Activity銷燬時及時關閉或者登出,否則這些資源將不會被回收,造成記憶體洩漏。參考http://waylenw.github.io/Android/android-bitmap-memory-yh/
3. OOM優化
當APP申請的記憶體空間大於系統為APP分配的空間時會出現OOM
- 調整影象大小後再放入記憶體
- 採用強引用+軟引用2級快取,提高載入效能
- 及時回收影象
- 不要建立過多的靜態變數
參考文章
相關推薦
Java記憶體機制以及Android記憶體優化
Java記憶體機制 1. 虛擬機器執行時資料區 基本概念 虛擬機器 模擬某種計算機體系結構,執行特定指令集的軟體。包括程序虛擬機器和系統虛擬機器(VMWare) 程序虛擬機器:JVM、Adobe Flash Player、FC模擬器 高階語言虛擬機器:JVM、.NE
Android開發——JobScheduler機制以及Android電量優化
0. 前言在Android Lollipop版本中增加了JobScheduler API,JobScheduler翻譯為任務排程器,可以替代WakeLodk和Alarm執行任務。那麼它們的區別在哪呢?J
Java中的堆疊機制以及堆記憶體和棧記憶體
java中記憶體分配策略及堆和棧的比較 1 記憶體分配策略 按照編譯原理的觀點,程式執行時的記憶體分配有三種策略,分別是靜態的,棧式的,和堆式的. 靜態儲存分配是指在編譯時就能確定每個資料目標在執行時刻的儲存空間需求,因而在編譯時就可以給他們分配固定的記憶體空間.這種分配策略要求程式程式碼中
Android之記憶體機制分析-Android堆和棧
Java 的堆是一個執行時資料區,類的(物件從中分配空間。這些物件通過new、newarray、anewarray和multianewarray等指令建立,它們不需要程式程式碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配記憶體大小,生存期也不必事先告訴編譯器,因為它是在執行時動態分配記憶
效能優化篇---記憶體管理之Android記憶體洩露
記憶體洩漏:當你不再需要某個例項後,但是這個物件卻仍然被引用,防止被垃圾回收。這個情況就叫做記憶體洩露(Memory Leak)。 常見洩漏場景: 1.Handler 導致的記憶體洩漏 12345678910111213141516171819202122 publ
Java虛擬機器的記憶體組成以及堆記憶體介紹
轉載於:http://www.hollischuang.com/archives/80什麼是Java虛擬機器這裡就不介紹了,不明白的可以另外一篇博文:JDK,JRE,JVM區別與聯絡一、java記憶體組成介紹:堆(Heap)和非堆(Non-heap)記憶體按照官方的說法:“J
關於Java的Semaphore以及Android的Looper使用
Android的Looper使用-關於Android非同步訊息處理機制 最近在Android專案中看到這個關鍵詞,一時間無從下手,這裡先賣個關子: new Thread(new Runnable() { @Over
java反射機制 以及通過反射獲得類的變數欄位,方法,建構函式
Class clz =Class.forName("day_29_ReflectAndJson.Person"); //前面獲取本類及其父類共有的方法就不再寫了 直接獲得本類中的所有方法 並傳引數後呼叫本方法 Method[] method=clz.getDec
什麼是Java反射機制以及動態代理
反射機制定義 Java反射機制是在執行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法,對於任意一個物件,都能夠呼叫它的任意一個方法和屬性。 反射機制獲取類的三種方式 1.類名.class
android 記憶體管理以及優化 粗略方案
Android的記憶體管理方式 1.android系統記憶體分配和回收方式 一個app通常就是一個程序對應一個虛擬機器 通過adb shell 檢視應用的記憶體分配情況 ①通過ps來檢視系統內的程序 ②通過 dumpsys meminfo 包名 檢視對應的應用的記憶體
Android記憶體優化—Java的引用方式
四種引用方式 1、強引用(StrongReference) 2、軟引用(SoftReference) 3、弱引用(WeakReference) 4、虛引用(PhantomReference) 強引用(StrongReference) 1、只要某個物件有強引用與之關聯,JVM必
Java效能優化三:記憶體管理與垃圾回收機制,開發必備優化技巧!
一、Java 類載入機制的特點: (1)基於父類的委託機制:執行一個程式時,總是由 AppClass Loader (系統類載入器)開始載入指定的類,在載入類時,每個類載入器會將載入任務上交給其父,如果其父找不到,再由自己去載入, Bootstrap Loader (啟動類載入器)是最頂級的類載
Android 效能優化之記憶體洩漏檢測以及記憶體優化(中)
Android 記憶體洩漏檢測 通過上篇部落格我們瞭解了 Android JVM/ART 記憶體的相關知識和洩漏的原因,再來歸類一下記憶體洩漏的源頭,這裡我們簡單將其歸為一下三類:自身編碼引起由專案開發人員自身的編碼造成;第三方程式碼引起這裡的第三
Android 效能優化之記憶體洩漏檢測以及記憶體優化(下)
Android 記憶體優化 上篇部落格描述瞭如何檢測和處理記憶體洩漏,這種問題從某種意義上講是由於程式碼的錯誤導致的,但是也有一些是程式碼沒有錯誤,但是我們可以通過很多方式去降低記憶體的佔用,使得應用的整體記憶體處於一個健康的水平,下面總結一下記憶
Android學習之 記憶體管理機制與應用記憶體優化
Random Access Memory(RAM)在任何軟體開發環境中都是一個很寶貴的資源。這一點在實體記憶體通常很有限的移動作業系統上,顯得尤為突出。儘管Android的Dalvik虛擬機器扮演了常規的垃圾回收的角色,但這並不意味著你可以忽視app的記憶體分配與釋放的時機與地點。於大多數apps來說
Android記憶體優化-記憶體洩漏的幾個場景以及解決方式
一.什麼是記憶體洩漏 在Java程式中,如果一個物件沒有利用價值了,正常情況下gc是會對其進行回收的,但是此時仍然有其他引用指向這個活在堆記憶體中的物件,那麼gc就不會認為這個物件是一個垃圾,那麼就不會對其進行回收,所以它會一直活在堆記憶體中佔用記憶體
Android記憶體基礎——Java記憶體管理機制
參考連結 參考資料1 背景介紹 Java優勢之一就是其具有垃圾回收機制。在大部分情況下,JVM的GC(垃圾回收器)能夠幫助我們回那些不可到達的物件(就是未被引用的物件)。 當然,在一些情況下
Android記憶體管理機制和記憶體洩漏分析及優化
Android中的記憶體管理機制 分配機制 Android為每個程序分配記憶體的時候,採用了彈性的分配方式,也就是剛開始並不會一下分配很多記憶體給每個程序,而是給每一個程序分配一個“夠用”的量。這個量是根據每一個裝置實際的實體記憶體大小來決定的。隨著應用
Java中的記憶體分配以及棧和堆的區別
Java中的記憶體分配以及棧和堆的區別 (1)棧: 存放的是區域性變數 區域性變數:在方法定義中或者方法宣告上的變數都是區域性變數。 (2)堆: 存放的是所有new出來的東西 特點: a: 每一個new出來的東西都會為其分配一個地制值。 b: 每
JVM記憶體結構、Java記憶體模型以及Java物件模型之間的區別
Java作為一種面向物件的,跨平臺語言,其物件、記憶體等一直是比較難的知識點。而且很多概念的名稱看起來又那麼相似,很多人會傻傻分不清楚。比如本文我們要討論的JVM記憶體結構、Java記憶體模型和Java物件模型,這就是三個截然不同的概念,但是很多人容易弄混。 可以這樣說,很多高階開發甚至都搞