【面試題集】JAVA內存模型詳解

在網上搜索JAVA內存模型。會發現網上全是將java的併發內存模型。本篇不承諾是原創,我是大自然的蒐集工。

實際上,Java中應爲不同的目的可以將java劃分爲兩種內存模型:gc內存模型。併發內存模型。

品茗IT-面試題集-首發

如果大家正在尋找一個java的學習環境,或者在開發中遇到困難,可以加入我們的java學習圈,點擊即可加入,共同學習,節約學習時間,減少很多在學習中遇到的難題。

gc內存模型

java在執行java程序的過程中會把它管理的內存劃分若干個不同功能的數據管理區域,如圖所示:

在這裏插入圖片描述

  1. 程序計數器:是一個數據結構,用於保存當前正常執行的程序的內存地址。Java虛擬機的多線程就是通過線程輪流切換並分配處理器時間來實現的,爲了線程切換後能恢復到正確的位置,每條線程都需要一個獨立的程序計數器,互不影響,該區域爲“線程私有”。
  2. Java虛擬機棧:線程私有的,與線程生命週期相同,用於存儲局部變量表,操作棧,方法返回值。局部變量表放着基本數據類型,還有對象的引用。
  3. 本地方法棧:跟虛擬機棧很像,不過它是爲虛擬機使用到的Native方法服務。
  4. Java堆:所有線程共享的一塊內存區域,對象實例幾乎都在這分配內存。
  5. 方法區:各個線程共享的區域,儲存虛擬機加載的類信息,常量,靜態變量,編譯後的代碼。
  6. 運行時常量池:代表運行時每個class文件中的常量表。包括幾種常量:編譯時的數字常量、方法或者域的引用。

幾乎所有的對象/數組的內存空間都在堆上(有少部分在棧上)。在gc管理中,將虛擬機堆分爲永久代、老年代、新生代。通過名字我們可以知道一個對象新建一般在新生代。經過幾輪的gc。還存活的對象會被移到老年代。永久代用來保存類信息、代碼段等幾乎不會變的數據。堆中的所有數據是線程共享的。

  • 新生代:應爲gc具體實現的優化的原因。hotspot又將新生代劃分爲一個eden區和兩個survivor區。每一次新生代gc時候。只用到一個eden區,一個survivor區。新生代一般的gc策略爲mark-copy。
  • 老年代:當新生代中的對象經過若干輪gc後還存活/或survisor在gc內存不夠的時候。會把當前對象移動到老年代。老年代一般gc策略爲mark-compact。
  • 永久代:永久代一般可以不參與gc。應爲其中保存的是一些代碼/常量數據/類信息。JDK 1.8 中已經不存在永久代。

併發內存模型

Java內存模型規定了所有的變量都存儲在主內存中,每條線程還有自己的工作內存,線程的工作內存中保存了該線程中是用到的變量的主內存副本拷貝,線程對變量的所有操作都必須在工作內存中進行,而不能直接讀寫主內存。不同的線程之間也無法直接訪問對方工作內存中的變量,線程間變量的傳遞均需要自己的工作內存和主存之間進行數據同步進行。

在這裏插入圖片描述

java虛擬機中主內存和工作內存交互,就是一個變量如何從主內存傳輸到工作內存中,如何把修改後的變量從工作內存同步回主內存:

  • lock(鎖定):作用於主內存,它把一個變量標記爲一條線程獨佔狀態;
  • unlock(解鎖):作用於主內存,它將一個處於鎖定狀態的變量釋放出來,釋放後的變量才能夠被其他線程鎖定;
  • read(讀取):作用於主內存,它把變量值從主內存傳送到線程的工作內存中,以便隨後的load動作使用;
  • load(載入):作用於工作內存,它把read操作的值放入工作內存中的變量副本中;
  • use(使用):作用於工作內存,它把工作內存中的值傳遞給執行引擎,每當虛擬機遇到一個需要使用這個變量的指令時候,將會執行這個動作;
  • assign(賦值):作用於工作內存,它把從執行引擎獲取的值賦值給工作內存中的變量,每當虛擬機遇到一個給變量賦值的指令時候,執行該操作;
  • store(存儲):作用於工作內存,它把工作內存中的一個變量傳送給主內存中,以備隨後的write操作使用;
  • write(寫入):作用於主內存,它把store傳送值放到主內存中的變量中。

Java內存模型還規定了執行上述8種基本操作時必須滿足如下規則:

  • 不允許read和load、store和write操作之一單獨出現,以上兩個操作必須按順序執行,但沒有保證必須連續執行,也就是說,read與load之間、store與write之間是可插入其他指令的。
  • 不允許一個線程丟棄它的最近的assign操作,即變量在工作內存中改變了之後必須把該變化同步回主內存。
  • 不允許一個線程無原因地(沒有發生過任何assign操作)把數據從線程的工作內存同步回主內存中。
  • 一個新的變量只能從主內存中“誕生”,不允許在工作內存中直接使用一個未被初始化(load或assign)的變量,換句話說就是對一個變量實施use和store操作之前,必須先執行過了assign和load操作。
  • 一個變量在同一個時刻只允許一條線程對其執行lock操作,但lock操作可以被同一個條線程重複執行多次,多次執行lock後,只有執行相同次數的unlock操作,變量纔會被解鎖。
  • 如果對一個變量執行lock操作,將會清空工作內存中此變量的值,在執行引擎使用這個變量前,需要重新執行load或assign操作初始化變量的值。
  • 如果一個變量實現沒有被lock操作鎖定,則不允許對它執行unlock操作,也不允許去unlock一個被其他線程鎖定的變量。
  • 對一個變量執行unlock操作之前,必須先把此變量同步回主內存(執行store和write操作)。

面試中一般會問gc內存模型,而併發內存模型太高端了,與我們jvm調優無關。

JVM調優需要了解gc的內存模型,能夠看的懂jstack等堆棧分析工具的分析結果。並針對性設置java允許參數。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章