java对象创建与内存模型总结
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java对象创建与内存模型总结相关的知识,希望对你有一定的参考价值。
1、JVM管辖的内存大致分为三个逻辑部分:java栈(Heap)、java堆(JavaStack)和方法区(MethodArea)。在JVM启动时创建,关闭时全部回收。
栈、本地方法栈、程序计数器:以线程为粒度,每个线程拥有自己的部分。而堆和方法区被所有线程共享。
堆:运行时的数据区域,程序(线程)运行时动态分配。
方法区:静态存储区,存储被装载到JVM中的class信息。
2、栈
i)、存放基本类型的变量数据和对象的引用变量(变量本身存放在堆和常量池中)。
ii)、java系统必须知道存储在栈内所有项的确切生命周期(故变量的垃圾回收由系统处理),以便上下移动栈指针,相比堆更高效,但限制了程序的灵活性。
iii)、可以数据共享(共享的是值,改变一个变量不会影响另一个变量)
int c = 1;int d =1;//c,d共享栈数据1
d = 2;//如果栈中没有2这个值(如果有就直接引用共享),新建值并将d重新指向它。
3、堆
i)、存放由new操作符创建的对象和数组(‘可以但不必须’将其引用存放在栈中,方便快速访问)。
ii)、运行时动态分配内存(速度慢于栈的主要原因),并由gc管理。
iii)、堆中的垃圾回收由gc处理(用户不能干预,即便调用System.gc()也只是‘希望执行’垃圾回收。因为gc是为所有java应用进程服务且gc的执行会影响所有java应用进程,不特殊对待某个进程)。
4、方法区
i)、位于堆上的静态存储区,存储被装载到JVM中的class信息。
ii)、常量池: 每个常量池从方法区分配,存放类、方法、接口等中的常量和字符串常量(常量:public static final)。
每个常量池在编译期确定下来,并保存到.class文件中。
可以数据共享。
5、特殊的String对象
String a1 = "123"; //这一句创建了1个字符串常量对象(常量池中)。
String a2 = "123"; //这两句还是1个对象。a2也指向a1的字符串常量对象“123”。
String b1 = new String("123"); //这一句本身应该创建两个对象(先创建常量对象“123”,再用它做参数用new在堆中创建一个新对象)。但因为字符串常量对象“123”已存在(常量池),所以此时只创建1个对象(堆中)
String b2 = new String("123"); //此时因为“123”已存在常量池中,故只用new操作符新建一个对象。
System.out.println(a1 == a2); //运算符 == 比较引用地址
System.out.println(b1 == b2);
对于 "123" --在常量池中创建字符串常量对象“123”(如果常量池中已有该对象就直接引用而不再创建)。共一个对象,位于常量池中。
对于 new String("123") --先在常量池中创建字符串常量对象“123”,再在堆中用new操作符创建新对象(new操作符每次都新建不同内存地址的对象)。共两个对象,分别存储于常量池、堆中。
6、垃圾回收
i)、处理位于java逻辑堆上的垃圾对象。
ii)、采用基于“标记-清除”、“停止-复制”算法的“自适应”的垃圾回收技术。进行垃圾回收时会暂停程序的运行。
iii)、当对象失去所有引用成为垃圾后,可能不被垃圾回收。
iiii)、无论“垃圾回收”还是“终结”,都不保证能发生,如果JVM并未面临内存耗尽的情形,它是不会浪费时间去执行垃圾回收以回复内存。
iiiii)、finalize()正确用法是,在对象即将被回收时,用来做一些善后处理。
iiiiii)、System.gc()用于强制进行垃圾回收动作。但并不能保证垃圾回收动作的执行,顶多增加垃圾回收动作发生的可能性。垃圾回收动作不受用户干预,因为gc服务于JVM中的所有进程,不为某个进程特殊处理。
int[] ary = {1,2,3}; //当ary超过作用域后,变量ary将立即在栈中被系统回收并可另作他用。它所指向的数组对象被gc‘标记-清除’,但对象依然存在堆中,等到一个(不确定)时间被gc回收。
6、对象创建的总结
java栈用来存放基本类型的变量数据和对象的引用变量,但不存放对象内容;
java堆存放使用new关键字创建的对象和数组对象;
特殊情况是字符串这个包装类:其引用是存放在栈里的,而对象内容位置由创建方式来决定(常量池、堆),用“”在编译期创建的存放在字符串常量池中,用new操作符在运行时创建的存放在堆中。
以上是关于java对象创建与内存模型总结的主要内容,如果未能解决你的问题,请参考以下文章