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。

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