[讨论] 关于编译时常量问题
freish
2012-11-05
问题:如果一个类中包含编译时常量,该常量是否存在与方法区
读《深入Java虚拟机 第二版》P94最后一段有这样的描述“作为常量池或字节码流的一部分,编译时常量保存在方法区中,就和一般的类变量一样”——这个翻译的很拗口,原文“As part of the constant pool, final class variables are stored in the method area--just like non-final class variables”后面还有一句“But whereas non-final class variables are stored as part of the data for the type that declares them, final class variables are stored as part of the data for any type that uses them. ” 但是在P160页又发现这样的描述(第一段代码的下面): class Example1d { static final int angle = 35; static final int length = angle * 2; } When the Example1d class is loaded by a Java Virtual Machine, angle and length are not stored as class variables in the method area 在java虚拟机规范中好像没有找到相关描述 通过反射可以获取常量值,推测应该是存在于方法区的。 ---------------------------- 综上,编译时常量到底有没有存在于方法区? |
|
RednaxelaFX
2012-11-05
Java语言(由Java语言规范所规定的)有常量表达式的概念。这里所认可的常量很重要的一个方面就是它们都是值语义的。值语义意味着你只关心某个数据的值,不关心它的“身份”(identity);也就是说无论存在哪里、有多少份拷贝、是不是同一份拷贝都好,只要值相等你就会认为它们是“相同”的。
那么回过头来看些例子。留意声明的一侧跟使用的一侧不一定是对等的。 如果有: public class Foo { public static final int BAR = 1; public static final int BAZ = 42; public static final int GOO = 256; public static final int QUUX = 65536; } (留意这是声明的一侧) 那么随便找个别的方法来看这些常量要访问的话会用怎样的字节码。 (接下来是使用的一侧) int i = Foo.BAR; 这个语句的右手边的常量表达式会变成: iconst_1 常量1就嵌在指令里了,而且不是作为指令的参数,而是指令本身就嵌着“1”这个常量值 int i = Foo.BAZ; 这个语句的右手边的常量表达式会变成: bipush 42 常量42作为指令bipush的参数嵌在字节码里 int i = Foo.GOO; 这个语句的右手边的常量表达式会变成: sipush 256 常量256作为参数嵌在字节码里 int i = Foo.QUUX; 这个语句的右手边的常量表达式会变成: ldc #2 常量65536存在常量池里的第2项(这个常量池项的编号是我随便选的,无所谓)。 所以你会发现正常(非反射)去访问这些常量的时候,它们可能的形态就有好多种;其中小常量是尽量不放在常量池里的。 当然,因为字节码概念上会存在方法区里,所以那些嵌在指令里的常量概念上也存在方法区里了。不过跟许多人想像的形式大概不一样吧。 而为了支持反射,同样的一份值会在类的元数据里也放上一份。对应到Class文件格式那就是Field里的ConstantValue属性,具体的值会存在声明该常量的类的常量池里(跟使用的一侧没关系)。 ConstantValue_attribute { u2 attribute_name_index; u4 attribute_length; u2 constantvalue_index; } (留意到constantvalue_index存的是个索引号,真正的常量值在这个索引号对应的常量池项) 这一份在运行时就会存在方法区里。 |
|
freish
2012-11-06
RednaxelaFX 写道 Java语言(由Java语言规范所规定的)有常量表达式的概念。这里所认可的常量很重要的一个方面就是它们都是值语义的。值语义意味着你只关心某个数据的值,不关心它的“身份”(identity);也就是说无论存在哪里、有多少份拷贝、是不是同一份拷贝都好,只要值相等你就会认为它们是“相同”的。
那么回过头来看些例子。留意声明的一侧跟使用的一侧不一定是对等的。 如果有: public class Foo { public static final int BAR = 1; public static final int BAZ = 42; public static final int GOO = 256; public static final int QUUX = 65536; } (留意这是声明的一侧) 那么随便找个别的方法来看这些常量要访问的话会用怎样的字节码。 (接下来是使用的一侧) int i = Foo.BAR; 这个语句的右手边的常量表达式会变成: iconst_1 常量1就嵌在指令里了,而且不是作为指令的参数,而是指令本身就嵌着“1”这个常量值 int i = Foo.BAZ; 这个语句的右手边的常量表达式会变成: bipush 42 常量42作为指令bipush的参数嵌在字节码里 int i = Foo.GOO; 这个语句的右手边的常量表达式会变成: sipush 256 常量256作为参数嵌在字节码里 int i = Foo.QUUX; 这个语句的右手边的常量表达式会变成: ldc #2 常量65536存在常量池里的第2项(这个常量池项的编号是我随便选的,无所谓)。 所以你会发现正常(非反射)去访问这些常量的时候,它们可能的形态就有好多种;其中小常量是尽量不放在常量池里的。 当然,因为字节码概念上会存在方法区里,所以那些嵌在指令里的常量概念上也存在方法区里了。不过跟许多人想像的形式大概不一样吧。 而为了支持反射,同样的一份值会在类的元数据里也放上一份。对应到Class文件格式那就是Field里的ConstantValue属性,具体的值会存在声明该常量的类的常量池里(跟使用的一侧没关系)。 ConstantValue_attribute { u2 attribute_name_index; u4 attribute_length; u2 constantvalue_index; } (留意到constantvalue_index存的是个索引号,真正的常量值在这个索引号对应的常量池项) 这一份在运行时就会存在方法区里。 R大解释的非常详细清楚,我想知道的就是有没有存在于类的元数据里,3Q |
|
RednaxelaFX
2012-11-06
freish 写道 R大解释的非常详细清楚,我想知道的就是有没有存在于类的元数据里,3Q
嗯嗯 ![]() 而我想说明的是“不是只存在于声明常量的类的元数据里”。 |
相关讨论
相关资源推荐
- oarcle 导入导出
- windows重启oracle监听口令,oracle_windows下命令启动oracle监听和服务
- windows2008安装oracle19c后监听无法启动
- 【Oracle】解决启动监听报错
- Oracle监听详解
- Oracle无法启动监听方法
- oracle安装后监听少了,Oracle安装监听器错误的解决方法
- oracle监听 program,Oracle 监听整理
- linux系统oracle监听器报错,ORACLE监听器 The listener supports no services 问题解决方法...
- 服务器异常断电,导致ORACLE多个控制文件不一致,无法启动的解决办法