由常量池 运行时常量池 String intern方法想到的(一)
2016-03-14 19:41
429 查看
最近在看《深入理解java虚拟机》,看到了常量池(Constant Pool Table)和运行时常量池(Runtime Constant Pool)这两个概念,对这两个概念不是很理解。又看到了String类的intern方法,intern方法也没用过。于是,查了查,记录如下。
以后遇到了问题之后,写blog时,先下问题的结论。
运行时常量池是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对象在堆中的地址),并返回该引用的值。
常量池就是class文件中的字面量及符号引用(符号引用是指被java编译器编译之后出现的各种类[之所以说是java编译器编译之后出现的各种类,而不是java源代码中出现的类是因为,java编译器会对java源代码进行一些优化,如String的”+”操作]及使用到的方法名,参数和返回值)。
运行时常量池就是class文件被加载(load)到JVM之后,常量池存放的内存区,该内存区属于Heap区。
inter方法:当string对象s调用intern时(
下面对“如果不存在s的值,则将s的地址(引用)存放在运行时常量池,并返回s的地址(引用)。”举例图解一下:
这两句话是main方法进来之后就执行的。
这句话会被编译器优化(可以从字节码指令中看出)。可以通过下面的命令查看上面的代码对应的字节码指令:
本文所使用的java版本如下:
上面命令的完整输出如下所示:
从上面可以看到主版本号、次版本号,常量池,java编译器自动添加的默认构造函数的字节码,main方法的字节码。
以后遇到了问题之后,写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 -version javac 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>":()V const #2 = class #22; // java/lang/StringBuilder const #3 = Method #2.#21; // java/lang/StringBuilder."<init>":()V const #4 = class #23; // java/lang/String const #5 = String #24; // 12 const #6 = Method #4.#25; // java/lang/String."<init>":(Ljava/lang/String;)V const #7 = Method #2.#26; // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; const #8 = String #27; // 3 const #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; // Test const #12 = class #31; // java/lang/Object const #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>":()V const #22 = Asciz java/lang/StringBuilder; const #23 = Asciz java/lang/String; const #24 = Asciz 12; const #25 = NameAndType #13:#32;// "<init>":(Ljava/lang/String;)V const #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: 0 public 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字节码的基本知识,并逐句分析字节码指令以及栈深度。相关文章推荐
- opencv 图像去噪要点总结
- 如何挑选适合的前端框架?
- Eclipse 报 堆内存不足
- 使用Reachability网络监测
- 第一次
- caffe代码阅读2:common的实现细节-2016.3.14
- jbpm timer实现自动提醒审批功能
- 2.堆和栈
- codeforces 505B B. Mr. Kitayuta's Colorful Graph-爆搜/DP
- 迷宫
- vim编辑器基本使用命令
- 数据结构入门
- 2016春季练习——水题
- eclipse 中集成 glassfish
- 浅谈 iOS设计之多视图—模态视图的基本操作
- 分布式系统理论基础 - 一致性、2PC和3PC
- Aoj2170 Marked Ancestor
- 最大连续和(分治法)O(nlogn)
- PHP 动态编译安装 mysqli mysql mysqlnd
- 搭建docker私有库registry