首页 > 解决方案 > 成员字段位于 JVM 中的什么位置?

问题描述

public class MemoryTest {

  final String fs = "final String"; //A
  final int fi = 1;                 //A

  String s = "Member String";       //A
  int i = 2;                        //A

  final static String fss = "final static String"; //B
  final static int fsi = 3;                        //B

  static String ss = "static String"; //C
  static int si = 4;                  //C

  public static void main(String args[]){
   MemoryTest m = new MemoryTest();
  }
}

[我的答案]

//A: 从类中创建对象时,通过引用方法区的常量池将其复制到对象中,并为创建的对象分配这些值。

=> 位置:堆

//B : 存在于方法区的常量池中,不复制到对象

=> 位置:方法区域的常量池

// C : 当执行引擎运行“static {}”时,方法区中的类变量会被引用到方法区中的一个常量池中。

=> location : 方法区的类变量

字节码分析

标签: javajvm-hotspot

解决方案


//A: 从类中创建对象时,通过引用方法区的常量池将其复制到对象中,并为创建的对象分配这些值。

不正确。

  1. 您所说的常量池是类文件的一部分,运行程序1无法引用池中的值。解释器和 JIT 编译器在后台使用它们,但这在应用程序级别是不可见的。

  2. 常量池由类文件中的所有方法共享。它不属于任何方法领域。

在字符串文字和字符串值常量表达式的情况下,类文件常量池中的值用于创建String对象2。然后将该对象interned放入运行时字符串池,并添加到运行时描述符中。

请注意,字符串池与常量池不同。在现代 JVM 中,字符串池是常规堆中的数据结构。

=> 位置:堆

正确的。String对象和MemoryTest持有fs, fi,s和变量的对象i都在堆中;即这些变量都在堆中。

s变量fs保存引用。i和变量保存简单的fi32 位整数值。他们没有提到任何东西。它们是……自给自足的。

//B : 存在于方法区的常量池中,不复制到对象

不正确;看上面。

情况有点复杂ffi,该变量的值的使用可能会被字节码编译器内联到代码中。但是,该字段在运行时仍然存在,并且可以使用反射或调试代理访问。

=> 位置:方法区域的常量池

不正确。对象的位置String在上面的字符串池中。fss和变量位于类静态框架中,该fsi框架也在堆中。

// C : 当执行引擎运行“static {}”时,方法区中的类变量会被引用到方法区中的一个常量池中。

不正确;看上面。(虽然si不会被内联。)

=> location : 方法区的类变量

不正确;看上面。


1 - 好的,您可以编写应用程序代码来自己解析类文件。或者您可以(理论上)使用本机代码来查找 JVM 缓存常量池的位置并挖掘信息。只是不要。

2 - 这曾经是急切地完成的,但最近的 JVMString在第一次使用字符串文字时会懒惰地创建对象。实际上可以编写一个测试来推断对象的创建时间。Jon Skeet 给我看了一次 :-)


推荐阅读