1. 程式人生 > >Android必須知道的Java記憶體結構及堆疊區別

Android必須知道的Java記憶體結構及堆疊區別

一、認識Android儲存結構

對於Android來說,儲存主要分為三個部分:記憶體、內部儲存以及外部儲存,詳細介紹如下:

(1)記憶體儲存RAM(Random Access Memory)記憶體與PC的記憶體是一樣的,是用來執行程式,不能用來永久儲存資料,手機一旦關機,在記憶體中的所有資料都將會丟失,記憶體也是現在人類製造的所有電子裝置所必需擁有的。(2)內部儲存ROM(Read Only Memory)就是就相當於是PC中的硬碟的角色。用於儲存Andoid 裝置的作業系統和應用程式的儲存介質。也就是說,Android裝置中的Android系統和應用程式(APK檔案)都是存在內部儲存區的。例如手機的/system/目錄、/data/目錄等。(3)外部儲存區相當於PC中的U盤或者行動硬碟。

由於Android裝置通常會將內部儲存器晶片固定在晶片上,所以一般無法更換內部儲存器的。 為了增強Android裝置的儲存能力,很多Android裝置都支援擴充套件的SD卡功能(通常稱之為MicroSD型別的儲存卡)。所以我們經常說的3GB+32GB或者 3GB+64GB是指 記憶體是3GB大小、內部儲存大小為32GB或者64GB大小。

二、 Java記憶體結構


1、棧區(stack),由編譯器自動分配釋放 ,存放函式的引數值,區域性變數的值等。其操作方式類似於資料結構中的棧。
2、堆區(heap),一般由程式設計師分配釋放, 若程式設計師不釋放,JVM不定時觀察發現沒有引用指向時會GC回收 。
3、靜態區(static storage),存放由static修飾的靜態成員。 程式結束後由系統釋放。
4、常量區(constant storage),基礎型別、字串等就是放在這裡的。常量儲存位於堆中。程式結束後由系統釋放 。
5、程式碼區,存放函式體的二進位制程式碼,而且是多個物件共享程式碼空間區域。

基本資料型別存在棧(stack), 引用資料型別存在堆(heap)。java資料型別結構圖:

                                             ┏數值型━┳━整數型:byte short int long
              ┏基本資料型別━━┫                ┗━浮點型:float double
              ┃                            ┣字元型:char                                          

資料型別  ╋                            ┗布林型:boolean              

              ┃                            ┏類(class)
              ┗引用資料型別━━╋介面(interface)
                                             ┗陣列(array)

基本資料型別和引用資料型別的區別:

(1)從概念方面來說:
    基本資料型別:變數名指向具體的數值;
    引用資料型別:變數名指向存資料物件的記憶體地址,即變數名指向hash值。

(2)記憶體構建方面來說:
    基本資料型別:變數在宣告之後java就會立刻分配給他記憶體空間;
    引用資料型別:這類變數宣告時不會分配記憶體,只是儲存了一個記憶體地址。(3)從使用方面來說:
    基本資料型別:使用時需要賦具體值,判斷時使用“==”號;
    引用資料型別:使用時可以賦null,判斷時使用equals方法。

(4)從區域性宣告來說:(即區域性變數)

    基本資料型別:所宣告的變數名及值都是放在方法棧中;

    引用資料型別:所宣告的變數(記憶體地址值)是放在方法的棧中,該變數所指向的物件實際是放在堆記憶體中。

(5)從全域性宣告來說:(即全域性變數)

    基本資料型別:所宣告的變數名及其值放在堆記憶體中;

    引用資料型別:所宣告的變數(記憶體地址值)指向所引用的物件。該變數名和物件都儲存在相應的堆記憶體中。


三、 堆和棧的區別

    棧與堆都是Java用來在Ram中存放資料的地方。與C++不同,Java自動管理棧和堆,程式設計師不能直接地設定棧或堆。
    Java的堆是一個執行時資料區,類的物件從中分配空間。這些物件通過new、new array、anewarray和 multianewarray等指令建立,它們不需要程式程式碼來顯式的釋放。堆是由垃圾回收來負責的,堆的優勢是可以動態地分配記憶體大小,生存期也不必事先告訴編譯器,因為它是在執行時動態分配記憶體的,Java的垃圾收集器會自動收走這些不再使用的資料。但缺點是,由於要在執行時動態分配記憶體,存取速度較慢。
    Java的棧存取速度比堆要快,僅次於暫存器,棧資料可以共享。但缺點是,存在棧中的資料大小與生存期必須是確定的,缺乏靈活性。棧中主要存放一些基本型別的變數(int, short, long, byte, float, double, boolean, char)和物件控制代碼。棧有一個很重要的特殊性,就是存在棧中的資料可以共享。例如:
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的值改變並不影響到b的值,如果令a = 4;那麼編譯器會重新搜尋棧中是否有4值,如果沒有,則將4存放進來,並令a指向4;如果已經有了,則直接將 a 指向這個地址即可。(為了更好理解各大區分配關係,專門配上了圖示和小案例)聽說理解上,小案例跟圖示更配哦:
public class Test {
	String a = "123456";    //值在常量池中
        String b = "123456";   // a == b true 引用地址相同。因為值在常量池中已存在,所以兩個變數指向的是同一個物件。
	final int A = 0; 		//final常量,堆中
	String[] c=new String[5]; 		// 手動分配new建立物件,在堆中
        static int d =0; 		//靜態區
	public static void main(String args[]) () {//方法棧
	        a = new String("123"); //建立了兩個String物件,一個存於常量池中,一個於堆記憶體中且由a指向。

                b = new String("123");//建立一個物件在堆記憶體中由b指向。因為常量池中已經存在"123"物件。

		String c = "123"; 	//值“123”在常量池堆中已存在。變數
	}
}

例如:String a = "123456";  // 引用物件 a  ,值 123456 在常量池中。為什麼呢?

    因為,基本資料型別存在棧(stack), String的物件例項存在堆(heap)。基礎資料型別在棧裡面直接分配記憶體 ,而引用資料則是通過堆裡的物件來對棧中的內容進行引用。在 java 中 String 是個物件,是引用型別不是基本資料型別,判斷是否相等,不能使用==,而使用equals方法。java中對 String 物件特殊對待,在堆區域給分成了兩塊,一塊是 String constant pool(儲存java字串常量物件),另一塊用於儲存普通物件及字串物件。另外,java虛擬機器處理基礎型別與引用型別的方式是不一樣的,對於基本型別,java虛擬機器會為其分配資料型別實際佔用的記憶體空間;而對於引用型別變數,它僅僅是一個指向堆區中某個例項的指標。

字串在記憶體中的儲存情況如下所示:

String s1 = "abc";    
String s2 = "xyz";    
String s3 = "123";    
String s4 = "A";    
String s5 = newString("abc");     
char[] c = {'J','A','V','A'};     
String s6 = newString(c);      
String s7 = newString(newStringBuffer());  


推薦閱讀:

本人研究不深,若有錯誤請指正。

———————————————————————————結尾———————————————————————————