天天看點

java中堆、棧知識點總結

1.圖示

java中堆、棧知識點總結

2.圖示解析

1.方法區和堆是所有線程可共享的區域(圖示綠色)

2.本地方法棧、虛拟機棧、程式計數器是由各個線程隔離的資料區域,并不是共享的(圖示黃色)

3.各區域作用詳解:

  1. 程式計數器:目前線程執行的位元組碼指令,是線程私有的。
  2. 虛拟機棧:存放的是java執行方法的記憶體模型,每個方法被執行的時候,都會去建立一個幀棧,把幀棧壓入棧,當方法執行完或抛出未捕獲的異常時,幀棧就會出棧。
  3. 本地方法棧:調用本地native的記憶體模型,線程獨享。
  4. 方法區:(1.8之前沒有方法區,之後才有方法區)用于存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯後的代碼等資料,是線程共享的。方法區中有運作時常量池(運作時常量池是方法區的一部分,Class檔案中除了存有類的版本、字段、方法、接口等描述資訊,還有一項是常量池,存放編譯期生成的各種字面量和符号引用,這部分内容在類加載後,存放到方法區的運作時常量池中)。
  5. 堆:java對象存儲的地方,是虛拟機管理的記憶體中最大的一塊,是所有線程共享的區域。堆在虛拟機啟動時建立,次記憶體區域的唯一目的就是存放對象執行個體,幾乎所有對象的執行個體都在這裡配置設定記憶體,存放new生成的對象和數組。java堆是垃圾收集器管理的記憶體區域,是以很多時候被稱為GC堆。

3.堆和棧的對比

  1. 棧解決的是程式的運作問題,即程式如何執行或如何處理資料;堆解決的是資料存儲的問題,即資料怎麼放、放在哪兒。
  2. 棧因為是運作機關,是以裡面存儲的資訊都是跟目前的線程相關的資訊,包括局部變量、程式的運作狀态、方法傳回值等等。而堆隻負責存儲對象的資訊。
  3. 在方法中定義的一些基本類型的變量和對象的引用變量都是在函數的棧記憶體中配置設定;堆記憶體用于存放由new建立的對象和數組。
  4. 在java中,一個線程就會有一個對應的線程棧與之對應,這點保證了程式的并發運作。而堆則是所有線程所共享的,也可以了解為多個線程通路同一個對象,比如多線程去讀寫同一個對象的值。

4.棧記憶體溢出問題

  1. StackOverflowError:線程請求的棧深度大于虛拟機所允許的深度(例如存在遞歸調用或循環依賴調用)或建立的線程過多。
  2. OutOfMemoryError:如果虛拟機棧可以動态擴充,而擴充時無法申請到足夠的記憶體,堆記憶體溢出是OutOfMemoryError。如果堆中沒有記憶體完成執行個體配置設定,并且堆也無法再擴充至時,抛出OutOfMemoryError。

5.java堆溢出問題

java堆用于存儲對象執行個體,隻要不斷地建立對象,當對象數量達到最大堆的容量限制後就會産生記憶體溢出的異常。最常見的記憶體溢出就是存在大的容器,而沒法回收。比如Map、list等。

  1. 記憶體溢出:記憶體空間不足導緻新對象還無法配置設定到足夠的記憶體。
  2. 記憶體洩漏:應該釋放後的對象沒有被釋放,多見于自己使用容器儲存元素的情況下。

解決方法:

首先要找出最大的對象,判斷最大對象的存在是否合理。如果合理就需要調整jvm記憶體大小。如果不合理,那麼這個對象的存在,就是最有可能引起記憶體溢出的根源。通過GC Roots的引用鍊資訊,就可以比較準确地定位出洩漏代碼的位置。

6.垃圾回收算法

分代收集算法(目前大部分JVM的垃圾收集器所采用的的算法)

思路:把堆分為新生代和老年代。(永久代指的是方法區)

  1. 新生代:由于新生代中每次垃圾回收都要回收大部分對象,是以使用的垃圾回收算法是Copying算法(複制清除算法)。新生代裡面分成一份較大的Eden空間和兩份較小的Survivor空間。每次隻使用Eden和一塊Survivor空間,然後垃圾回收的時候,把存活對象放到未使用的Survivor空間中,清空Eden和剛使用過的Survivor空間。
  2. 老年代:由于老年代每次隻回收少量的對象,是以采用的是Mark-compact算法(标記-整理算法)。先标記存活對象,然後把存活對象向一邊移動,然後清理掉端邊界以外的記憶體,不容易産生記憶體碎片。
  3. 永久代:在堆區外(方法區)有一個永久代。對永久代的回收主要是無效的類和常量。