首页 > 编程知识 正文

字符串池和常量池有什么区别(JAVA字符串常量)

时间:2023-05-06 00:13:20 阅读:69396 作者:4578

在讲述这些之前,我需要一些预备知识:

可以从两个方面看到Java的内存结构。

从这个角度看,Java内存结构包括以下部分:

1、堆栈区域:编译器自动分配释放,具体方法执行结束后,系统自动释放JVM内存资源。

其作用是保存局部变量的值,包含以下内容。 1 .用于保存基本数据类型的值: 2 .保存类的实例,该实例是对堆对象的引用。 也可以用于保存加载方法时的框架。

2、堆:一般由程序员分配释放,JVM不定期看这个对象,如果没有引用就回收这个对象。

它用于存储动态生成的数据,如new出现的实例、字符数组等。 请注意,创建的对象只包含属于各自的成员变量,不包含成员方法。

因为同一类中的对象都有各自的成员变量,并存储在各自的堆中。 但是,他们共享该类的方法,而不是每次创建对象时都复制成员方法。

3、代码区域:存储程序中方法的二进制代码,且多个对象共享一个代码空间区域。

4、数据区:存储静态定义的静态成员。

5、常量池: JVM按加载类型维护常量池。 常量池是这种类型使用的常量的有序集合。 包含对直接常数(基本类型、字符串)和其他类型、方法和字段的符号引用。 池中的数据通过索引访问,就像数组一样。 常量池在Java动态链接中起着核心作用,因为常量池包含对其他类型、方法和字段的所有符号引用。 常量池位于堆中。

下图大致显示了JAVA的内存分配

二.从操作系统上的流程角度出发。 有关定义,请参阅各种操作系统的资料。 例如,如果是Linux,请参考这个简单的介绍。 Linux Processes explained (这方面通常很少被讨论,本文对此进行一点介绍。 )

请注意,JVM规范中描述的抽象JVM概念和实际实现并不一定是一对一的。

接下来,我们来看看代码示例和注释。

1 publicclassteststringconstant {

2 publicstaticvoidmain (string args [ ] ) {

3 //字符串常量,分配给常量池,编译器对其进行优化的Interned table

如果4 //即字符串已经存在,则直接指向s2,而不是重复创建同一对象。

5字符串S1=' hello ';

6字符串S2=' hello ';

7 //new中出现的对象被分配到heap中。s3和s4中,指示的字符串内容相同,但是是2个不同的对象。

8 //所以==比较起来,其保存的参照不同,所以不相等

9字符串3=新字符串(世界);

10 strings4=新字符串(world );

11

12system.out.println(S1==S2 );//真

13system.out.println(S3==S4 ); //false

14system.out.println(S3.equals ) S4 );//真

15 //String重写了equals方法,并比较了内容是否相等。

16 }

17 }

现在,我们将更详细地讨论上面的示例代码中提到的编译器优化。 请看下面的代码示例:

类a {

隐私字符串a=' aa ';

公共布尔方法数据库

String b='bb ';

final String c='cc ';

返回假;

}

}

“aa”和“bb”字符串对象根据JVM规范位于Java heap中,在JDK8之前的HotSpot VM实现中位于PermGen中,在来自JDK7的HotSpot VM中位于常规Java heap中(“cc

这些字符串对象属于interned String。 String是Java对象,必须存在于Java heap中,如JVM规范定义。 互联字符串也不例外。 Interned String的特别之处在于,JVM具有一个对Interned String的引用的字符串表,以防止内容相同的字符串对象重复intern。 (这里是编译器的优化)

虽然没有规定此StringTable如何实现JVM规范,但通常只是保存对String对象的引用,而不是保存String对象的内容。

/p>

从JVM规范看a、b、c变量:

a变量作为A类的对象实例字段,会跟随A的实例在Java heap上。

b变量作为局部变量会在Java线程栈上。

c变量虽然也是局部变量,但因为有final修饰并且有初始化为一个常量值,所以c是一个常量。它可能会被优化掉(就没有c这个变量了),也可能跟b一样作为局部变量在Java线程栈上。

从HotSpot VM的实现看:

当methodB()被解释执行时,输入的字节码是怎样的就会怎样执行,而由于javac的实现不会优化掉变量b,所以调用methodB()时它一定会在Java线程栈上的局部变量区里;当字节码里变量c存在时,它也跟b一样在Java线程栈的局部变量区。

当methodB()被JIT编译执行时,由于局部变量b、c都没有被使用,所以它们经过JIT编译后就消失了,调用methodB()不会在栈上给b或c变量分配任何空间。

通过以上相信大家对于字符串常量的分配区域以及java的内存分配有了一个较为形象的了解。

下面是一些相关知识点的补充与注意事项:

1.分清什么是实例什么是对象。Class a= new Class();此时a叫实例,而不能说a是对象。实例在栈中,对象在堆中,操作实例实际上是通过实例的指针间接操作对象。多个实例可以指向同一个对象。

2.栈中的数据和堆中的数据销毁并不是同步的。方法一旦结束,栈中的局部变量立即销毁,但是堆中对象不一定销毁。因为可能有其他变量也指向了这个对象,直到栈中没有变量指向堆中的对象时,它才销毁,而且还不是马上销毁,要等垃圾回收扫描时才可以被销毁。

3.以上的栈、堆、代码段、数据段等等都是相对于应用程序而言的。每一个应用程序都对应唯一的一个JVM实例,每一个JVM实例都有自己的内存区域,互不影响。并且这些内存区域是所有线程共享的。这里提到的栈和堆都是整体上的概念,这些堆栈还可以细分。

4.类的成员变量在不同对象中各不相同,都有自己的存储空间(成员变量在堆中的对象中)。而类的方法却是该类的所有对象共享的,只有一套,对象使用方法的时候方法才被压入栈,方法不使用则不占用内存。

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。