Java中的栈、堆和方法区

一、栈内存:
栈又被称作堆栈,栈帧入栈为压栈,出栈为弹栈,遵循后进先出的原则。与之对应,队列遵循先进先出的原则。
1、 存放数据类型:
栈内存主要用于存放基本数据类型和对象的引用。而对象的数据访问,则是通过栈内存的指针,指向对应元素在堆内存中的位置实现的。
2、 线程机制:
栈内存不被线程共享。
3、 存取特征:
一级缓存,用过即回收。栈内存中的数据的大小和生存时间是固定的,因此存取速度快,仅次于位于CPU中的寄存器;但同时缺乏灵活性。
4、 销毁机制:
栈中没有垃圾回收机制,只有进栈和弹栈,进栈则分配内存,弹栈则释放内存。用完即丢。
5、每个方法执行时,该方法会建立自身的内存栈,也就是栈帧,以便于将该方法内定义的变量——局部变量加入到栈帧中,方法结束时,栈帧销毁,该方法内的局部变量也随之销毁。

二、堆内存:
1、 存放数据类型:
堆内存主要用于存放对象的实例。对象被创建(new)后,其实例存放于堆内存,其内的成员变量同时在堆内存分配空间,同时其内部出现方法的标记,而方法本体则指向方法区中。
2、 线程机制:
堆内存被所有线程共享。
3、 存取特征:
二级缓存,用过也需等待垃圾回收机制。堆内存中的数据大小和生存时间不必确定,空间不连续,因此比较灵活;但因为运行时要动态分配内存,所以存取速度较慢。堆内存如果没有空间分配,则会报出OutOfMemoryError异常。
4、 销毁机制:
对象实例本身存放于堆内存,并不会随方法结束而销毁,且可以被其他变量引用,只有当任何变量均不引用该对象时,垃圾回收机制才会在合适的时间点回收。

三、方法区:
1、存放数据类型:
方法区主要用于存放对象实例的类信息(对象的类型、父类、实现接口和方法本体等)、常量(常量池,内部含有字符串常量池)、静态(静态区,包含静态变量和静态方法)。
2、 线程机制:
方法区被所有线程共享。
3、 存取特征:
方法区中的数据大小和生存时间不必确定,空间不连续,因此比较灵活;但存取速度较慢。
4、 销毁机制:
方法区内同样存在垃圾回收机制。

由以上说明,得出结论:
1、类变量、成员变量和局部变量的生命周期
类变量:类信息,加载于方法区。随着程序运行而创建,随着程序结束而销毁。
成员变量:对象实例信息,加载于堆内存。随着对象的创建而创建,随着对象被JVM垃圾回收机制回收而销毁。
局部变量:方法内信息,加载于栈内存。随着方法调用而创建,随着方法调用的结束而销毁。

2、 对象的普通方法的调用过程
程序运行开始,类中的方法本体等类信息、静态和常量存储于方法区。
当类被创建出实例对象后,方法的标记、成员变量随着实例对象存储于堆内存。
当对象调用该方法时,该方法从栈内存中通过堆内存,最终指向方法区本体,然后在栈内存中快速创建,方法以栈帧的形式进栈,内部的局部变量开始被加载。

3、 静态的调用过程
静态调用时,判断为static,则直接指向方法区中的类的本身属性,接着,从方法区的静态区中取出结果。因此,静态方法和静态变量的访问过程,全程和对象无关。

4、字符串常量池对性能的提升:
(1)编译期便将字符串存放于常量池中。
(2)同样的字符串只占用一份,防止多次创建而消耗内存空间。
举例:
String a = “abc”;
String b = “abc”;
String c = new String(“abc”);
String d = new String(“abc”);
/*
等号比较的是地址值,equals方法比较的是实际值。
*/
System.out.println(a == b);
//因为字符串常量池的限定,为节省内存空间,a创建完"abc"后,会被存入字符串常量池;到了b创建"abc"时,发现字符串常量池中有该字符串,则直接调用,因此a和b指向同一个字符串,两者地址值必然相同,输出为true。
System.out.println(c == d);
//与上面不同,c和d两者均为新创建的字符串对象,地址值不同,因而输出为false。
System.out.println(c.equals(d));
//c和d比较实际值,必然相等,输出为true。

针对地址值,进行补充说明:
Java的对象中寄存着真正的地址值——内存地址,但是永远都不能输出出来。因为一旦对对象进行输出,会自动调用toString()方法,在未重写toString()方法时,则只能打印出类包名路径@hashcode——哈希地址。
哈希地址是数据在哈希表中的位置,是通过哈希算法散列得到;而内存地址是通过物理层面得到,由系统分配。
不同对象,内存地址不可能相同,而哈希地址有极小概率可能相同。
哈希地址是逻辑上确保唯一性,内存地址是物理上确保唯一性。
因为java无法真正调出内存地址值,因此其安全性很高。

你可能感兴趣的:(Java中的栈、堆和方法区)