Java面試相關——堆和棧-簡書

引言:Java 中的堆和棧 Java把內存劃分成兩種:一種是棧內存,一種是堆內存。至於「方法區」(靜態存儲區),可以理解為:主要存放靜態數據、全局 static 數據和常量。這塊內存在程序編譯時就已經分配好,並且在程序整個運行期間都存在。總的來說:堆和棧針對非靜態數據,而方法區針對靜態數據。

一、堆內存和棧內存

棧(stack)與堆(heap)都是Java用來在Ram中存放數據的地方。與C++不同,Java自動管理棧和堆,程序員不能直接地設置棧或堆。

·棧:

o簡單理解:堆棧(stack)是操作系統在建立某個進程或者線程(在支持多線程的操作系統中是線程)為這個線程建立的存儲區域,該區域具有先進後出的特性。

Advertisements

o特點:存取速度比堆要快,僅次於直接位於CPU中的寄存器。棧中的數據可以共享(意思是:棧中的數據可以被多個變數共同引用)。

o缺點:存在棧中的數據大小與生存期必須是確定的,缺乏靈活性。

o相關存放對象:①一些基本類型的變數(,int, short, long, byte, float, double, boolean, char)和對象句柄【例如:在函數中定義的一些基本類型的變數和對象的引用變數】。②方法的形參 直接在棧空間分配,當方法調用完成後從棧空間回收。

o特殊:①方法的引用參數,在棧空間分配一個地址空間,並指向堆空間的對象區,當方法調用完成後從棧空間回收。②局部變數new出來之後,在棧控制項和堆空間中分配空間,當局部變數生命周期結束后,它的棧空間立刻被回收,它的堆空間等待GC回收。

Advertisements

·堆:

o簡單理解:每個Java應用都唯一對應一個JVM實例,每一個JVM實例唯一對應一個堆。應用程序在運行中所創建的所有類實例或者數組都放在這個堆中,並由應用所有的線程共享。Java中分配堆內存是自動初始化的,Java中所有對象的存儲控制項都是在堆中分配的,但這些對象的引用則是在棧中分配,也就是一般在建立一個對象時,堆和棧都會分配內存。

o特點:可以動態地分配內存大小、比較靈活,生存期也不必事先告訴編譯器,Java的垃圾收集器會自動收走這些不再使用的數據。在堆中分配的內存,由Java虛擬機的自動垃圾回收器來管理。

o缺點:由於要在運行時動態分配內存,存取速度較慢。

o主要存放:①由new創建的對象和數組 ;②this

o特殊:引用數據類型(需要用new來創建),既在棧控制項分配一個地址空間,又在堆空間分配對象的類變數。

補充:在堆中產生了一個數組或對象后,還可以在棧中定義一個特殊的變數,讓棧中這個變數的取值等於數組或對象在堆內存中的首地址,棧中的這個變數就成了數組或對象的引用變數。

引用變數就相當於是為數組或對象起的一個名稱,以後就可以在程序中使用棧中的引用變數來訪問堆中的數組或對象。

引用變數是普通變數,定義時在棧中分配內存,引用變數在程序運行到作用域外釋放。而數組&對象本身在堆中分配,即使程序運行到使用new產生數組和對象的語句所在地代碼塊之外,數組和對象本身佔用的堆內存也不會被釋放,<u>數組和對象在沒有引用變數指向它的時候,才變成垃圾,不能再被使用,但是仍然占著內存,在隨後的一個不確定的時間被垃圾回收器釋放掉。這個也是java比較占內存的主要原因</u>。

這裡可以理解為:

String s1 = new String("abc");

這裡面:"abc"表示棧中的一個存儲空間中的一個數據,new String("abc")表示存在於堆中的一個對象,這個對象的值為『abc』String s1則表示棧中定義的一個取了new String("abc")在堆中的首地址的一個特殊變數,也就是:s1成了引用變數,相當於一個別名。

二、Java數據存儲和JVM內存分區

·<u>JAVA中,有六個不同的地方可以存儲數據:</u>

