jvm学习(1)

        jvm内存模型是javaer的必经之路,也算是面试时候必问的问题(至少博主去年面试的几家公司都问到这个了)。

        jvm内存模型其实挺乱的,这几天来缕一缕这个,慢慢得一点一点来吧,争取花几天时间把内存模型方面的东西缕清楚。

       首先看这个代码:

Object o = new Object();

       相信大部分人都知道,这行代码在jvm的堆内存上开辟了一块空间(内存块),存储一个新的object对象。而这个对象的引用,则在栈上。

 

            jvm学习(1)_第1张图片

      首先看深入理解jvm上的这张图,虚拟机栈和堆就是我们平时所说的堆和栈。上面的那行代码是在堆上创建一个新的object对象,然后在栈上创建一个引用,指向新创建的对象o。

    
jvm学习(1)_第2张图片
      而这时候,如果执行代码:b = o,那么结果就是在栈上新增一个对象的引用,指向o指向的在堆里面的对象Object()。

     看到这,就很容易java里面的传参问题了。java的方法传参传递的都是对象的引用,对于可变对象来说(非基础类型对象和非String或其他的不可变对象),只要在方法里不重新进行引用赋值,那么操作的就是传递之前的对象。比如以下代码:

public static void main(String[] args) {
		// TODO Auto-generated method stub
		List l = new ArrayList();
		l.add("test");
		test1(l);
		System.out.println(l);
		List l1 = new ArrayList();
		l1.add("test");
		test2(l1);
		System.out.println(l1);
		List l2 = new ArrayList();
		l2.add("test");
		test3(l2);
		System.out.println(l2);
	}
	
	/**  
	* test1: 测试传递传参,在参数基础上进行add操作
	* @param l 
	* void  返回类型   
	*/
	public static void test1(List l){
		l.add("test1");
	}
	
	/**  
	* test1: 测试传递传参,在参数基础上进行重新赋值操作
	* @param l 
	* void  返回类型   
	*/
	public static void test2(List l){
		l = new ArrayList<String>();
		l.add("test2");
	}
	
	/**  
	* test1: 测试传递传参,在参数基础上进行赋null操作
	* @param l 
	* void  返回类型   
	*/
	public static void test3(List l){
		l = null;
	}

 结果:

[test, test1]
[test]
[test]

       因为方法test1并没有改变传参引用的对象地址,只是在这个地址上进行add操作,所以最终结果会反映到传参之前的list,而方法2和3进行了重新赋值操作,相当于上图中的b,箭头改变了,指向了其他位置,那么原来的引用o对指向的对象当然不会改变了。

 

      还是看深入jvm的那张图,其中堆是jvm中最大的一块内存,所有new出来的对象都会在堆中开辟内存空间进行保存。

       其他的分区包括:

       程序计数器:一块较小的区域,可以看作是当前线程所执行的字节码的行号指示器。这是java程序员最不用关心的区域。

       本地方法栈:和虚拟机栈类似,不过本地方法栈是虚拟机使用到的native方法服务。如果看过源码的人,应该可以看到java源码里用到很多本地方法栈,比如拷贝方法System.arraycopy。

       方法区:除了栈和堆之外,第三重要的内存区域。和堆一样,所有线程共享,存储类信息,类的常量,静态变量等数据。他是堆内存区域的一个逻辑部分。(HotSpot虚拟机把它划分到了永久代部分,尽量少用static变量也是因为这个原因,因为永久代的垃圾回收是几乎不发生的,所以static变量多了,或者类太多了,就容易发生out of memory error)

       运行时常量池:方法区的一部分,深入jvm中对他的解释是:存放编译期生成的各种字面量和符号引用。除了包含每个类和接口的常量,它也包含了所有方法和变量的引用。简而言之,当一个方法或者变量被引用时,JVM通过运行时常量区来查找方法或者变量在内存里的实际地址。

       以下这段话引自(http://chenzehe.iteye.com/blog/1727062),觉得说的还是比较容易理解的。

       在Class文件结构中,最头的4个字节用于存储魔数Magic Number,用于确定一个文件是否能被JVM接受,再接着4个字节用于存储版本号,前2个字节存储次版本号,后2个存储主版本号,再接着是用于存放常量的常量池,由于常量的数量是不固定的,所以常量池的入口放置一个U2类型的数据(constant_pool_count)存储常量池容量计数值。常量池主要用于存放两大类常量:字面量(Literal)和符号引用量(Symbolic References),字面量相当于Java语言层面常量的概念,如文本字符串,声明为final的常量值等,符号引用则属于编译原理方面的概念,包括了如下三种类型的常量:

       类和接口的全限定名

       字段名称和描述符

       方法名称和描述符

       

       

你可能感兴趣的:(jvm)