繁体   English   中英

Java在内存中的含义是什么

[英]What does Java look like in memory

我是java新手,还在学习。 我抓住了内心和匿名课程。 现在我有一个关于java在内存中的样子,分配对象,定义类等的技术问题。

就像当一个字段是一个在外部类和内部类中定义的对象时,内存的样子。 静态类看起来与非静态不同吗?

我只需要一个视觉参考。

多谢你们

细节在实现中(不是规范)。 然而,实现通常遵循非常简单的模式。 Java中的大多数内存布局都非常简单明了。 我的术语可能与Java术语不匹配,因为我没有做很多Java编程。

通常,对象以指向其vtable的指针开始,然后有一堆字段。 字段是基本类型(int / bool / float)或指向对象的指针。 这就是对象。 (类也是对象。)空指针就像C一样,它们是无效的,不像Python,其中None是一个对象。

在内部类中,有一个额外的隐藏字段,指向外部类的实例。 这是内部类访问外部类中的数据的方式。 匿名类的工作方式相同。 静态方法只是类的方法而不是实例上的方法。

vtable是所有魔法发生的地方。 每个类都有自己的vtable在所有对象之间共享。 vtable具有关于类的信息,例如其实例的大小以及字段的布局方式。 垃圾收集器使用此信息。 vtable还有一个指向该类实现的所有方法的指针。 当您调用方法时,运行时首先从对象中获取vtable指针,然后将方法指针从vtable中取出,然后调用该方法并将该对象作为隐式参数传递给该方法。 它与C ++类似,但实现起来要简单得多。 如果方法或类是“最终的”,则可以跳过该过程。

我知道Java并没有真正的“指针”,它有“符号句柄”或类似的东西,但常见的实现只使用普通的旧指针。

欢迎来到Java世界。 与C语言不同,语言构造和内存表示几乎一对一地映射,Java稍微复杂一些。

首先,当人们谈论Java时,它可能意味着两件事:Java-the-language和Java-the-platform。 在这里,我将Java称为Java编程语言。 用Java编写的代码首先被编译为字节码,Java虚拟机的机器代码。 如果您对Java语言的详细信息感兴趣,请参阅Java语言规范 对于JVM,有Java虚拟机规范

当我有一个字段是一个在外部类和内部类中定义的对象时,内存是什么样的。

我将这解释为Java虚拟机中的内存布局是什么样的,因为物理布局取决于JVM的实现。 为此,我浏览了Java虚拟机的结构

与Java语言一样,Java虚拟机也可以使用两种类型: 基本类型引用类型 相应地,有两种值可以存储在变量中,作为参数传递,由方法返回,并对其进行操作: 原始值参考值

Java虚拟机期望几乎所有类型检查都在编译时完成,而不是由Java虚拟机本身完成。 特别是,不需要标记数据或以其他方式检查数据以确定类型。

....

对对象的引用被视为具有Java虚拟机类型reference 类型reference值可以被认为是指向对象的指针。

所以答案似乎是两个字段看起来完全一样: reference

就像当一个字段是一个在外部类和内部类中定义的对象时,内存的样子。 静态类看起来与非静态不同吗?

非静态内部(或匿名)类的实例将引用用于实例化它的外部类实例。 这允许内部类中的方法引用封闭类中声明的实例级成员。 通常,此引用作为构造函数中的隐藏额外参数传递给内部类。 但是如果使用反射来创建内部类实例,则必须显式提供该额外参数。

(注意,当匿名类使用实例化它的方法范围内的locals /参数时,会使用不同的机制...)

如果您需要更多详细信息,可以使用javap来反汇编一些简单示例类的字节码。

我只需要一个视觉参考。

对不起,我不做漂亮的照片:-)

静态(嵌套)类的工作方式与顶级类的工作方式完全相同。 唯一的区别是它的名字有另一个以它为前缀的类名。 (如果查看已编译的.class文件,您实际上会看到对于名为Nested的类嵌套在名为Outer的类中,您将获得类似“Outer $ Nested.class”的内容。)

内部类有一个隐藏字段,它是对其外部类的包含实例的引用。 当你写:

class Outer {
  final int x;

  class Nested {
    int y;

    Nested(int y) {
      this.y = y;
    }

    int bar() {
      return x + y;
    }
  }

  void foo() {
    Nested n = new Nested(5);
  }
}

就像你写的那样:

class Outer {
  final int x;

  static class Nested {
    Outer outer;
    int y;

    Nested(Outer outer, int y) {
      this.outer = outer;
      this.y = y;
    }

    int bar() {
      return outer.x + y;
    }
  }

  void foo() {
    Nested n = new Nested(this, 5);
  }
}

隐藏字段的名称(我在这里称之为“外部”)对你来说是隐藏的,虽然你可以通过在内部类中说Outer.this来引用它(其中Outer是你的外部类的名字,当然)。 同样,请注意,当内部类中的方法引用外部类中的某些内容时,该引用实际上是通过对外部类的隐藏引用。

关于访问控制(例如:private)如何与嵌套/内部类一起工作,还有一些额外的复杂性,但这并不会真正影响您所询问的“内存”问题。

public class I {
    class inner {
        public void ctor() {};
    }
}

看起来像是一样,你可以使用JAD

class I$inner {

  // Field descriptor #6 LI;
  final synthetic I this$0;

  // Method descriptor #8 (LI;)V
  // Stack: 2, Locals: 2
  I$inner(I arg0);
     0  aload_0 [this]
     1  aload_1
     2  putfield I$inner.this$0 : I [10]
     5  aload_0 [this]
     6  invokespecial java.lang.Object() [12]
     9  return
      Line numbers:
        [pc: 0, line: 3]
      Local variable table:
        [pc: 0, pc: 10] local: this index: 0 type: I.inner

  // Method descriptor #14 ()V
  // Stack: 0, Locals: 1
  public void ctor();
    0  return
      Line numbers:
        [pc: 0, line: 4]
      Local variable table:
        [pc: 0, pc: 1] local: this index: 0 type: I.inner
}

作为一个hexdump,它将从0xcafebabe开始

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM