1. 程式人生 > >java基礎知多少(二)

java基礎知多少(二)

Java基礎知識(二)

摘要

通常情況下:瞭解一些看上去很容易,其實這些東西的背後是很複雜的。想深入瞭解並熟悉它們,就必需要認識它們背後的知識。

問題:

要了解Java基礎知識中的一些看上去很難的知識點,像鎖,同步,之類的。想要很容易的瞭解為什麼?需要了解JVM(Java虛擬機器),在JVM中有很多的知識點可以輕鬆的解釋JavaSE中的難點。

先來了解一下JVM,VM是Virtual Machine,這和我們平時說的虛擬機器(這裡指裝載作業系統的虛擬機器)有什麼不同。平時說的VM,是建立在物理硬體上的,而JVM是建立在軟體上的,因此會有不同。

在學習Java的過程中記憶體溢位和記憶體洩露,記憶體溢位。引用以下內容來解釋它們之間的不同之處:

引用

記憶體溢位 out of memory,是指程式在申請記憶體時,沒有足夠的記憶體空間供其使用,出現out of memory;比如申請了一個integer,但給它存了long才能存下的數,那就是記憶體溢位。

記憶體洩露 memory leak,是指程式在申請記憶體後,無法釋放已申請的記憶體空間,一次記憶體洩露危害可以忽略,但記憶體洩露堆積後果很嚴重,無論多少記憶體,遲早會被佔光。

JVM知識引用

在解釋記憶體洩露的基礎原理之前,我們要學習以下JVM的棧(stack),棧是執行緒私有,棧由一系列幀組成(因此Java棧也叫做幀棧),幀儲存一個方法的區域性變數、運算元棧、常量池指標,每一次方法呼叫建立一個幀,並壓棧。函式呼叫組成幀棧,幀棧會自回收自己的資源,一般不會發生記憶體洩露,因此發生記憶體洩露是在堆上。堆(heap)和程式開發密切相關,應用系統物件都儲存在Java堆中,所有執行緒共享Java堆,對分代GC來說,堆也是分代的,GC的主要工作區間。記憶體溢位可以發生在堆,也可能發生在棧上。

這裡是一張關於jvm載入變數的流程圖,從圖片上可以看出主記憶體就是堆(heap),本地記憶體是棧(stack),執行緒將共享變數複製成副本儲存在棧上,這時就出現了讀寫問題。

對於Java中的關鍵字volatile,我們一般知道volatile 不能代替鎖,但是一般認為volatile 比鎖效能好(不絕對)。volatile對變數是執行緒可見的,就是一個執行緒修改了變數,其他執行緒可以立即知道。相當於在本地記憶體A,B中沒有儲存共享變數的副本,而是直接讀寫主存中的變數。可以實現執行緒可見的還有final關鍵字,synchronized,由此可見這些基本和讀寫有關。對於鎖,同步在jvm中主要是由於jvm在對二進位制程式碼進行優化是使用了指令重排導致的問題給出的解決方案:

執行緒內序列語義

  • 寫後讀 a = 1;b = a; 寫一個變數之後,再讀這個位置。
  • 寫後寫 a = 1;a = 2; 寫一個變數之後,再寫這個變數。
  • 讀後寫 a = b;b = 1; 讀一個變數之後,再寫這個變數。

注意:以上語句不可重排

編譯器不考慮多執行緒間的語義可重排: a=1;b=2;上面是關於讀寫重排的邏輯解釋。破壞執行緒間的有序性,因此,Java找到了同步和鎖來解決這個問題。下面是指令重排的基本原則:

指令重排的基本原則

  • 程式順序原則:一個執行緒內保證語義的序列性。
  • volatile規則:volatile變數的寫,先發生於讀。
  • 鎖規則:解鎖(unlock)必然發生在隨後的加鎖(lock)前。
  • 傳遞性:A先於B,B先於C 那麼A必然先於C。
  • 執行緒的start方法先於它的每一個動作。
  • 執行緒的所有操作先於執行緒的終結(Thread.join())。
  • 執行緒的中斷(interrupt())先於被中斷執行緒的程式碼。
  • 物件的建構函式執行結束先於finalize()方法。