java堆栈⽅法区常量池_java栈、堆、常量池、⽅法区
栈(stack):主要保存基本类型(或者叫内置类型)(char、byte、short、int、long、float、double、boolean)和对象的引⽤,数据可以共享,速度仅次于寄存器(register),快于堆。
堆(heap):⽤于存储对象和数组
常量池:
常量池的划分
Class ⽂件常量池
运⾏时常量池
字符串常量池
1. Class ⽂件常量池
Class ⽂件常量池指的是编译⽣成的 class 字节码⽂件,其结构中有⼀项是常量池(Constant Pool Table),⽤于存放编译期⽣成的各种字⾯量和符号引⽤,这部分内容将在类加载后进⼊⽅法区的运⾏时
常量池中存放。
什么是字⾯量和符号引⽤?
字⾯量是指字符串字⾯量和声明为 final 的(基本数据类型)常量值,这些字符串字⾯量除了类中所有双引号括起来的字符串(包括⽅法体内的),还包括所有⽤到的类名、⽅法的名字和这些类与⽅法的字符串描述、字段(成员变量)的名称和描述符;⽅法内的常量值由栈分配,所以并不算是字⾯量。
符号引⽤,就是指指向 UTF-8 表中向这些字⾯量的引⽤,包括类和接⼝的全限定名(包括包路径的完整名)、字段的名称和描述符、⽅法的名称和描述符。只不过是以⼀组符号来描述所引⽤的⽬标,和内存并⽆关,所以称为符号引⽤,直接指向内存中某⼀地址的引⽤称为直接引⽤。
2. 运⾏时常量池(Run-time Constant Pool)
运⾏时常量池是⽅法区的⼀部分,是⼀块内存区域。Class ⽂件常量池将在类加载后进⼊⽅法区的运⾏时常量池中存放。
⼀个类加载到 JVM 中后对应⼀个运⾏时常量池,运⾏时常量池相对于 Class ⽂件常量池来说具备动态性,Class ⽂件常量只是⼀个静态存储结构,⾥⾯的引⽤都是符号引⽤。⽽运⾏时常量池可以在运⾏期间将符号引⽤解析为直接引⽤。
运⾏时常量池就是⽤来索引和查字段和⽅法名称和描述符的。给定任意⼀个⽅法或字段的索引,通过这个索引最终可得到该⽅法或字段所属的类型信息和名称及描述符信息。
3. 字符串常量池(Interned Strings)
在 JDK1.6(含)之前也是⽅法区的⼀部分,并且其中存放的是字符串的对象。
在 JDK1.7(含)之后,是在堆内存之中,存储的是字符串对象的引⽤,字符串实例是在堆中。
字符串常量池是全局的,JVM 中独此⼀份,因此也称为全局字符串常量池。
⽅法区:
Java⽅法区和堆⼀样,⽅法区是⼀块所有线程共享的内存区域,他保存系统的类信息。 ⽐如类的字段、⽅法、常量池等。⽅法区的⼤⼩决定系统可以保存多少个类。如果系统 定义太多的类,导致⽅法区溢出。虚拟机同样会抛出内存溢出的错误。⽅法区可以理解 为永久区。
String str = new String("abc");问⼀共创造了⼏个对象
String str=new String("abc"); 紧接着这段代码之后的往往是这个问题,那就是这⾏代码究竟创建了⼏个String对象呢?
相信⼤家对这道题并不陌⽣,答案也是众所周知的,2个。
接下来我们就从这道题展开,⼀起回顾⼀下与创建String对象相关的⼀些JAVA知识。
我们可以把上⾯这⾏代码分成String str、=、"abc"和new String()四部分来看待。String str只是定义了⼀个名为str的String类型的变量,因此它并没有创建对象;=是对变量str进⾏初始化,将某个对象的引⽤(或者叫句柄)赋值给它,显然也没有创建对象;现在只剩下new String("abc")了。那么,new String("abc")为什么⼜能被看成"abc"和new String()呢?
我们来看⼀下被我们调⽤了的String的构造器:
public String(String original) { //other code ... } ⼤家都知道,我们常⽤的创建⼀个类的实例(对象)的⽅法有以下两种:
⼀、使⽤new创建对象。
⼆、调⽤Class类的newInstance⽅法,利⽤反射机制创建对象。
我们正是使⽤new调⽤了String类的上⾯那个构造器⽅法创建了⼀个对象,并将它的引⽤赋值给了str变量。同时我们注意到,被调⽤的构造器⽅法接受的参数也是⼀个String对象,这个对象正是"abc"。由此我们⼜要引⼊另外⼀种创建String对象的⽅式的讨论——引号内包含⽂本。
这种⽅式是String特有的,并且它与new的⽅式存在很⼤区别。
String str="abc";
毫⽆疑问,这⾏代码创建了⼀个String对象。
String a="abc"; String b="abc"; 那这⾥呢?
答案还是⼀个。
String a="ab"+"cd"; 再看看这⾥呢?
答案是三个。
说到这⾥,我们就需要引⼊对字符串池相关知识的回顾了。
java valueof在JAVA虚拟机(JVM)中存在着⼀个字符串池,其中保存着很多String对象,并且可以被共享使⽤,因此它提⾼了效率。由于String类是final的,它的值⼀经创建就不可改变,因此我们不⽤担⼼String对象共享⽽带来程序的混乱。字符串池由String类维护,我们可以调⽤intern()⽅法来访问字符串池。
我们再回头看看String a="abc";,这⾏代码被执⾏的时候,JAVA虚拟机⾸先在字符串池中查是否已经存在了值为"abc"的这么⼀个对象,它的判断依据是String类equals(Object obj)⽅法的返回值。如果有,则不再创建新的对象,直接返回已存在对象的引⽤;如果没有,则先创建这个对象,然后把它加⼊到字符串池中,再将它的引⽤返回。因此,我们不难理解前⾯三个例⼦中头两个例⼦为什么是这个答案了。
只有使⽤引号包含⽂本的⽅式创建的String对象之间使⽤“+”连接产⽣的新对象才会被加⼊字符串池中。对于所有包含new⽅式新建对象(包括null)的“+”连接表达式,它所产⽣的新对象都不会被加⼊字符串池中,对此我们不再赘述。因此我们提倡⼤家⽤引号包含⽂本的⽅式来创建String对象以提⾼效率,实际上这也是我们在编程中常采⽤的。
String 类和常量池
1 String 对象的两种创建⽅式:
String str1 = "abcd";
String str2 = new String("abcd");
System.out.println(str1==str2);//false
这两种不同的创建⽅法是有差别的,第⼀种⽅式是在常量池中拿对象,第⼆种⽅式是直接在堆内存空间创建⼀个新的对象。
2 String 类型的常量池⽐较特殊。它的主要使⽤⽅法有两种:
直接使⽤双引号声明出来的 String 对象会直接存储在常量池中。
如果不是⽤双引号声明的 String 对象,可以使⽤ String 提供的 intern ⽅String.intern() 是⼀个 Native ⽅法,它的作⽤是:如果运⾏时常量池中已经包含⼀个等于此 String 对象内容的字符串,则返回常量池中该字符串的引⽤;如果没有,则在常量池中创建与此 String 内容相同的字符串,并返回常量池中创建的字符串的引⽤。
String s1 = new String("计算机");
String s2 = s1.intern();
String s3 = "计算机";
System.out.println(s2);//计算机 System.out.println(s1 == s2);//false,因为⼀个是堆内存中的String对象⼀个是常量池中的String 对象, System.out.println(s3 == s2);//true,因为两个都是常量池中的String对象
3 String 字符串拼接
String str1 = "str";
String str2 = "ing";
String str3 = "str" + "ing";//常量池中的对象 String str4 = str1 + str2; //在堆上创建的新的对象 String str5 = "string";//常量池中的对象 System.out.println(str3 == str4);//false System.out.println(str3 == str5);//true System.out.println(str4 ==
str5);//false
尽量避免多个字符串拼接,因为这样会重新创建对象。如果需要改变字符串的花,可以使⽤ StringBuilder 或者 StringBuffer。
8种基本类型的包装类和常量池
Java 基本类型的包装类的⼤部分都实现了常量池技术,即Byte,Short,Integer,Long,Character,Boolean;这5种包装类默认创建了数值[-128,127]的相应类型的缓存数据,但是超出此范围仍然会去创建新的对象。
两种浮点数类型的包装类 Float,Double 并没有实现常量池技术。
Integer i1 = 33;
Integer i2 = 33;
System.out.println(i1 == i2);// 输出true Integer i11 = 333;
Integer i22 = 333;
System.out.println(i11 == i22);// 输出false Double i3 = 1.2;
Double i4 = 1.2;
System.out.println(i3 == i4);// 输出false
Integer 缓存源代码:
/***此⽅法将始终缓存-128到127(包括端点)范围内的值,并可以缓存此范围之外的其他值。*/
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
应⽤场景:1. Integer i1=40;Java 在编译的时候会直接将代码封装成Integer i1=Integer.valueOf(40);,从⽽使⽤常量池中的对象。 2. Integer i1 = new Integer(40);这种情况下会创建新的对象。
Integer i1 = 40;
Integer i2 = new Integer(40);
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论