[英]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.