java 为什么需要常量池

转载自  java 为什么需要常量池

java中讲的常量池,通常指的是运行时常量池,它是方法区的一部分,一个jvm实例只有一个运行常量池,各线程间共享该运行常量池。

java内存模型中将内存分为堆和栈,其中堆为线程间共享的内存数据区域,栈为线程间私有的内存区域。堆又包括方法区以及非方法区部分,栈包括本地方法栈、虚拟机栈等,如下图所示:

为什么需要常量池

jvm 在栈帧(frame) 中进行操作数和方法的动态链接(link),为了便于链接,jvm 使用常量池来保存跟踪当前类中引用的其他类及其成员变量和成员方法。

每个栈帧(frame)都包含一个运行常量池的引用,这个引用指向当前栈帧需要执行的方法,jvm使用这个引用来进行动态链接。

在 c/c++ 中,编译器将多个编译期编译的文件链接成一个可执行文件或者dll文件,在链接阶段,符号引用被解析为实际地址。java 中这种链接是在程序运行时动态进行的。

常量池探秘

每个 java 文件编译为 class 文件后,都将产生当前类独有的常量池,我们称之为静态常量池。class 文件中的常量池包含两部分:字面值(literal)和符号引用(Symbolic Reference)。其中字面值可以理解为 java 中定义的字符串常量、final 常量等;符号引用指的是一些字符串,这些字符串表示当前类引用的外部类、方法、变量等的引用地址的抽象表示形式,在类被jvm装载并第一次使用这些符号引用时,这些符号引用将会解析为直接引用。符号常量包含:

  • 类和接口的全限定名

  • 字段的名称和描述符

  • 方法的名称和描述符

jvm在进行类装载时,将class文件中常量池部分的常量加载到方法区中,此时方法区中的保存常量的逻辑区域称之为运行时常量区。

使用javap -verbose 命令可以查看class字节码的详细信息,其中包含了编译期确定的静态常量池。

public class StringTest {public static void main(String[] args){String s = new String("abc");String s2 = s.intern();System.out.println(s2 == s);String s3 = (s + s2);System.out.println(s3 == s3.intern());}
}

上述代码javap -verbose后得到(只拿出常量池部分):

major version: 52
Constant pool:#1 = Methodref          #13.#26        // java/lang/Object."<init>":()V#2 = Class              #27            // java/lang/String#3 = String             #28            // abc#4 = Methodref          #2.#29         // java/lang/String."<init>":(Ljava/lang/String;)V#5 = Methodref          #2.#30         // java/lang/String.intern:()Ljava/lang/String;#6 = Fieldref           #31.#32        // java/lang/System.out:Ljava/io/PrintStream;#7 = Methodref          #33.#34        // java/io/PrintStream.println:(Z)V#8 = Class              #35            // java/lang/StringBuilder#9 = Methodref          #8.#26         // java/lang/StringBuilder."<init>":()V#10 = Methodref          #8.#36         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#11 = Methodref          #8.#37         // java/lang/StringBuilder.toString:()Ljava/lang/String;#12 = Class              #38            // StringTest#13 = Class              #39            // java/lang/Object#14 = Utf8               <init>#15 = Utf8               ()V#16 = Utf8               Code#17 = Utf8               LineNumberTable#18 = Utf8               main#19 = Utf8               ([Ljava/lang/String;)V#20 = Utf8               StackMapTable#21 = Class              #40            // "[Ljava/lang/String;"#22 = Class              #27            // java/lang/String#23 = Class              #41            // java/io/PrintStream#24 = Utf8               SourceFile#25 = Utf8               StringTest.java#26 = NameAndType        #14:#15        // "<init>":()V#27 = Utf8               java/lang/String#28 = Utf8               abc#29 = NameAndType        #14:#42        // "<init>":(Ljava/lang/String;)V#30 = NameAndType        #43:#44        // intern:()Ljava/lang/String;#31 = Class              #45            // java/lang/System#32 = NameAndType        #46:#47        // out:Ljava/io/PrintStream;#33 = Class              #41            // java/io/PrintStream#34 = NameAndType        #48:#49        // println:(Z)V#35 = Utf8               java/lang/StringBuilder#36 = NameAndType        #50:#51        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;#37 = NameAndType        #52:#44        // toString:()Ljava/lang/String;#38 = Utf8               StringTest#39 = Utf8               java/lang/Object#40 = Utf8               [Ljava/lang/String;#41 = Utf8               java/io/PrintStream#42 = Utf8               (Ljava/lang/String;)V#43 = Utf8               intern#44 = Utf8               ()Ljava/lang/String;#45 = Utf8               java/lang/System#46 = Utf8               out#47 = Utf8               Ljava/io/PrintStream;#48 = Utf8               println#49 = Utf8               (Z)V#50 = Utf8               append#51 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;#52 = Utf8               toString

