JVM之Java程序與內存映射

      首先看下一個Java程序是如何在機器上執行的:

      Java源程序(.java文件)-->Java編譯器(如Eclipse)-->字節碼(.class文件)-->JVM編譯器-->裝配 -->機器碼--> 經過系統總線-->微處理器-->邏輯門-->電路-->設備硬件.

JVM的內存模型

 Java內存模型指明瞭JVM如何在計算機內存(RAM)中工作的。
 JVM內部的Java內存模型被分爲線程棧和堆兩塊。如下圖所示就是Java內存模型的邏輯圖:


 JVM中每個線程都運行在它自己的線程棧中,線程棧中包含了當前線程正在執行的方法(所有的方法都在棧中被調用執行)。線程棧也包含了所有正在執行的方法的局部變量。一個線程創建的局部變量對其他線程都是不可見的。即使當兩個線程都在執行同一段代碼(局部變量),兩個線程都會在他們自己的線程棧中創建局部變量。因此,每一個線程都有它自己的方法局部變量控制權。注意,只有局部變量是每個線程互相獨立的,全局變量的生命週期是屬於對象的,所以全局變量是所有線程共享的。
  堆內存包含了所有程序中創建的對象,不管是哪個線程創建都保存在堆內存,他們是所有線程共享的。不管對象是是否被傳遞給局部變量,還是作爲其它對象的變量所創建,對象都是存儲在堆上。總之,任何情況下,對象都保存在堆內存。如下圖就是線程棧的局部變量存儲和堆內存上對象的存儲邏輯圖:


 簡單的局部變量類型(八大基本類型)是完全存儲在線程棧中的。
 但是一個局部的引用型變量,這個引用本身是存儲在線程棧中,但是其引用的對象仍然在堆內存(因爲引用型變量其值就是一個對象地址,所有的線程都有一份此地址,均指向同一個對象)。
 一個對象可以包含一些方法同時,方法裏可能包含有一些局部變量,這些局部變量都存儲在線程棧中,儘管對象的方法是被存儲在堆。一個對象的成員變量是隨着對象本身被存儲在堆內存上的,不管此成員變量是基本類型還是引用類型。
 static變量也被存儲在堆內存。
   堆內存上的所有對象都可以被任意的線程使用,當一個線程能夠使用一個對象時,它也可以使用對象的成員變量。如果兩個線程在同一時刻調用用同一個對象的方法,那麼這兩個線程都可以去使用這個對象的成員變量,但是每一個線程都有它自己的局部變量副本。
   以下是邏輯圖:

    兩個線程都有一系列的局部變量,其中有一個叫做Local Variable2的局部變量指向堆上共同的對象Object3.。兩個線程都分別有一個不同引用指向同一個對象,
這是什麼意思呢,意思就是Local variable2是一個引用類型變量,它是被保存在兩個線程各自的棧上面的,但是這兩個引用類型的引用(對象地址)都是指向了Object3
這個對象。
   注意,對象Object3引用了Object2和Object4作爲其成員變量。通過Object3的成員引用變量,兩個線程就都可以訪問到對象Object2和Object4.。上面的圖也展示了一個
局部變量指向堆上兩個不同的對象,也就是兩個局部變量Local variable1一個指向了Object1,另一個指向了Object5.這種情況是如何產生的呢?理論上說,兩個線程都可以
使用Object1和Object5(因爲他們有相同的局部變量Local variable1),但是什麼情況下會出現同一個局部引用變量會指向不同的對象呢?這種情況的出現其實是很簡單的
一種操作,那就是在方法裏直接new了對象了,但是對於每一個線程來說其引用變量在每個線程棧上都有一個副本,但是每個指向的對象又是不一樣的!下面我們就用代碼來展示
上面的邏輯圖!
	public class MyRunnable implements Runnable {
		public void run() {
			methodOne();
		}

		public void methodOne() {
			int localVariable1 = 45;
			MySharedObject localVariable2 = MySharedObject.sharedInstance;
			// ... do more with local variables.
			methodTwo();
		}

		public void methodTwo() {
			Integer localVariable1 = new Integer(99);
		}
	}

	public static class MySharedObject {
		// static variable pointing to instance of MySharedObject
		public static final MySharedObject sharedInstance = new MySharedObject();
		// member variables pointing to two objects on the heap
		public Integer object2 = new Integer(22);
		public Integer object4 = new Integer(44);

		public long member1 = 12345;
		public long member2 = 67890;
	}



  如果兩個線程都執行run()方法,那麼首先搶到CPU的線程將會執行,run()方法調用了methodOne(),methodOne()調用了methodTwo();
methoOne()聲明瞭一個基本類型的局部變量(localVariable1)和一個引用類型的局部變量localVariable2.
  每一個線程在執行methodOne()的時候都會在它自己的線程棧中創建它localVariable1和localVariable2副本。變量localVariable1將會從每一個線程中完全的隔離開來,
它只存在線程的棧區空間(這裏僅僅是指這個localVariable1這個引用變量的值,而不是對象本身)。一個線程不能看到另一個線程的localVariable1發生了什麼變化。
  每一個線程在執行methodOne()時也會創建他們的localVariable2副本,但是兩個不同localVariable2副本都是指向同一個堆內存上的對象
  總結的來說:每個線程的線程棧上都存儲有局部變量的副本,對於基本類型的局部變量來說每個線程的的值都互不影響(因爲他m們各自的變量存儲的值就在其自己的棧中),
但是對於引用類型的局部變量來說,雖然各線程仍然有局部變量的引用,但是這些引用的值卻有可能指向同一個對象sharedInstance,由於此對象是static類型的,其時屬於整
個MySharedObject 類的,他在第一次被創建時第二次發現仍然存在並不會再次被創建,而是直接返回上次創建的那個sharedInstance。

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