如果我在Jasmin程序集中创建一个新项目然后存储它,我会使用指令aload,因为它是一个地址:
new Object
dup
invokespecial.....
astore_3 ; load the object reference into local variable 3
现在,如果我想从常量池中保存一个字符串......我会用ldc创建它,然后用aload保存它:
ldc "Great string"
astore_3 ; save the reference to the actual string in the constant pool
现在......这些地址是在同一表格和相同的字节数上吗?由于我使用相同的指令来加载和存储这些项,因此JVM必须能够区分属于常量池的地址和堆中的地址?
在检查字节码时,似乎我的情况下常量池中的实际地址只是一个1字节的索引(我猜这个常量池的主要引用也保存在某处)...现在我知道了那是对常量池中som UTF8数据的引用,但是实际字符串所在的位置还是仅仅是对其他地方的字节数组的引用?检查堆中的“新对象”的地址我无法做到......基本上,我需要弄清楚这两个内存区域如何使用相同形式的指令以及JVM如何管理判断地址是常量池中的偏移量还是堆中的对象?
答案 0 :(得分:2)
JVM解释的字节码不一定与.class
文件中写入的字节码相同。许多JVM在不同的执行阶段执行所谓的字节码重写。
HotSpot JVM也是如此。初始化类时,HotSpot重写ldc
字节码,引用常量池中的String条目,使用JVM特定的fast_aldc
字节码,该字节码引用CP缓存中的对象(即java.lang.String
个实例)。当第一次执行此类fast_aldc
字节码时,JVM会解析常量池条目,在Java堆中创建一个String,并使用对此String的引用填充CP缓存。在进一步执行相同的字节码后,JVM将立即从CP缓存中获取引用并将其推送到Java堆栈。
在解释ldc
字节码(或其重写形式)之后,栈顶将包含对Java堆中对象的有效引用。 new
字节码生成相同类型的引用。所以没有必要区分引用类型。
解释器的工作方式。当然,在一个方法得到JIT编译之后,就没有更多的字节码,常量池引用等等。所有这些都只是抽象。只是一个模特。
答案 1 :(得分:0)
首先,整个字节码格式只是VM提供的抽象。它不一定与运行时代码或内存的实际表示有任何相似之处。
其次,Constant Pool是一个包含16位索引的最多65,535个条目的表。由于使用小索引和类别1类型索引常量池是一项常见任务,因此有一个特殊的速记指令 - ldc。
ldc指令使用单字节索引,因此它仅可用于前255个条目。如果要访问上面的条目,则需要使用两字节格式ldc_w。这种情况类似于其他速记说明,例如aload_3 vs aload 3 vs wide aload 3.
而且,这一切都是抽象的。实际上,VM会将常量池转换为更友好的内部格式,并可以将实际指向其运行时位置的指针编译到代码中。但这只是一种可能的实施方式。