jvm將class文讀取到內(nèi)存中,經(jīng)過(guò)對(duì)class文件的校驗(yàn)萄传、轉(zhuǎn)換解析进胯、初始化最終在jvm的heap和方法區(qū)分配內(nèi)存形成可以被jvm直接使用的類型的過(guò)程
Java 虛擬機(jī) 一般有五部分構(gòu)成:
- 1送漠、一組指令集
- 2喧兄、一組寄存器
- 3、一個(gè)棧
- 4宅粥、一個(gè)垃圾收集堆
- 5参袱、一個(gè)方法區(qū)域
指令:類似匯編語(yǔ)言,處理cpu的運(yùn)算
寄存器:保存虛擬機(jī)運(yùn)行狀態(tài)秽梅。 包括4中(pc:java程序計(jì)數(shù)器 opTop 指向操作數(shù)棧頂指針 frame 指向方法環(huán)境的指針 vars指向局部變量第一個(gè)變量指針)
棧:java 虛擬機(jī)中的棧分3 個(gè)區(qū) : 1抹蚀、局部變量區(qū) 2、運(yùn)行環(huán)境區(qū)(1企垦、動(dòng)態(tài)鏈接 2环壤、正常返回值 3、異常返回 )3钞诡、操作數(shù)棧
垃圾回收堆: 運(yùn)行時(shí)數(shù)據(jù)區(qū)郑现,對(duì)象從這里分配空間
方法區(qū):保存編譯后的代碼和符號(hào)表,class 和 static
二:JVM內(nèi)存區(qū)域模型
1.方法區(qū)
也稱”永久代” 、“非堆”荧降, 它用于存儲(chǔ)虛擬機(jī)加載的類信息接箫、常量、靜態(tài)變量朵诫、是各個(gè)線程共享的內(nèi)存區(qū)域列牺。默認(rèn)最小值為16MB,最大值為64MB拗窃,可以通過(guò)-XX:PermSize 和 -XX:MaxPermSize 參數(shù)限制方法區(qū)的大小瞎领。
運(yùn)行時(shí)常量池:是方法區(qū)的一部分,Class文件中除了有類的版本随夸、字段九默、方法、接口等描述信息外宾毒,還有一項(xiàng)信息是常量池驼修,用于存放編譯器生成的各種符號(hào)引用,這部分內(nèi)容將在類加載后放到方法區(qū)的運(yùn)行時(shí)常量池中诈铛。
2.虛擬機(jī)棧
描述的是Java 方法執(zhí)行的內(nèi)存模型:每個(gè)方法被執(zhí)行的時(shí)候 都會(huì)創(chuàng)建一個(gè)“棧幀”用于存儲(chǔ)局部變量表(包括參數(shù))乙各、操作棧、方法出口等信息幢竹。每個(gè)方法被調(diào)用到執(zhí)行完的過(guò)程耳峦,就對(duì)應(yīng)著一個(gè)棧幀在虛擬機(jī)棧中從入棧到出棧的過(guò)程。聲明周期與線程相同焕毫,是線程私有的蹲坷。
局部變量表存放了編譯器可知的各種基本數(shù)據(jù)類型(boolean、byte邑飒、char循签、short、int疙咸、float县匠、long、double)撒轮、對(duì)象引用(引用指針乞旦,并非對(duì)象本身),其中64位長(zhǎng)度的long和double類型的數(shù)據(jù)會(huì)占用2個(gè)局部變量的空間腔召,其余數(shù)據(jù)類型只占1個(gè)杆查。局部變量表所需的內(nèi)存空間在編譯期間完成分配,當(dāng)進(jìn)入一個(gè)方法時(shí)臀蛛,這個(gè)方法需要在棧幀中分配多大的局部變量是完全確定的亲桦,在運(yùn)行期間棧幀不會(huì)改變局部變量表的大小空間。
3.本地方法棧
與虛擬機(jī)椬瞧停基本類似客峭,區(qū)別在于虛擬機(jī)棧為虛擬機(jī)執(zhí)行的java方法服務(wù),而本地方法棧則是為Native方法服務(wù)抡柿。
4.堆
也叫做java 堆舔琅、GC堆是java虛擬機(jī)所管理的內(nèi)存中最大的一塊內(nèi)存區(qū)域,也是被各個(gè)線程共享的內(nèi)存區(qū)域洲劣,在JVM啟動(dòng)時(shí)創(chuàng)建备蚓。該內(nèi)存區(qū)域存放了對(duì)象實(shí)例及數(shù)組(所有new的對(duì)象)课蔬。其大小通過(guò)-Xms(最小值)和-Xmx(最大值)參數(shù)設(shè)置,-Xms為JVM啟動(dòng)時(shí)申請(qǐng)的最小內(nèi)存郊尝,默認(rèn)為操作系統(tǒng)物理內(nèi)存的1/64但小于1G二跋,-Xmx為JVM可申請(qǐng)的最大內(nèi)存,默認(rèn)為物理內(nèi)存的1/4但小于1G流昏,默認(rèn)當(dāng)空余堆內(nèi)存小于40%時(shí)扎即,JVM會(huì)增大Heap到-Xmx指定的大小,可通過(guò)-XX:MinHeapFreeRation=來(lái)指定這個(gè)比列况凉;當(dāng)空余堆內(nèi)存大于70%時(shí)谚鄙,JVM會(huì)減小heap的大小到-Xms指定的大小,可通過(guò)XX:MaxHeapFreeRation=來(lái)指定這個(gè)比列刁绒,對(duì)于運(yùn)行系統(tǒng)闷营,為避免在運(yùn)行時(shí)頻繁調(diào)整Heap的大小,通常-Xms與-Xmx的值設(shè)成一樣膛锭。
由于現(xiàn)在收集器都是采用分代收集算法粮坞,堆被劃分為新生代和老年代。新生代主要存儲(chǔ)新創(chuàng)建的對(duì)象和尚未進(jìn)入老年代的對(duì)象初狰。老年代存儲(chǔ)經(jīng)過(guò)多次新生代GC(Minor GC)任然存活的對(duì)象莫杈。
新生代:
程序新創(chuàng)建的對(duì)象都是從新生代分配內(nèi)存,新生代由Eden Space和兩塊相同大小的Survivor Space(通常又稱S0和S1或From和To)構(gòu)成奢入,可通過(guò)-Xmn參數(shù)來(lái)指定新生代的大小筝闹,也可以通過(guò)-XX:SurvivorRation來(lái)調(diào)整Eden Space及Survivor Space的大小。
老年代:
用于存放經(jīng)過(guò)多次新生代GC任然存活的對(duì)象腥光,例如緩存對(duì)象关顷,新建的對(duì)象也有可能直接進(jìn)入老年代,主要有兩種情況:①.大對(duì)象武福,可通過(guò)啟動(dòng)參數(shù)設(shè)置-XX:PretenureSizeThreshold=1024(單位為字節(jié)议双,默認(rèn)為0)來(lái)代表超過(guò)多大時(shí)就不在新生代分配,而是直接在老年代分配捉片。②.大的數(shù)組對(duì)象平痰,切數(shù)組中無(wú)引用外部對(duì)象。
老年代所占的內(nèi)存大小為-Xmx對(duì)應(yīng)的值減去-Xmn對(duì)應(yīng)的值伍纫。
5.程序計(jì)數(shù)器
是最小的一塊內(nèi)存區(qū)域宗雇,它的作用是當(dāng)前線程所執(zhí)行的字節(jié)碼的行號(hào)指示器,在虛擬機(jī)的模型里莹规,字節(jié)碼解釋器工作時(shí)就是通過(guò)改變這個(gè)計(jì)數(shù)器的值來(lái)選取下一條需要執(zhí)行的字節(jié)碼指令赔蒲,分支、循環(huán)、異常處理舞虱、線程恢復(fù)等基礎(chǔ)功能都需要依賴計(jì)數(shù)器完成欢际。
三:直接內(nèi)存
直接內(nèi)存并不是虛擬機(jī)內(nèi)存的一部分,也不是Java虛擬機(jī)規(guī)范中定義的內(nèi)存區(qū)域砾嫉。jdk1.4中新加入的NIO幼苛,引入了通道與緩沖區(qū)的IO方式,它可以調(diào)用Native方法直接分配堆外內(nèi)存焕刮,這個(gè)堆外內(nèi)存就是本機(jī)內(nèi)存,不會(huì)影響到堆內(nèi)存的大小墙杯。
1配并、一個(gè)Android 手機(jī)中開啟N個(gè)app,那么 此時(shí)啟動(dòng)了N個(gè)虛擬機(jī)。每次運(yùn)行一個(gè) java main()方法的程序都會(huì)啟動(dòng)一個(gè)虛擬機(jī)高镐。每個(gè)虛擬機(jī)都有一個(gè) 方法區(qū) 和 堆區(qū) 供該虛擬機(jī)中所有 線程共享溉旋。每次啟動(dòng)線程 得到兩個(gè)東西: 一個(gè)是pc寄存器 ,一個(gè)是java棧 :
pc寄存器
java棧
指令1
指令2
...
操作數(shù)
局部變量
返回值
中間結(jié)果
java虛擬機(jī)和 Dalvik 虛擬機(jī)差別 :
1嫉髓、java 虛擬機(jī)基于棧观腊,dalvik虛擬機(jī)給予寄存器(理論上 dalvik要離cpu近,處理速度要快算行,少了一些讀取棧數(shù)據(jù)的操作)
2梧油、執(zhí)行 的字節(jié)碼文件不一樣
java虛擬機(jī) : java-->.class-->.jar
dalvik : java → .class-→.dex
jar 包里面包含了很多的class每個(gè)class 單獨(dú)包含 頭信息(如編譯版本)、常量池州邢、類信息儡陨、域、方法量淌、屬性骗村。dex把所有的class里面信息整合在一起.
3、dalvik 會(huì)通過(guò)zygote preloadclass 呀枢,完成虛擬機(jī)初始化胚股。
Dalvik運(yùn)作流程
Dalvikvm :創(chuàng)建虛擬機(jī),執(zhí)行參數(shù)指定的java類
dvz: 從zygote孵化出一個(gè)虛擬機(jī)裙秋,和dalvikvm的區(qū)別是琅拌,dvz預(yù)裝了framework的大部分類和資源
app_process:一個(gè)進(jìn)程用于加載 ZygoteInit.java ,SystemServer.java
啟動(dòng)zygotte:
class文件加載:
1、mmap()--------→讀取dex 残吩。生成Dexfile(主要包含頭部财忽,索引,數(shù)據(jù))
2泣侮、dexFileParse()------>分析即彪,將結(jié)果存在Dexfile數(shù)據(jù)結(jié)構(gòu)中
3、加載class 文件(需要用ClassObject保存加載類)
4、加載基本類庫(kù)文件
5隶校、加載用戶類文件
Apk加載機(jī)制
派生了 兩個(gè)類 DexClassLoader漏益,PathClassLoader 集成 ClassLoader,重寫了findClass深胳,而不能使用defineClass绰疤。符合雙親委派原則。
DexClassLoader 和 pathClassLoader區(qū)別 :
DexClassLoader 可以加載 沒(méi)有安裝到app內(nèi)部的jar 或者apk 文件舞终,必須先把文件裝近Application 目錄下轻庆。DexClassLoader 傳入了一個(gè)optimizedDirectory加載問(wèn)類以后會(huì)生成新的dex文件;而 PathClassLoader 的optimizedDirectory=null敛劝,所以不會(huì)進(jìn)行組裝新的dex文件余爆,故只能加載已經(jīng)裝載到app里面的文件。
ClasLoader 的 loaderClass 函數(shù)中:
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
long t0 = System.nanoTime();
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
}
}
return c;
}
Dalvik進(jìn)程管理
1夸盟、linux 的啟動(dòng)腳本都是init.rc 在system/core/rootdir/init.rc
2蛾方、
java虛擬機(jī)內(nèi)存分配策略
內(nèi)存分配會(huì)涉及到 :
1.寄存器:最快的存儲(chǔ)區(qū), 由編譯器根據(jù)需求進(jìn)行分配,我們?cè)诔绦蛑袩o(wú)法控制.
- 棧:存放基本類型的變量數(shù)據(jù)和對(duì)象的引用,但對(duì)象本身不存放在棧中上陕,而是存放在堆(new 出來(lái)的對(duì)象)或者常量池中(字符串常量對(duì)象存放在常量池中桩砰。)
- 堆:存放所有new出來(lái)的對(duì)象。
- 靜態(tài)域:存放靜態(tài)成員(static定義的)
- 常量池:存放字符串常量和基本類型常量(public static final)释簿。
- 非RAM存儲(chǔ):硬盤等永久存儲(chǔ)空間
這里我們主要關(guān)心棧亚隅,堆和常量池,對(duì)于棧和常量池中的對(duì)象可以共享辕万,對(duì)于堆中的對(duì)象不可以共享枢步。棧中的數(shù)據(jù)大小和生命周期是可以確定的,當(dāng)沒(méi)有引用指向數(shù)據(jù)時(shí)渐尿,這個(gè)數(shù)據(jù)就會(huì)消失醉途。堆中的對(duì)象的由垃圾回收器負(fù)責(zé)回收,因此大小和生命周期不需要確定砖茸,具有很大的靈活性隘擎。
對(duì)于字符串:其對(duì)象的引用都是存儲(chǔ)在棧中的,如果是編譯期已經(jīng)創(chuàng)建好(直接用雙引號(hào)定義的)的就存儲(chǔ)在常量池中凉夯,如果是運(yùn)行期(new出來(lái)的)才能確定的就存儲(chǔ)在堆中货葬。對(duì)于equals相等的字符串,在常量池中永遠(yuǎn)只有一份劲够,在堆中有多份震桶。
堆內(nèi)區(qū) :分3種類型
1、永久存儲(chǔ)區(qū)Permanent space(java 系統(tǒng)Class interferce 元數(shù)據(jù)征绎,運(yùn)行環(huán)境蹲姐,不會(huì)被垃圾回收)
2、新生區(qū) Young Generation Space(進(jìn)行產(chǎn)生、分配柴墩、最后被垃圾回收收集忙厌,消亡)
3、養(yǎng)老區(qū) Tenure Generation Space
堆區(qū)的數(shù)據(jù)有 new,newArray,anewArray,multianewarray等指令建立江咳。
例如 :
String a=new String(“aaaaa”);存到堆里面
String b=”bbb”; //因?yàn)椤眀bb” 逢净,先在棧里創(chuàng)建對(duì)象b,在常量池查找”bbb”歼指,如果沒(méi)有就將”bbb”放入常量池爹土,另b→“bbb”.
所以
String a=”aaaa”;
String b=”aaaa”;
system.out.println(a==b)东臀; //true
直接內(nèi)存:各種NIO 流
Dalvik內(nèi)存分配
1着饥、對(duì)象布局 :(1、clazz 2惰赋、lock 3、data)
堆: heapBase ,heapLimit ,dalvik.vm.heapsize=32M 一般
可以通過(guò)VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
垃圾回收
虛引用:任何時(shí)刻都可能被垃圾回收器回收呵哨,唯一作用是赁濒,在垃圾回收的時(shí)候能得到系統(tǒng)通知,phatomReference ,必須和ReferenceQueue 聯(lián)合使用孟害。
他的使用場(chǎng)景:1拒炎、用來(lái)跟蹤對(duì)象何時(shí)被回收。2挨务、避免析構(gòu)函數(shù)后的一些問(wèn)題击你。
Java垃圾收集:
常見的收集策略:
- Reference Count(引用計(jì)數(shù))
在每個(gè)對(duì)象有引用的時(shí)候進(jìn)行計(jì)數(shù) +1,釋放 -1 谎柄。當(dāng)某一個(gè)對(duì)象引用值=0的時(shí)候丁侄,說(shuō)明這個(gè)對(duì)象需要回收。(優(yōu)勢(shì):簡(jiǎn)單朝巫,不需要暫停整個(gè)應(yīng)用鸿摇。 缺點(diǎn):不能處理循環(huán)引用) - 跟蹤收集器:(包含3種)
1、Mark-sweep collector: 掃描活躍的對(duì)象劈猿,作標(biāo)記拙吉,標(biāo)記完后清除沒(méi)有標(biāo)記的對(duì)象(優(yōu)點(diǎn)?可以處理循環(huán)引用揪荣,缺點(diǎn)暫停掃描耗時(shí))
2.copying collector(復(fù)制收集): 準(zhǔn)備兩塊空間筷黔,保證同一時(shí)間只有一塊空間處于活躍狀態(tài),每次等一塊空間滿了仗颈,掃描把活躍的對(duì)象放置另一塊空間佛舱,留下的就是不活躍的對(duì)象。等把活躍對(duì)象復(fù)制到新的空間后,釋放剩余的空間名眉。
3.mark-compat-colletor: 綜合了 mark-sweep 和copying collector粟矿,分兩階段,一损拢、先進(jìn)行mark-sweep 標(biāo)記陌粹,清除。把活躍對(duì)象壓棧底福压。(減少碎片)
java 采用了分代收集策略:
新生代采用Mark-compat 命名(minorGC)掏秩,對(duì)老年代采用mark-sweep命名(FullGc). 注意:System.gc () 執(zhí)行的是FullGc
引入 1、Serial Collector 串行程收集荆姆,只用于新生代收集 2蒙幻、concurrent collector 并發(fā)收集器,縮短收集時(shí)間胆筒,用于老生代和 持久代收集邮破。
Dalvik 垃圾回收:
垃圾回收機(jī)制: 智能指針(WP 和 SP) sp(Strong point) wp(weak Point)強(qiáng)指針和若指針 ,類似與java的強(qiáng)引用和 弱引用.
dalvik 下面 在 reBase 這個(gè)類中.
dalvik垃圾收集3種算法
1仆救、引用計(jì)數(shù)
2抒和、mark-sweep
3、SemiSpaceCopy