提到常量池,一般是指运行时常量池,是方法区的一部分。方法区就是通常说的永久代。那么常量池中会存储那些数据呢?
①编译期生成的各种字面量和符号引用
②也有可能将运行期间的常量放入常量池
先看第一种:编译期生成的各种字面量和符号引用,这部分数据经过编译后存在.class文件的‘常量池’中,注意这个所谓的‘常量池’是‘静态常量池’,静态常量池的数据会在类加载后放入运行时常量池。举个例子
测试一
public class ConstantPoolAndStrTest {
String string1 = "Hello";
public void methodTest(){
String string2 = "pool";
}
}
将上面代码编译后生成ConstantPoolAndStrTest.class文件,现在我们要想知道数据成员string1=“Hello”和局部变量string2=”pool“会不会进入常量池,只需要解析ConstantPoolAndStrTest.class文件,看其常量池(静态)中是否包含“Hello”和“pool”。而.class文件中存储的是只有JVM能够读懂的字节码,所以我们要想识别其内容要借助JDK自带的反汇编工具javap。执行命令javap -verbose ConstantPoolAndStrTest.class 可以得到下面数据:
上图只贴出了常量池(静态)的数据。可以看到string1和string2都被放入了常量池,String 之所以会被放到常量池里是因为她是final型,而这部分数据会在类加载后放入运行时常量池。
经过同上的测试发现:
测试二
String strTest = "my"+"string";
编译期会将上面代码优化为strTest="mystring","mystring"被存入常量池中,但"my"和"string"并没有进入常量池。
测试三
String string1 = "a";
String string2 = "b";
String string3 = string1+string2;
String string4 = string1+"c";
上面的代码,string3="ab"和string4="bc"都没有进入常量池。结合测试二、测试三可知:java编译器会对字面量的运算(测试一)进行优化,但不会对含有引用的运算(测试二)进行优化。
下面说一个稍微绕一点的情况:
测试四
String newStrtest = new String("pool");
编译,javap 可得到下面结果
到这里可能会有一点迷惑了:
new String("pool")不是在堆中创建对象么?怎么会跑到了常量池里?
没错,new String("pool")是在堆中分配,但new的操作要等到运行时才能执行,编译期并不会执行new操作,而编译器只是把字面量"pool"放入常量池中。运行时执行new操作会重新在堆中创建对象,并将引用 newStrtest指向这个对象。即测试四创建了两个对象,一个在常量池中,一个在堆中。下面这代码输出结果为false,可以证明这一点。
String s1 = "hey";
String s2 = new String("hey");
System.out.println(s1==s2);//==比较的是引用,s1指向常量池,s2指向堆中,引用指向不同地址,因此输出false
再看第二种:[size=small]也有可能将运行期间的常量放入常量池
比如
string.intern()
会检查常量池中是否存在string则返回池里的字符串引用;如果不存在则将string加入到常量池再返回其引用。这样做可以避免在堆中不断的创建字符串对象,起到节省空间的作用。
测试五(下面这段代码来自博客
http://txy821.iteye.com/blog/760957)[/size]
String str1 = "a";
String str2 = "bc";
String str3 = "a"+"bc";
String str4 = str1+str2;
System.out.println(str3==str4);
str4 = (str1+str2).intern();
System.out.println(str3==str4);
输出结果为false,true ‘测试三’中说过:编译器不会优化包含引用的运算,str4会在运行期,在堆中分配内存,所以第一个输出为false;而经过
str4 = (str1+str2).intern();
s4指向了常量池中的"abc"所以第二个输出为true。这样就可以证明上面提到的intern()的作用。
最后,
在JDK源码String.intern()方法的注释中有这样一段话,"A pool of strings, initially empty, is maintained privately by the class {@code String}."
说"有一个字符串池,初始为空,由String类私有的维护"。但注释中并没有说这个字符串池位于JVM内存分区的哪一个部分。是在方法区?堆区?这个困扰了我很长时间。查资料看到有人说位于堆中,但如果这样的话将会和方法区中常量池中的数据大量重复。而且网上的资料看到很多人都在说"字符串池"怎样怎样,但JVM的内存划分中并没有这样一块区域。
所以我把"字符串池"理解成"常量池中字符串的集合"
以上,如有错误,请指正
- 大小: 195.8 KB
- 大小: 231.6 KB
分享到:
相关推荐
【Java面试题】对String常量池的理解
Java常量池是Java编程语言中的一个重要概念,它在JVM(Java虚拟机)的运行时数据区中占据着核心地位。常量池是每个类或接口在编译时都会生成的一部分,它存储了各种类型的常量,包括字面量(如字符串、整数、浮点数...
在Java内存管理中,堆(Heap)、栈(Stack)、常量池(Constant Pool)和方法区(Method Area)是四个核心概念,它们在Java程序运行时扮演着不同的角色。 首先,方法区是用来存放类的信息、常量、静态变量等数据的...
深入探索Java常量池 Java常量池是Java虚拟机(JVM)中一个非常重要的概念,它主要分为两种:静态常量池和运行时常量池。静态常量池是class文件中的常量池,包括字符串(数字)字面值、类和方法的信息,占用了class...
### Java堆、栈和常量池——内存剖析 #### 寄存器 寄存器作为最快的存储区域之一,由编译器自动管理分配与回收,它位于CPU内,用于存储临时变量,例如局部变量和一些操作数。由于寄存器的数量有限且由编译器自动...
Java String 源码和 String 常量池的全面解析 Java String 源码和 String 常量池是 Java 语言中非常重要的两个概念,它们之间存在着紧密的联系。在 Java 语言中,String 类是不可变的,finalize 方法被禁用,以确保...
在Java中,内存主要分为四个区域:寄存器、栈、堆和方法区(包括常量池)。以下是这四个区域的详细说明: 1. **寄存器**: 这是计算机硬件的一部分,用于存储非常快速访问的数据。在Java中,寄存器主要由JVM直接管理...
Java String 字符串常量池解析 Java 中的字符串常量池是一种为了提高性能和减少内存开销的机制。它是 JVM 实例化字符串常量时进行的一些优化,主要是为了减少字符串对象的创建和存储。 字符串常量池的设计思想是...
在Java中,有两种创建字符串对象的方式:直接使用双引号声明出来的String对象会直接存储在常量池中。如果不是用双引号声明的String对象,可以使用String提供的intern方法。intern方法会从字符串常量池中查询当前字符...
### Java堆、栈和常量池详解 #### Java内存模型概览 在深入探讨Java中的堆、栈以及常量池之前,我们先来简要回顾一下Java内存模型的基本概念。Java程序运行时会使用到不同的内存区域来存储各种类型的数据,这些...
"探究Java常量本质及三种常量池" Java中的常量池是Java虚拟机(JVM)中的一种机制,用于存储编译期常量和运行期常量。常量池是JVM中的一种重要机制,它可以将常量存储在内存中,并提供快速的访问和共享机制。 Java...
在Java编程语言中,字符串常量池(String Constant Pool)是一个重要的概念,它与程序的内存管理和性能优化密切相关。理解这个概念对于任何Java开发者来说都至关重要。字符串常量池是Java虚拟机(JVM)在运行时为...
当我们使用`new String("Hello")`创建字符串时,即使字符串字面量已经存在于常量池中,也会创建一个新的String对象。这是因为`new`关键字总是创建一个新的对象实例。 #### 示例代码解析 考虑下面这段示例代码: `...
Java常量池是Java编程语言中的一个重要概念,它涉及到JVM(Java虚拟机)的内存管理,特别是关于字符串和类的加载。常量池在Java中分为两种形态:静态常量池和运行时常量池。 静态常量池是存在于`.class`文件中的,...
在Java中,如果我们使用字面值方式创建的String对象,它们是常量池中的字符串常量,如果我们使用new关键字创建的String对象,它们是运行时创建的新对象。例如,String s0="kvill";,String s1=new String("kvill");...
在Java编程语言中,`String`类是一个...总的来说,Java中的`String`类和它的常量池,以及`equals()`和`==`的区别,是理解Java内存管理和对象比较的关键知识点。掌握这些知识,有助于编写出更加高效、健壮的Java代码。
在JDK 8版本中,字符串常量池的位置发生了变化,从方法区移至Java堆中,但这并不影响程序员对字符串常量池的概念理解与使用。理解这一点对编写高效代码十分关键。 当程序需要存储一段字符串时,字符串常量池的机制...
### Java堆、栈和常量池详解 #### 一、Java内存模型概述 Java程序运行时,内存可以分为几个不同的区域: 1. **寄存器**:这部分内存由硬件直接支持,程序无法直接控制。 2. **栈**:用于存储基本类型的数据和对象...
Java常量池是一种内存管理机制,它在Java虚拟机(JVM)中扮演着重要的角色。常量池主要存储程序中的常量,如字符串字面量、符号引用等,其目的是为了提高内存效率和程序执行速度。下面我们将深入探讨Java常量池的...
7. **String与基本类型的转换** - `Integer.parseInt(String s)`/`Double.parseDouble(String s)`:将字符串转换为整型/浮点型数值。 - `String.valueOf(int i)`/`String.valueOf(double d)`:将整型/浮点型数值...