1.寄存器(register。這是最快的存儲區,因為它位於不同於其他存儲區的地方——處理器內部。但是寄存器的數量極其有限,所以寄存器由編譯器根據需求進行分配。你不能直接控制,也不能在程序中感覺到寄存器存在的任何跡象。

2.堆棧(stack。位於通用RAM中,但通過它的「堆棧指針」可以從處理器哪裡獲得支持。堆棧指針若向下移動,則分配新的內存;若向上移動,則釋放那些內存。這是一種快速有效的分配存儲方法,僅次於寄存器。創建程序時候,JAVA編譯器必須知道存儲在堆棧內所有數據的確切大小和生命周期,因為它必須生成相應的代碼,以便上下移動堆棧指針。這一約束限制了程序的靈活性,所以雖然某些Java數據存儲在堆棧中——特別是對象引用,但是JAVA對象不存儲其中。

3.堆(heap。一種通用性的內存池(也存在於RAM中),用於存放所以的JAVA對象。堆不同於堆棧的好處是:編譯器不需要知道要從堆里分配多少存儲區域,也不必知道存儲的數據在堆里存活多長時間。因此,在堆里分配存儲有很大的靈活性。當你需要創建一個對象的時候

,只需要new寫一行簡單的代碼,當執行這行代碼時,會自動在堆里進行存儲分配。當然,為這種靈活性必須要付出相應的代碼。用堆進行存儲分配比用堆棧進行存儲存儲需要更多的時間。

4.靜態存儲(static storage。這裡的「靜態」是指「在固定的位置」。靜態存儲里存放程序運行時一直存在的數據。你可用關鍵字static來標識一個對象的特定元素是靜態的,但JAVA對象本身從來不會存放在靜態存儲空間里。

5.常量存儲(constant storage。常量值通常直接存放在程序代碼內部,這樣做是安全的,因為它們永遠不會被改變。有時,在嵌入式系統中,常量本身會和其他部分分割離開,所以在這種情況下,可以選擇將其放在ROM中 。

6.RAM存儲。如果數據完全存活於程序之外,那麼它可以不受程序的任何控制,在程序沒有運行時也可以存在。

就速度來說,有如下關係:

寄存器 < 堆棧 < 堆 < 其他

·<u>JVM的內存分區:</u>

JVM的分區可分為三個:堆(heap)、棧(stack)和方法區(method)

1.堆區:

o存儲的全是對象,每個對象都包含一個與之對應的class信息(我們常說的類類型,Clazz.getClass()等方式獲取),class目的是得到操作指令。

oJVM只有一個堆區(heap)被所有線程共享,堆中不存放基本類型和對象引用,只存放對象本身。【這裡的『對象』,就不包括基本數據類型】

4.棧區:

o每個線程包含自己的一個棧區,棧中只保存基本數據類型的對象和自定義對象的引用。

o每個棧中的數據(基本類型和對象引用)都是私有的,其他棧不可訪問。

o棧 = 基本類型變數區 + 執行環境上下文 + 操作指令區(存放操作指令)

8.方法區【這個可能比較陌生】

o又稱為『靜態區』,和堆一樣,被所有的線程共享。

o方法區包含所有的class和static變數。

補充:大家也許聽說過「數據區」或者「運行時數據區」這個名詞,這裡,我們說JVM是驅動Java程序運行的基礎,而它有三個分區:堆、棧、方法區,實際上,JVM的三個方法區就是包含於 JVM的運行時數據區中的三大塊。於是,「數據區」與上述的分區的關係就明朗了。

三、Java的兩種數據類型:

1.基本類型(primitive types), 共有8種,即int, short, long, byte, float, double, boolean, char(注意,

並沒有string的基本類型)。這種類型的定義是通過諸如int a = 3; long b = 255L;的形式來定義的,稱為自動變數。【自動變數存的是字面值,不是類的實例(即不是類的引用),這裡並沒有類的存在,如int a=3;
這裡a只是指向int類型(不是類)的引用,指向字面值3,此時,由於這些字面值的數據大小可知並且生存期可知(他們在程序內某個固定代碼塊中,代碼塊退出,他們就消失),為了追求速度,於是存在中】

2.包裝類,如Integer, String, Double等將相應的基本數據類型包裝起來的類。這些類數據全部存在於中,Java用new()語句來顯式地告訴編譯器,在運行時才根據需要動態創建,因此比較靈活,但缺點是要佔用更多的時間。

視頻資料鏈接:

data:text/html;charset=UTF-8;base64,

5oGt5Zac5L2g77yM5p625p6E5biI5a2m5Lmg576k5Y+35pivNTc1NzUxODU0Cg==

複製粘貼在網站打開即可

Advertisements

你可能會喜歡