由常量池 运行时常量池 String intern方法想到的(一)
来源:互联网 发布:ubuntu root无声音 编辑:程序博客网 时间:2024/06/14 02:57
最近在看《深入理解java虚拟机》,看到了常量池(Constant Pool Table)和运行时常量池(Runtime Constant Pool)这两个概念,对这两个概念不是很理解。又看到了String类的intern方法,intern方法也没用过。于是,查了查,记录如下。
以后遇到了问题之后,写blog时,先下问题的结论。
结论
常量池这个概念是针对java class文件而言的。当java代码被编译成字节码(class)文件时,会将字面量(文本字符串,声明为final的常量值)、符号引用存放在class文件的常量池中。
运行时常量池是JVM内存中方法区的一部分。当class文件被加载(load)到JVM时会将常量池中的内容存放在运行时常量池(在perm区中,即永久代)中。上面 这一句只是针对JDK1.6及之前的版本适用。JDK1.7之后对方法区进行了一些优化。1.7之后将运行时常量池移到了堆中。原因,Perm 区是一个类静态的区域,主要存储一些加载类的信息,常量池,方法片段等内容,默认大小只有4M。一旦超过范围,会直接产生java.lang.OutOfMemoryError: PermGen space错误。
intern方法:当调用该方法时,会在运行时常量池查看有无该字符串,如果有的话,直接返回该引用(这个字符串在运行时常量池中的地址);如果没有的话,在运行时常量池中存放一份数据,并返回该字符串在运行时常量池中的地址。这都是在JDK1.6及之前发生的事情。如果是1.7之后,如果发现在运行时常量池中没有的话,在运行时常量池中存放的是一份这个字符串String的引用(即这个String对象在堆中的地址),并返回该引用的值。
终极结论
终极结论针对JDK1.7及以后。
常量池就是class文件中的字面量及符号引用(符号引用是指被java编译器编译之后出现的各种类[之所以说是java编译器编译之后出现的各种类,而不是java源代码中出现的类是因为,java编译器会对java源代码进行一些优化,如String的”+”操作]及使用到的方法名,参数和返回值)。
运行时常量池就是class文件被加载(load)到JVM之后,常量池存放的内存区,该内存区属于Heap区。
inter方法:当string对象s调用intern时(s.intern()
),如果运行时常量池中有s的值,则直接返回该字符串在运行时常量池的地址;如果不存在s的值,则将s的地址(引用)存放在运行时常量池,并返回s的地址(引用)。
下面对“如果不存在s的值,则将s的地址(引用)存放在运行时常量池,并返回s的地址(引用)。”举例图解一下:
public class Test { public static void main(String[] args) { String s = new String("12") + new String("3"); s.intern(); }}
这两句话是main方法进来之后就执行的。
String s = new String("12") + new String("3");
这句话会被编译器优化(可以从字节码指令中看出)。可以通过下面的命令查看上面的代码对应的字节码指令:
//查看java版本java -versionjavac Test.java //编译//-verbose查看class文件中的常量池//-c 表示将生成JVM字节码指令javap -verbose -c Test
本文所使用的java版本如下:
java version "1.6.0_45"Java(TM) SE Runtime Environment (build 1.6.0_45-b06)Java HotSpot(TM) 64-Bit Server VM (build 20.45-b01, mixed mode)
上面命令的完整输出如下所示:
Compiled from "Test.java"public class Test extends java.lang.Object SourceFile: "Test.java" minor version: 0 major version: 50 Constant pool:const #1 = Method #12.#21; // java/lang/Object."<init>":()Vconst #2 = class #22; // java/lang/StringBuilderconst #3 = Method #2.#21; // java/lang/StringBuilder."<init>":()Vconst #4 = class #23; // java/lang/Stringconst #5 = String #24; // 12const #6 = Method #4.#25; // java/lang/String."<init>":(Ljava/lang/String;)Vconst #7 = Method #2.#26; // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;const #8 = String #27; // 3const #9 = Method #2.#28; // java/lang/StringBuilder.toString:()Ljava/lang/String;const #10 = Method #4.#29; // java/lang/String.intern:()Ljava/lang/String;const #11 = class #30; // Testconst #12 = class #31; // java/lang/Objectconst #13 = Asciz <init>;const #14 = Asciz ()V;const #15 = Asciz Code;const #16 = Asciz LineNumberTable;const #17 = Asciz main;const #18 = Asciz ([Ljava/lang/String;)V;const #19 = Asciz SourceFile;const #20 = Asciz Test.java;const #21 = NameAndType #13:#14;// "<init>":()Vconst #22 = Asciz java/lang/StringBuilder;const #23 = Asciz java/lang/String;const #24 = Asciz 12;const #25 = NameAndType #13:#32;// "<init>":(Ljava/lang/String;)Vconst #26 = NameAndType #33:#34;// append:(Ljava/lang/String;)Ljava/lang/StringBuilder;const #27 = Asciz 3;const #28 = NameAndType #35:#36;// toString:()Ljava/lang/String;const #29 = NameAndType #37:#36;// intern:()Ljava/lang/String;const #30 = Asciz Test;const #31 = Asciz java/lang/Object;const #32 = Asciz (Ljava/lang/String;)V;const #33 = Asciz append;const #34 = Asciz (Ljava/lang/String;)Ljava/lang/StringBuilder;;const #35 = Asciz toString;const #36 = Asciz ()Ljava/lang/String;;const #37 = Asciz intern;{public Test(); Code: Stack=1, Locals=1, Args_size=1 0: aload_0 1: invokespecial #1; //Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 1: 0public static void main(java.lang.String[]); Code: Stack=4, Locals=2, Args_size=1 0: new #2; //class java/lang/StringBuilder 3: dup 4: invokespecial #3; //Method java/lang/StringBuilder."<init>":()V 7: new #4; //class java/lang/String 10: dup 11: ldc #5; //String 12 13: invokespecial #6; //Method java/lang/String."<init>":(Ljava/lang/String;)V 16: invokevirtual #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 19: new #4; //class java/lang/String 22: dup 23: ldc #8; //String 3 25: invokespecial #6; //Method java/lang/String."<init>":(Ljava/lang/String;)V 28: invokevirtual #7; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 31: invokevirtual #9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String; 34: astore_1 35: aload_1 36: invokevirtual #10; //Method java/lang/String.intern:()Ljava/lang/String; 39: pop 40: return LineNumberTable: line 3: 0 line 4: 35 line 5: 40}
从上面可以看到主版本号、次版本号,常量池,java编译器自动添加的默认构造函数的字节码,main方法的字节码。
结束语
这篇博文引出了问题,何为常量池,何为运行时常量池,String#intern()方法是干什么的;举了一个例子,引出了java字节码指令。下篇文章由常量池 运行时常量池 String intern方法想到的(二),学习下java字节码的基本知识,并逐句分析字节码指令以及栈深度。
- 由常量池 运行时常量池 String intern方法想到的(一)
- 由常量池 运行时常量池 String intern方法想到的(四)之深入理解intern
- 由常量池 运行时常量池 String intern方法想到的(四)之 常量池
- 由常量池 运行时常量池 String intern方法想到的(三)之String内存模型
- JVM-由常量池 运行时常量池 String intern方法想到的(三)之String内存模型
- 由常量池 运行时常量池 String intern方法想到的(二)之class文件及字节码指令
- String放入运行时常量池的时机与String.intern()方法解惑
- String放入运行时常量池的时机与String.intern方法解惑
- 运行时常量池
- 运行时常量池
- Java String intern() 方法解析 常量池
- String中的intern方法与常量池
- String.intern()方法与常量池存入时的疑惑!
- 虚拟机运行时常量池与String的intern方法
- java常量池和运行时常量池
- 常量池、运行时常量池、字符串值基本概念区分
- 运行时常量池与Class文件常量池的区别
- final修饰的常量和jvm中的运行时常量池没有关联
- Jenkins问题
- Linux统计某文件夹下文件、文件夹的个数
- hdu1050 活动安排问题(有坑)
- c盘下安装的文件访问权限问题
- 欢迎使用CSDN-markdown编辑器
- 由常量池 运行时常量池 String intern方法想到的(一)
- POJ 2236 - Wireless Network
- c# 动态设置控件位置
- 重学备忘
- 文章标题
- An instance of “(null)” is already running. Choose “Stop” to terminate and launch a new instance.
- Leet Code OJ 268. Missing Number [Difficulty: Medium]
- ubuntu 切换python3 和 python2
- Android工程的编译过程