Java中的内存地址存储在哪里?

4

Java中的内存地址存储在哪里?我想要理解的是以下内容的存储方式。我已经知道堆栈和堆之间的区别,但想更深入地挖掘一层。

int i = 5;

i5是分别存储的,然后建立映射关系吗?

同样地,我们说对于32位版本最多可以有4GB RAM(实际上要少得多),那么这些内存块的内存地址存储在哪里呢?


1
i并没有被“存储”。它只是内存中某个位置的别名。而数字5则被存储在该位置上。 - NPE
http://www.docjar.com/docs/api/sun/misc/Unsafe.html - user2982130
2个回答

7
我想了解的是以下内容如何存储。
这取决于 int i = 5; 的位置:
局部变量
如果它在方法内,因此 i 是一个局部变量,那么 5 作为栈上的局部变量存储(i 不会“存储”在任何地方,字节码生成器只是记住了 i 在栈上的位置)。字节码具有用于与局部变量交互的特定指令,包括一些非常高效的专用无参版本,例如从“局部变量0”加载 intiload_0。(这些通过 iload_3 进行,然后有一个带参数的版本,iload 后跟索引。)
考虑这个类:
public class Example {

    public static final void main(String[] args) {
        int i = 5;

        System.out.println(i);
    }
}

以下是该代码的字节码(您可以通过编译并执行javap -c Example命令来获取),右侧带有注释:
public class Example {
  // ...omitted constructor stuff...

  public static final void main(java.lang.String[]);
    Code:
      // The `int i = 5;` line:
       0: iconst_5               // Load the constant 5 onto the stack
       1: istore_1               // Store it in local variable 1

      // The `System.out.println(i);` line:
       2: getstatic     #2       // Get the static field java/lang/System.out:Ljava/io/PrintStream onto the stack
       5: iload_1                // Load int variable 1 on the stack
       6: invokevirtual #3       // Call java/io/PrintStream.println, which gets
                                 // the stream and what to write from the stack
       9: return
}

请注意栈的两种不同用法:局部变量在方法为局部变量分配自己的堆栈部分上(因为它预先知道有多少个),然后是其后面的动态部分,用于将信息传递给方法等。
实例字段
如果这是类定义内部的内容,因此i是该类的实例字段,则i是Java为实例保留的结构的一部分(在堆栈或堆上,有时在它们之间移动)。
字节码并没有真正阐明这一点,但请考虑以下类:
public class Example {

    int i = 5;

    public static final void main(String[] args) {

        Example ex = new Example();
        System.out.println(ex.i);
    }
}

这是它的字节码:

public class Example {
  // Here's the instance field
  int i;

  // ...omitted the constructor stuff...

  public static final void main(java.lang.String[]);
    Code:
      // Create our Example instance and save it in a local variable
       0: new           #3
       3: dup
       4: invokespecial #4
       7: astore_1

      // The `System.out.println(ex.i)` line:
       8: getstatic     #5     // Get the java/lang/System.out:Ljava/io/PrintStream field
      11: aload_1              // Get `ex` onto the stack
      12: getfield      #2     // Get the value of field `i` onto the stack from the instance we just put on the stack (pops the instance off)
      15: invokevirtual #6     // Call java/io/PrintStream.println, which
                               // again gets the stream to write to and
                               // what to write from the stack
      18: return
}

注意:由于您正在描述虚拟机字节码,这与它在实际机器中存储的方式没有太大关系,因此这是关于它如何虚拟存储的。 - Peter Lawrey

2
在Java中,内存地址并不仅仅是指引用,由于压缩oops的存在,它们很少被用于Java相关的数据。
我想要理解的是像下面这样的东西是如何存储的。值得记住的是,JVM是一个虚拟机,你可以说在字节码中它被理论上存储了,但是代码本身经过了多个优化阶段,每次都可能会有所改变。
另外,JVM非常擅长优化掉那些毫无意义的代码,因此在许多微不足道的情况下,答案是:没有地方,代码已经被优化成了无关紧要的东西。
我已经知道栈和堆的区别,但我想再深入挖掘一层。
 int i = 5;

是i和5分别存储,然后建立映射吗?

在运行时,这个值很可能已经被内联优化掉了。但是,它可能存在于堆栈、堆或两者中,具体取决于上下文。

同样的,我们说对于32位版本,最多只能有4GB的RAM(实际上要少得多),所有这些内存块的内存地址都存储在哪里?

在32位版本中,您只能使用可用的最大连续区域作为堆。这在Windows上限制为约1.4GB,在Unix上限制为约3GB。

在64位版本中,通常使用32位引用,通过翻译允许您寻址多达32GB(Java 7)和64GB(Java 8)。没有压缩指针(Compressed Oops),您可以在大多数操作系统上寻址48位地址空间(受硬件和操作系统的限制)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接