我们可以看到,常量池共包含52个常量。#1 是一个类中方法的符号引用,它由 #13 和 #26 两个utf8编码的字符串构成;#3 是程序中定义的 String 类型的字面值 "abc",它包含指向一个utf8编码字符串 "abc" 的索引 #28

方法的调用、成员变量的访问最终都是通过运行时常量池来查找具体地址的。

String 常量池

运行时常量池有一种 String 类型的常量,即通常我们所说的字符串字面值,所有的字符串字面值组成一个 String 常量表。String常量表并不是一成不变的,程序运行时可以动态添加字符串常量,使用String的intern()可以动态的添加String常量。但

jvm 确保两个在值上完全相等的字符串字面值(即其中包含的字符序列是相同的,使用equals()来判断)指向同一个 String 实例。

如:

String s1 = "abc";String s2 = "abc";System.out.println(s1 == s2); // true

上述代码中的字符串 s1 和 s2 将指向同一个 String 实例。实际上通过查看class文件,我们可以看到,在编译后,静态常量池中已经包含了一个 String 类型的字面值 "abc",程序运行时只是从常量池中获取这个String字面值的引用地址,并赋值给变量 s1 和变量 s2。

Constant pool:#1 = Methodref          #6.#19         // java/lang/Object."<init>":()V#2 = String             #20            // abc······#20 = Utf8               abcpublic static void main(java.lang.String[]);······Code:stack=3, locals=3, args_size=10: ldc           #2                  // String abc2: astore_13: ldc           #2                  // String abc

其中,ldc 表示将一个常量加载到操作数栈。

String 的 intern() 是一个native方法,返回的是一个String对象的标准表示。当调用该方法时,如果运行时常量池中已经存在与之相等(equal())的字符串,则直接返回常量池中的字符串引用,否则将此字符串添加到池中,并返回。

String s1 = "abc";String s2 = new String("abc");System.out.println(s1 == s2);           //返回 falseSystem.out.println(s1.equals(s2));      //返回 trueSystem.out.println(s1 == s2.intern());  //返回 true

上述代码中,虽然 s1 和 s2 中的值是相同的,但是他们指向的并不是同一个对象,但 s2 的标准化表示和s1是同一个 String 对象,都是编译期确定的常量池中的 "abc"。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/328951.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

.NET Core第三方开源Web框架YOYOFx

YOYOFx框架 YOYOFx是一个轻量级用于构建基于 HTTP 的 Web 服务&#xff0c;基于 .NET 和 Mono 平台。 本着学习的态度&#xff0c;造了这个轮子&#xff0c;也是为了更好的了解各个框架的原理和有点&#xff0c;还希望可以和大家多交流 。 GitHub&#xff1a;https://github.co…

JavaScript实现搜索框效果

要求&#xff1a;搜索框获取焦点的时候如果里面的内容是“请输入关键字”&#xff0c;则清空&#xff0c;否则不清空 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">…

JVM调优总结(1):一些概念

转载自 JVM调优总结&#xff08;1&#xff09;&#xff1a;一些概念数据类型Java虚拟机中&#xff0c;数据类型可以分为两类&#xff1a;基本类型和引用类型。基本类型的变量保存原始值&#xff0c;即&#xff1a;他代表的值就是数值本身&#xff1b;而引用类型的变量保存引用…

android通讯录增删改查,android 通话记录的增删改查 .

//获取当前的手机号public String getLocalNumber() {Context context getApplicationContext();TelephonyManager tManager (TelephonyManager)context.getSystemService(TELEPHONY_SERVICE);String number tManager.getLine1Number();return number;}//删除通话记录privat…

使用C#进行系统编程

虽然对于系统编程&#xff08;System programming&#xff09;的定义很模糊&#xff0c;不过可以将其描述为在比特、字节、指令&#xff0c;或CPU周期层面所进行的思考。系统编程这个概念也暗含了对性能和可靠性的需求。Microsoft技术总监Joe Duffy在QCon New York活动中介绍了…

html表格转换为csv,python实现将html表格转换成CSV文件的方法

python实现将html表格转换成CSV文件的方法发布于 2015-11-18 16:53:39 | 155 次阅读 | 评论: 0 | 来源: 网友投递Python编程语言Python 是一种面向对象、解释型计算机程序设计语言&#xff0c;由Guido van Rossum于1989年底发明&#xff0c;第一个公开发行版发行于1991年。Pyth…

JVM调优总结(2):基本垃圾回收算法

转载自 JVM调优总结&#xff08;2&#xff09;&#xff1a;基本垃圾回收算法可以从不同的的角度去划分垃圾回收算法&#xff1a; 按照基本回收策略分 引用计数&#xff08;Reference Counting&#xff09;: 比较古老的回收算法。原理是此对象有一个引用&#xff0c;即增加一个…

巧妙使用信用卡N个小技巧

大家看标题也看出来了&#xff0c;今天的博文与之前的相差十万八千里&#xff0c;既不是代码&#xff0c;也不是黑科技&#xff0c;既不属于创业&#xff0c;也不属于赚钱&#xff0c;今天咱们坐下来就慢慢的谈谈信用卡吧&#xff0c;现在大多数白领&#xff0c;老板&#xff0…

