JVM? DVM ART
java中內(nèi)存: 1.寄存器:最快的存儲區(qū), 由編譯器根據(jù)需求進行分配,我們在程序中無法控制.? 2. 棧:存放基本類型的變量數(shù)據(jù)和對象的引用废登,但對象本身不存放在棧中,而是存放在堆(new 出來的對象)或者常量池中(字符串常量對象存放在常量池中哩掺。)? 3. 堆:存放所有new出來的對象娃闲。? 4. 靜態(tài)域:存放靜態(tài)成員(static定義的)? 5. 常量池:存放字符串常量和基本類型常量(public static final)。? 6. 非RAM存儲:硬盤等永久存儲空間? 這里我們主要關(guān)心棧拟赊,堆和常量池,對于棧和常量池中的對象可以共享粹淋,對于堆中的對象不可以共享吸祟。棧中的數(shù)據(jù)大小和生命周期是可以確定的,當沒有引用指向數(shù)據(jù)時桃移,這個數(shù)據(jù)就會消失屋匕。堆中的對象的由垃圾回收器負責回收,因此大小和生命周期不需要確定借杰,具有很大的靈活性过吻。? 對于字符串:其對象的引用都是存儲在棧中的,如果是編譯期已經(jīng)創(chuàng)建好(直接用雙引號定義的)的就存儲在常量池中第步,如果是運行期(new出來的)才能確定的就存儲在堆中疮装。對于equals相等的字符串,在常量池中永遠只有一份粘都,在堆中有多份廓推。
Java把內(nèi)存劃分成兩種:一種是棧內(nèi)存,一種是堆內(nèi)存翩隧。??? ???? ??在函數(shù)中定義的一些基本類型的變量和對象的引用變量都在函數(shù)的棧內(nèi)存中分配樊展。??? ???? ??當在一段代碼塊定義一個變量時,Java就在棧中為這個變量分配內(nèi)存空間堆生,當超過變量的作用域后专缠,Java會自動釋放掉為該變量所分配的內(nèi)存空間,該內(nèi)存空間可以立即被另作他用淑仆。??? ???? ??堆內(nèi)存用來存放由new創(chuàng)建的對象和數(shù)組涝婉。??? ???? ??在堆中分配的內(nèi)存,由Java虛擬機的自動垃圾回收器來管理蔗怠。??? ???? ??在堆中產(chǎn)生了一個數(shù)組或?qū)ο蠛蠖胀洌€可以在棧中定義一個特殊的變量,讓棧中這個變量的取值等于數(shù)組或?qū)ο笤诙褍?nèi)存中的首地址寞射,棧中的這個變量就成了數(shù)組或?qū)ο蟮囊米兞俊??? ???? ??引用變量就相當于是為數(shù)組或?qū)ο笃鸬囊粋€名稱渔工,以后就可以在程序中使用棧中的引用變量來訪問堆中的數(shù)組或?qū)ο蟆?/p>
具體的說: 棧與堆都是Java用來在Ram中存放數(shù)據(jù)的地方。與C++不同桥温,Java自動管理棧和堆引矩,程序員不能直接地設(shè)置棧或堆侵浸。? ??????????Java的堆是一個運行時數(shù)據(jù)區(qū),類的(對象從中分配空間旺韭。這些對象通過new、newarray掏觉、anewarray和multianewarray等指令建立茂翔,它們不需要程序代碼來顯式的釋放。堆是由垃圾回收來負責的履腋,堆的優(yōu)勢是可以動態(tài)地分配內(nèi)存大小珊燎,生存期也不必事先告訴編譯器,因為它是在運行時動態(tài)分配內(nèi)存的遵湖,Java的垃圾收集器會自動收走這些不再使用的數(shù)據(jù)悔政。但缺點是,由于要在運行時動態(tài)分配內(nèi)存延旧,存取速度較慢谋国。? ?????????棧的優(yōu)勢是,存取速度比堆要快迁沫,僅次于寄存器芦瘾,棧數(shù)據(jù)可以共享捌蚊。但缺點是,存在棧中的數(shù)據(jù)大小與生存期必須是確定的近弟,缺乏靈活性缅糟。棧中主要存放一些基本類型的變量(,int,?short,?long,?byte,?float,?double,?boolean,?char)和對象句柄。? ?????????棧有一個很重要的特殊性祷愉,就是存在棧中的數(shù)據(jù)可以共享窗宦。假設(shè)我們同時定義:? int?a?=?3;? int?b?=?3;? 編譯器先處理int?a?=?3二鳄;首先它會在棧中創(chuàng)建一個變量為a的引用赴涵,然后查找棧中是否有3這個值,如果沒找到订讼,就將3存放進來髓窜,然后將a指向3。接著處理int?b?=?3欺殿;在創(chuàng)建完b的引用變量后纱烘,因為在棧中已經(jīng)有3這個值,便將b直接指向3祈餐。這樣擂啥,就出現(xiàn)了a與b同時均指向3的情況。這時帆阳,如果再令a=4哺壶;那么編譯器會重新搜索棧中是否有4值,如果沒有蜒谤,則將4存放進來山宾,并令a指向4;如果已經(jīng)有了鳍徽,則直接將a指向這個地址资锰。因此a值的改變不會影響到b的值。要注意這種數(shù)據(jù)的共享與兩個對象的引用同時指向一個對象的這種共享是不同的阶祭,因為這種情況a的修改并不會影響到b,?它是由編譯器完成的绷杜,它有利于節(jié)省空間。而一個對象引用變量修改了這個對象的內(nèi)部狀態(tài)濒募,會影響到另一個對象引用變量鞭盟。
String是一個特殊的包裝類數(shù)據(jù)」逄辏可以用:? String?str?=?new?String("abc");? String?str?=?"abc";? 兩種的形式來創(chuàng)建齿诉,第一種是用new()來新建對象的,它會在存放于堆中。每調(diào)用一次就會創(chuàng)建一個新的對象粤剧。? 而第二種是先在棧中創(chuàng)建一個對String類的對象引用變量str歇竟,然后查找棧中有沒有存放"abc",如果沒有抵恋,則將"abc"存放進棧焕议,并令str指向”abc”,如果已經(jīng)有”abc”?則直接令str指向“abc”馋记。
比較類里面的數(shù)值是否相等時,用equals()方法懊烤;當測試兩個包裝類的引用是否指向同一個對象時梯醒,用==,下面用例子說明上面的理論腌紧。? String?str1?=?"abc";? String?str2?=?"abc";? System.out.println(str1==str2);?//true? 可以看出str1和str2是指向同一個對象的茸习。
String?str1?=new?String?("abc");? String?str2?=new?String?("abc");? System.out.println(str1==str2);?//?false? 用new的方式是生成不同的對象。每一次生成一個壁肋。? ???????因此用第一種方式創(chuàng)建多個”abc”字符串,在內(nèi)存中其實只存在一個對象而已.?這種寫法有利與節(jié)省內(nèi)存空間.?同時它可以在一定程度上提高程序的運行速度号胚,因為JVM會自動根據(jù)棧中數(shù)據(jù)的實際情況來決定是否有必要創(chuàng)建新對象。而對于String?str?=?new?String("abc")浸遗;的代碼猫胁,則一概在堆中創(chuàng)建新對象,而不管其字符串值是否相等跛锌,是否有必要創(chuàng)建新對象弃秆,從而加重了程序的負擔。? ???????另一方面,?要注意:?我們在使用諸如String?str?=?"abc"髓帽;的格式定義類時菠赚,總是想當然地認為,創(chuàng)建了String類的對象str郑藏。擔心陷阱衡查!對象可能并沒有被創(chuàng)建!而可能只是指向一個先前已經(jīng)創(chuàng)建的對象必盖。只有通過new()方法才能保證每次都創(chuàng)建一個新的對象拌牲。由于String類的immutable性質(zhì),當String變量需要經(jīng)常變換其值時歌粥,應(yīng)該考慮使用StringBuffer類们拙,以提高程序效率。
java中內(nèi)存分配策略及堆和棧的比較? 2.1?內(nèi)存分配策略? 按照編譯原理的觀點,程序運行時的內(nèi)存分配有三種策略,分別是靜態(tài)的,棧式的,和堆式的.? 靜態(tài)存儲分配是指在編譯時就能確定每個數(shù)據(jù)目標在運行時刻的存儲空間需求,因而在編譯時就可以給他們分配固定的內(nèi)存空間.這種分配策略要求程序代碼中不允許有可變數(shù)據(jù)結(jié)構(gòu)(比如可變數(shù)組)的存在,也不允許有嵌套或者遞歸的結(jié)構(gòu)出現(xiàn),因為它們都會導致編譯程序無法計算準確的存儲空間需求.? 棧式存儲分配也可稱為動態(tài)存儲分配,是由一個類似于堆棧的運行棧來實現(xiàn)的.和靜態(tài)存儲分配相反,在棧式存儲方案中,程序?qū)?shù)據(jù)區(qū)的需求在編譯時是完全未知的,只有到運行的時候才能夠知道,但是規(guī)定在運行中進入一個程序模塊時,必須知道該程序模塊所需的數(shù)據(jù)區(qū)大小才能夠為其分配內(nèi)存.和我們在數(shù)據(jù)結(jié)構(gòu)所熟知的棧一樣,棧式存儲分配按照先進后出的原則進行分配阁吝。? 靜態(tài)存儲分配要求在編譯時能知道所有變量的存儲要求,棧式存儲分配要求在過程的入口處必須知道所有的存儲要求,而堆式存儲分配則專門負責在編譯時或運行時模塊入口處都無法確定存儲要求的數(shù)據(jù)結(jié)構(gòu)的內(nèi)存分配,比如可變長度串和對象實例.堆由大片的可利用塊或空閑塊組成,堆中的內(nèi)存可以按照任意順序分配和釋放.
2.2?堆和棧的比較? 上面的定義從編譯原理的教材中總結(jié)而來,除靜態(tài)存儲分配之外,都顯得很呆板和難以理解,下面撇開靜態(tài)存儲分配,集中比較堆和棧:? 從堆和棧的功能和作用來通俗的比較,堆主要用來存放對象的砚婆,棧主要是用來執(zhí)行程序的.而這種不同又主要是由于堆和棧的特點決定的:? 在編程中,例如C/C中,所有的方法調(diào)用都是通過棧來進行的,所有的局部變量,形式參數(shù)都是從棧中分配內(nèi)存空間的装盯。實際上也不是什么分配,只是從棧頂向上用就行,就好像工廠中的傳送帶(conveyor?belt)一樣,Stack?Pointer會自動指引你到放東西的位置,你所要做的只是把東西放下來就行.退出函數(shù)的時候坷虑,修改棧指針就可以把棧中的內(nèi)容銷毀.這樣的模式速度最快,?當然要用來運行程序了.需要注意的是,在分配的時候,比如為一個即將要調(diào)用的程序模塊分配數(shù)據(jù)區(qū)時,應(yīng)事先知道這個數(shù)據(jù)區(qū)的大小,也就說是雖然分配是在程序運行時進行的,但是分配的大小多少是確定的,不變的,而這個"大小多少"是在編譯時確定的,不是在運行時.? 堆是應(yīng)用程序在運行的時候請求操作系統(tǒng)分配給自己內(nèi)存,由于從操作系統(tǒng)管理的內(nèi)存分配,所以在分配和銷毀時都要占用時間埂奈,因此用堆的效率非常低.但是堆的優(yōu)點在于,編譯器不必知道要從堆里分配多少存儲空間迄损,也不必知道存儲的數(shù)據(jù)要在堆里停留多長的時間,因此,用堆保存數(shù)據(jù)時會得到更大的靈活性。事實上,面向?qū)ο蟮亩鄳B(tài)性,堆內(nèi)存分配是必不可少的,因為多態(tài)變量所需的存儲空間只有在運行時創(chuàng)建了對象之后才能確定.在C中账磺,要求創(chuàng)建一個對象時芹敌,只需用?new命令編制相關(guān)的代碼即可。執(zhí)行這些代碼時垮抗,會在堆里自動進行數(shù)據(jù)的保存.當然氏捞,為達到這種靈活性,必然會付出一定的代價:在堆里分配存儲空間時會花掉更長的時間冒版!這也正是導致我們剛才所說的效率低的原因,看來列寧同志說的好,人的優(yōu)點往往也是人的缺點,人的缺點往往也是人的優(yōu)點(暈~).
2.3?JVM中的堆和棧? JVM是基于堆棧的虛擬機.JVM為每個新創(chuàng)建的線程都分配一個堆棧.也就是說,對于一個Java程序來說液茎,它的運行就是通過對堆棧的操作來完成的。堆棧以幀為單位保存線程的狀態(tài)辞嗡。JVM對堆棧只進行兩種操作:以幀為單位的壓棧和出棧操作捆等。? 我們知道,某個線程正在執(zhí)行的方法稱為此線程的當前方法.我們可能不知道,當前方法使用的幀稱為當前幀。當線程激活一個Java方法,JVM就會在線程的?Java堆棧里新壓入一個幀续室。這個幀自然成為了當前幀.在此方法執(zhí)行期間,這個幀將用來保存參數(shù),局部變量,中間計算過程和其他數(shù)據(jù).這個幀在這里和編譯原理中的活動紀錄的概念是差不多的.? 從Java的這種分配機制來看,堆棧又可以這樣理解:堆棧(Stack)是操作系統(tǒng)在建立某個進程時或者線程(在支持多線程的操作系統(tǒng)中是線程)為這個線程建立的存儲區(qū)域栋烤,該區(qū)域具有先進后出的特性。? 每一個Java應(yīng)用都唯一對應(yīng)一個JVM實例挺狰,每一個實例唯一對應(yīng)一個堆班缎。應(yīng)用程序在運行中所創(chuàng)建的所有類實例或數(shù)組都放在這個堆中,并由應(yīng)用所有的線程共享.跟C/C++不同,Java中分配堆內(nèi)存是自動初始化的她渴。Java中所有對象的存儲空間都是在堆中分配的达址,但是這個對象的引用卻是在堆棧中分配,也就是說在建立一個對象時從兩個地方都分配內(nèi)存,在堆中分配的內(nèi)存實際建立這個對象趁耗,而在堆棧中分配的內(nèi)存只是一個指向這個堆對象的指針(引用)而已?
而寄存器是CPU的存儲器沉唠,速度快。 內(nèi)存是CPU和硬盤之間的通道苛败,數(shù)據(jù)結(jié)構(gòu)為堆棧满葛。
按用途可以分為主存儲器和輔助存儲器,主存儲器又稱內(nèi)存罢屈,是CPU能直接尋址的存儲空間嘀韧,它的特點是存取速率快。內(nèi)存一般采用半導 體存儲單元缠捌,包括隨機存儲器(RAM)锄贷、只讀存儲器(ROM)和高級緩存(Cache)译蒂。 從命名可以看出來,隨機存儲器(RAM)可以隨機讀寫數(shù)據(jù)谊却,但是電源關(guān)閉時存儲的數(shù)據(jù)就會丟失柔昼; 只讀存儲器(ROM):(Read Only Memory)只能讀取,不能更改炎辨,即使機器斷電捕透,數(shù)據(jù)也不會丟失; ? ? 高級緩存(Cache):它是介于CPU與內(nèi)存之間碴萧,常用有一級緩存(L1)乙嘀、二級緩存(L2)、三級緩存(L3)(一般存在于Intel系列)破喻。它的讀寫速度比內(nèi)存還快虎谢,當CPU在內(nèi)存中讀取或?qū)懭霐?shù)據(jù) 時,數(shù)據(jù)會被保存在高級緩沖存儲器中低缩,當下次訪問該數(shù)據(jù)時嘉冒,CPU直接讀取高級緩沖存儲器曹货,而不是更慢的內(nèi)存咆繁。 輔助存儲器又稱外存儲器(簡稱外存),就是那些磁盤顶籽、硬盤玩般、光盤啦,也就是你在電腦上看到的C礼饱、D坏为、E、F盤镊绪。
編譯期 使用mvn package匀伏,或者mvn intall打包的過程就叫編譯期。是指將java代碼編譯為機器識別的字節(jié)碼文件的過程蝴韭。
運行期 從jvm加載字節(jié)碼文件够颠,到使用到最后的卸載過程,都是屬于運行期的范疇榄鉴。
加載
將類的.class文件中的二進制數(shù)據(jù)讀到內(nèi)存中履磨,將其放在運時數(shù)據(jù)區(qū)的方法區(qū)內(nèi),然后在堆區(qū)創(chuàng)建一個java.lang.Class對象庆尘,用來封裝類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)剃诅。
加載.class文件的方式
從本地系統(tǒng)上直接加載。
通過網(wǎng)絡(luò)下載.class文件驶忌。
從zip矛辕,jar等歸檔文件中加載.class文件。
將java源文件動態(tài)編譯為.class文件
jVM基于棧,意味著需要去棧中讀寫數(shù)據(jù)如筛,所需要的指令會更多堡牡,這樣會導致速度變慢,對于性能有限的移動設(shè)備顯然不合適杨刨。DVM是基于寄存器的晤柄,它沒有基于棧的虛擬機在復制數(shù)據(jù)時而使用大量的出入棧指令,指令更緊湊妖胀、簡潔芥颈。但是由于顯式的制定了操作數(shù),所以基于寄存器的指令會比基于棧的指令要大
2)執(zhí)行字節(jié)碼不同 JVM中赚抡,java類被編譯成一個或多個.class文件爬坑,并打包成.jar文件,而后JVM會通過相應(yīng)的.class文件和.jar文件獲取相應(yīng)的字節(jié)碼涂臣。 而DVM會用dx工具把所有的class文件打包成一個.dex文件盾计,然后DVM會從該.dex文件中讀取指令和數(shù)據(jù)。這個.dex文件將所有的.class文件里面所包含的信息全部整合到了一起赁遗,這樣加載就加快了速度署辉。
3)DVM允許在有限的內(nèi)存中同時運行多個進程 DVM經(jīng)過優(yōu)化,允許在有限的內(nèi)存中同時運行多個進程岩四。在Android中每一個應(yīng)用都運行在一個DVM實例中哭尝,每一個DVM實例都運行在一個獨立的進程空間,獨立的進程可以防止虛擬機崩潰時所有程序都被關(guān)閉剖煌。 4)DVM由Zygote創(chuàng)建并初始化 在《Android系統(tǒng)啟動流程》中提到過材鹦,Zygote是一個DVM進程,同時也用來創(chuàng)建和初始化其他DVM進程耕姊。每當系統(tǒng)需要一個應(yīng)用程序進程的時候桶唐,Zygote就會fork自身,快速地創(chuàng)建和初始化一個DVM實例茉兰,用于程序運行尤泽。對于一些只讀的系統(tǒng)庫,所有的DVM實例都會和Zygote共享一塊內(nèi)存區(qū)域邦邦,節(jié)省內(nèi)存開銷安吁。 5)DVM有共享機制
DVM中的應(yīng)用每次運行時,字節(jié)碼都需要通過JIT編譯器編譯為機器碼燃辖,這樣會使應(yīng)用程序的運行效率降低鬼店。而在ART中,系統(tǒng)安裝應(yīng)用程序時會進行一次AOT(ahead of time compilation)黔龟,將字節(jié)碼預編譯成機器碼并存儲在本地妇智,這樣應(yīng)用程序每次運行時就不需要執(zhí)行編譯了滥玷,會大大增加效率。但是AOT不是完美的巍棱,它的缺點主要有兩個:第一個是AOT會使安裝應(yīng)用的時間變長惑畴,尤其是復雜的應(yīng)用。第二個是字節(jié)碼預先編譯成機器碼航徙,機器碼需要存儲空間會多一些如贷。為了解決這兩個問題,Android 7.0版本中的ART加入了JIT即時編譯器到踏,作為AOT的一個補充杠袱。應(yīng)用程序安裝時并不會將字節(jié)碼全部編譯成機器碼,而是在系統(tǒng)運行中將熱點代碼編譯成機器碼窝稿,從而縮短應(yīng)用程序安裝時間楣富,并且節(jié)省內(nèi)存。
ART 的缺點
應(yīng)用安裝需要更長的時間伴榔,因為 DEX 字節(jié)碼需要在安裝時就翻譯成機器碼纹蝴。
由于在安裝時時生成的native機器碼是存儲在內(nèi)部存儲器上,所以需要更多的內(nèi)部存儲空間踪少。
當我們使用Android Studio的時候塘安,這些步驟都交給它去做了。
ART對比Dalvik優(yōu)缺點(類似解釋性語言和編譯型語言對比):
優(yōu)點:
系統(tǒng)性能顯著提升秉馏。 應(yīng)用啟動更快耙旦、運行更快脱羡,體驗更流暢萝究,觸感反饋更及時。 更長的電池續(xù)航能力锉罐。 缺點:
更大的存儲空間占用帆竹,可能會增加10%~20%。 更長的應(yīng)用安裝時間脓规。 總的來說ART的功效就是空間換時間 ———————————————— Java虛擬機 Dalvik虛擬機 java虛擬機基于棧栽连,基于棧的機器必須使用指令來載入和操作棧上數(shù)據(jù) Dalvik虛擬機基于寄存器
編譯 classes.dex 文件 編譯 resources.arsc 文件 生成資源索引表resources.arsc。 把resources.arsc轉(zhuǎn)換成的二進制格式侨舆。 清單文件AndroidMenifest.xml文件轉(zhuǎn)為二進制秒紧。 使用debug.keystore對整個程序進行打包簽名。