示例代码
1 | public class Person { |
初始化:
开始创建对象,调用构造器,给属性赋值:
局部变量放置在栈中
-
首先会在栈中开辟一个独立的栈帧(Person构造器栈帧),分别有a、b、c ,分别进行赋值,其中a、b属于基本数据类型,基本数据类型是值传递,即直接把值付给变量,对于引用数据类型(c),会在方法区中有一个字符串常量值,会将“清华大学”放在字符串常量池中(放入后对应的地址比如是0X18)
将“清华大学”赋值给c的时候,实际上是把地址0x18指向c
-
开始给成员变量赋值
1
2
3id = a;
age = b;
school = c;此时堆中的id=1;age=20,school即0x18地址。
到此为止,构造器执行结束,整个Person构造器栈帧就消失了 -
构造对象结束,赋值给p,main栈帧中变量p,指向的地址即0x93
总结
- 栈空间(stack),连续的存储空间,遵循后进先出的原则,用于存放局部变量。包括:1.用来保存基本数据类型的值;2.保存类的实例,即堆区对象的引用(指针)。也可以用来保存加载方法时的帧。
- 堆空间(heap),不连续的空间,用于存放new出的对象,或者说是类的实例。
注意: 创建出来的对象只包含属于各自的成员变量,并不包括成员方法。因为同一个类的对象拥有各自的成员变量,存储在各自的堆中,但是他们共享该类的方法,并不是每创建一个对象就把成员方法复制一次。 - 方法区(method),方法区在堆空间内,用于存放①类的代码信息;②静态变量和方法;③常量池(字符串常量等,具有共享机制)。
注意:
- 分清什么是实例什么是对象。Class a= new Class();此时a叫实例,而不能说a是对象。实例在栈中,对象在堆中,操作实例实际上是通过实例的指针间接操作对象。多个实例可以指向同一个对象。
- 栈中的数据和堆中的数据销毁并不是同步的。方法一旦结束,栈中的局部变量立即销毁,但是堆中对象不一定销毁。因为可能有其他变量也指向了这个对象,直到栈中没有变量指向堆中的对象时,它才销毁,而且还不是马上销毁,要等垃圾回收扫描时才可以被销毁。
- 类的成员变量在不同对象中各不相同,都有自己的存储空间(成员变量在堆中的对象中)。而类的方法却是该类的所有对象共享的,只有一套,对象使用方法的时候方法才被压入栈,方法不使用则不占用内存。
- 以上的栈、堆、代码段、数据段等等都是相对于应用程序而言的。每一个应用程序都对应唯一的一个JVM实例,每一个JVM实例都有自己的内存区域,互不影响。并且这些内存区域是所有线程共享的。这里提到的栈和堆都是整体上的概念,这些堆栈还可以细分。
- 基本类型和基本类型的包装类。基本类型有:byte、short、char、int、long、boolean。基本类型的包装类分别是:Byte、Short、Character、Integer、Long、Boolean。注意区分大小写。二者的区别是:基本类型体现在程序中是普通变量,基本类型的包装类是类,体现在程序中是引用变量。因此二者在内存中的存储位置不同: 基本类型存储在栈中,而基本类型包装类存储在堆中 。上边提到的这些包装类都实现了常量池技术,另外两种浮点数类型的包装类则没有实现。另外,String类型也实现了常量池技术。
- 以上提到的几种基本类型包装类均实现了常量池技术,但他们维护的常量仅仅是【-128至127】这个范围内的常量,如果常量值超过这个范围,就会从堆中创建对象,不再从常量池中取。比如,把上边例子改成Integer i1 = 400; Integer i2 = 400;,很明显超过了127,无法从常量池获取常量,就要从堆中new新的Integer对象,这时i1和i2就不相等了。
参考
https://blog.csdn.net/shimiso/article/details/8595564
java内存分配研究:http://www.blogjava.net/Jack2007/archive/2008/05/21/202018.html
Java常量池详解之一道比较蛋疼的面试题:http://www.cnblogs.com/DreamSea/archive/2011/11/20/2256396.html
jvm常量池:http://www.cnblogs.com/wenfeng762/archive/2011/08/14/2137820.html
深入Java核心 Java内存分配原理精讲:http://developer.51cto.com/art/201009/225071.htm
如果您喜欢此博客或发现它对您有用,则欢迎对此发表评论。 也欢迎您共享此博客,以便更多人可以参与。 如果博客中使用的图像侵犯了您的版权,请与作者联系以将其删除。 谢谢 !