ubuntu16.4下用jexus部署asp.net core rtm

今天说下ubuntu 下部署asp.net core&#xff0c;不需要安装.net core sdk&#xff0c;自带运行时方式部署&#xff0c;利用jexus服务器转发请求到asp.net core. 1.部署准备环境 vmware虚拟机&#xff08;其他也可以&#xff09; ubuntu16.04镜像 2.发布环境 win10系统 vs2015 u…

html页面会出现浏览器崩溃,大规模WebGL应用引发浏览器崩溃的几种情况及解决办法...

我们使用Javascript写WebApp的时候&#xff0c;一般都不会考虑对象的生命周期&#xff0c;不太关注内存“泄露”的问题&#xff0c;依赖JS引擎的垃圾回收机制就可以运行的很好&#xff0c;基本上很少会出现浏览器崩溃的情况。但在Web端显示大规模三维模型的时候&#xff0c;内存…

细说SSO单点登录

什么是SSO&#xff1f; 如果你已知道&#xff0c;请略过本节&#xff01; SSO核心意义就一句话&#xff1a;一处登录&#xff0c;处处登录&#xff1b;一处注销&#xff0c;处处注销。即&#xff1a;在多个应用系统中&#xff0c;用户只需要登录一次就可以访问所有相互信任的应…

java实现字符逆序输出

package cn.jbit.array;import java.util.Arrays;public class CharsSort {/*** 字符逆序输出*/public static void main(String[] args) {char[] chars new char[]{a,c,u,b,e,p,f,z};System.out.print("原字符序列&#xff1a;");for(int i 0; i < chars.lengt…

html5页面转场,基于HTML5 SVG的页面过渡切换效果

示例中使用了CSS Flexbox和CSS变量&#xff0c;不是所有的浏览器都支持这些新的CSS属性。该HTML5 SVG的页面过渡变形切换效果的基本HTML结构如下&#xff1a;d"M -44,-50 C -52.71,28.52 15.86,8.186 184,14.69 383.3,22.39 462.5,12.58 638,14 835.5,15.6 987,6.4 1194,1…

JVM调优总结(4):分代垃圾回收

转载自 JVM调优总结&#xff08;4&#xff09;&#xff1a;分代垃圾回收为什么要分代 分代的垃圾回收策略&#xff0c;是基于这样一个事实&#xff1a;不同的对象的生命周期是不一样的。因此&#xff0c;不同生命周期的对象可以采取不同的收集方式&#xff0c;以便提高回收效…

.NET Core系列 : 2 、project.json 这葫芦里卖的什么药

.NET Core系列 &#xff1a; 1、.NET Core 环境搭建和命令行CLI入门 介绍了.NET Core环境&#xff0c;本文介绍.NET Core中最重要的一个配置文件project.json的相关内容。我们可以使用.NET Core 的dotnet 命令行接口&#xff08;CLI&#xff09;dotnet new命令创建一个应用&…

html5 地图效果,html5 echarts图表插件炫光的分布地图动画特效

特效描述&#xff1a;图表插件 炫光的动画 分布地图动画 动画特效。html5地图动画&#xff0c;html5 echarts图表插件代码结构1. 引入JS2. HTML代码&#xfeff;ECharts$(#document).ready(function(){getEcharts();});function getEcharts(){// Step:3 conifg EChartss path, …

ASP.NET MVC @helper使用说明

简单的 helper 方法应用场景 Razor中的helper语法让您能够轻松创建可重用的方法&#xff0c;此方法可以在您的视图模板中封装输出功能。他们使代码能更好地重用&#xff0c;也使代码更具有可读性。 在我们定义helper方法之前的代码 让我们看一个简单的产品列表应用场景。在此场…

java实现向有序数组中插入一个元素

package cn.jbit.array; import java.util.*;public class Insert {public static void main(String[] args) {//字符排序char[] chars new char[9];chars[0] a;chars[1] c;chars[2] u;chars[3] b;chars[4] e;chars[5] p;chars[6] f;chars[7] z;System.out.print(&quo…

JVM调优总结(5):典型配置

转载自 JVM调优总结&#xff08;5&#xff09;&#xff1a;典型配置以下配置主要针对分代垃圾回收算法而言。 堆大小设置 年轻代的设置很关键 JVM中最大堆大小有三方面限制&#xff1a;相关操作系统的数据模型&#xff08;32-bt还是64-bit&#xff09;限制&#xff1b;系统的…

html点击弹出iframe,JavaScript:关于跨iframe弹窗

index.htmliframe.html$(function(){$("#window").window({width: 200,height: 100});});效果由于是在iframe中弹窗致使窗口被腰斩&#xff0c;所以需要向window中追加节点再弹窗&#xff0c;解决方式如下var win window.top.document.createElement("div"…