我的同事只是问了我一个非常有趣的问题,我不能给他一个答案。
让我们假设我们有以下类:
public class Person {
String name;
public Person(String name) {
this.name = name;
}
public void print() {
System.out.println("xxx");
}
}
现在,我们正在创建对象:
Person p1 = new Person("a");
Person p2 = new Person("b");
Person p3 = new Person("c");
Person p4 = new Person("d");
Person p5 = new Person("e");
Person p6 = new Person("f");
Person p7 = new Person("g");
Person p8 = new Person("h");
问题是:
我们是否保留有关每个对象中可用方法的信息?如果我们创建一个新对象p9
,JVM会使用仅有关字段的信息创建对象,或者它也会添加到有关方法的对象信息中?
另一个问题:
如果我调用p1.print()
会发生什么?p1
是否必须要求Person
类提供此方法,还是已经保存在p1
对象中?
对于所有实例,方法的代码均不复制,这是完全不必要的。该代码生活在内存中的特殊区域中,并由所有实例共享。另一方面,每个实例都拥有实例变量所需的内存。
关于如何调用方法,该对象实际上并不需要每次调用方法时都会询问该类,它具有指向该方法代码的指针,并且可以立即调用。
有关JVM内部工作的更多信息,请参阅此处:http://docs.oracle.com/javase/javase/specs/jvms/se7/html/jvms-2.html
一般而言,无论有多少个对象,代码都会一次表示。对象"具有"方法的概念只是一个抽象。因此,从本质上讲,无论您的方法是实例化还是静态,它仍然"属于"班级。
对于最终方法,呼叫使用静态绑定(基于表达式的类型已经预定了哪种方法)。
对于虚拟方法,发生了更有趣的事情。根据实现的不同,该对象包含正确方法的地址(不是方法本身),或者运行时反映出该对象以确定其实际类并在层次结构中找到适当的方法。
我不知道Oracle JVM是如何做到的,但是在一般面向对象的编程系统中,对象具有一个隐藏的实例变量,可以指向其类。所有实例指向同一类对象,实例方法的指针是类的一部分。
JVM通过不将对象和方法绑定在一起而做得很好。要了解它的功能,您需要了解其底层体系结构。
JVM具有一个类加载程序系统,该系统由以下资源组成:
- 方法区域
- HEAP
- java stack
- PC寄存器
- 本机方法堆栈。
创建一个对象时,类将加载(懒负载),并获取所有必需的资源。在方法区域中,所有方法都存在,并且对象位于堆中,并且它们在所有线程之间共享。JVM分配/组织将程序执行到几个运行时内存区域所需的所有内存。