创建对象时,是将新的内存分配给实例字段和方法,还是仅分配给实例字段


14

我有一个下课

class Student{

int rollNumber;
int marks;

public void setResult(int rollNumber, int marks){

    this.rollNumber=rollNumber;
    this.marks=marks;   
}

public void displayResult(){

    System.out.println("Roll Number= "+this.rollNumber+"   Marks= "+this.marks);

}
}

现在,我创建两个类型为Student的对象,如下所示

Student s1=new Student();
Student s2=new Student();

现在,为实例字段分配了两组不同的内存。现在我的问题是为方法(setResultdisplayResult)分配内存两次还是一次?

请参见下图,您能帮我说一下哪个图提供了正确的信息。

在此处输入图片说明


1
分享您的研究成果对所有人都有帮助。告诉我们您尝试过的内容以及为什么它不能满足您的需求。这表明您已花时间尝试自我帮助,这使我们免于重复显而易见的答案,并且最重要的是,它可以帮助您获得更具体和相关的答案。另请参阅如何提出
蚊蚋

3
我正在学习Java ...,在所有材料中,他们只是说,每当我们创建一个对象时,都会向所有实例字段分配一个新的内存。不是
Harish_N 2014年

Answers:


13

方法的代码是Class(更简洁地说,Class<Student>)的一部分,并且在首次加载该类时将其加载到内存中。

也就是说,执行任何方法时,都会使用额外的内存,以将内存分配给参数,局部变量,临时表达式结果,返回值等。但是这种内存是在堆栈中分配(创建新实例时使用的内存是在堆中分配

根据您的问题,现在应该清楚图B是正确的(尽管它不能反映您实际调用该方法时发生的情况)。


好。我现在90%清楚了....但是有一个小疑问。.假设如果我创建10个类型为Student的对象,则仅将1组新鲜内存分配给Student类中的方法,而将10组新鲜内存分配给分配用于存储10个对象的实例变量..对吗?
Harish_N 2014年

对。认为不仅是属性会占用内存,而且与实例本身相关的开销也很小(没有属性的类的实例将使用超过0个字节的内存)。
SJuan76

还有一件事......我问的问题保持Java的考虑....做同样的事情也发生在Java中.....
Harish_N

Java语言规范没有说明何时以及出于什么目的分配多少内存。这留给实施者,每个实施者可以选择不同的内容。
约尔格W¯¯米塔格

6

实例字段(包括属性支持字段)将获得N个对象的N个副本。

静态字段每个类获得一个副本。

方法是字节码块(或在JIT之后,是本机指令块),它们是程序“映像”或可执行代码段的一部分。由于方法位于磁盘上,因此它已经成为程序映像的一部分。一旦操作系统(或CLR)加载了映像,就将有一个方法代码的共享副本。

它们通常不属于“堆”或运行时分配,除非您可以使用可托管的编译器动态编译新方法。方法不会像对象那样“分配”,并且相对于对象创建而言,它们也不是“分配”的。它们仅在实例化单个对象之前作为程序的一部分存在。即使是lambda /代表也不会被即时分配。编译器按需创建类以实现这些其他看似动态的代码对象,并且它们还作为磁盘上字节码映像的一部分存在。

每个评论的更新:

JVM标准有这样的说法:

2.5.4。方法范围

Java虚拟机具有一个在所有Java虚拟机线程之间共享的方法区域。该方法区域类似于常规语言的编译代码的存储区域,或者类似于操作系统过程中的“文本”段。它存储每个类的结构,例如运行时常量池,字段和方法数据,以及方法和构造函数的代码,包括用于类和实例初始化以及接口初始化的特殊方法(§2.9)。

方法区域是在虚拟机启动时创建的。尽管方法区域在逻辑上是堆的一部分,但是简单的实现可以选择不进行垃圾回收或压缩。此版本的Java虚拟机规范没有规定方法区域或用于管理已编译代码的策略的位置。方法区域可以是固定大小的,或者可以根据计算的需要进行扩展,如果不需要更大的方法区域,则可以缩小。方法区域的内存不必是连续的。

因此很明显,(1)是,该规范并不指示如何完成此操作,但(2)它类似于常规语言的已编译代码的存储区域,即。文本段。这就是我要说的重点。


您的意思是有道理的,但JLS确实可以保证吗?通常,JLS 在这种问题上给实施者许多回旋余地。
约尔格W¯¯米塔格

不确定,@JörgWMittag。也许你是对的。我试图说明的一点是“ new T()”没有分配方法的新实例。至于JVM的细节,类加载器确实确实将字节码存储在堆中,并且我猜可能存在实例化类本身甚至收集垃圾的情况。但这是运行时的实现细节,从概念上讲,我在说的堆是“用户”堆。在普通用户上下文中,类和方法不视为数据。但是由于我们也可以从用户区控制类加载器,所以我想我不知道。
codenheim 2014年

JLS甚至根本不谈论堆,不是吗?用动态堆栈,没有堆而不是固定大小固定的堆栈和动态堆来实现Java是完全合法的。JLS也没有说明JVM,在没有JVM的情况下实现Java是完全有效的。
约尔格W¯¯米塔格

您正在引用JLS,但是我在谈论JVM。JVM标准肯定会讨论堆。您必须提供一个变量范围/生存期,以使堆栈的本地范围转义。至于理论上可行的方法,我宁愿以“已知的实现”的方式来考虑。我很确定实现一个没有堆原语的完整JVM是一项艰巨的任务,甚至是不可能的,因为JVM并不是纯粹的堆栈计算机。我对Forth机器以及其他纯堆栈体系结构的理解是,如果存在用于随机变量访问的原语是可能的,但我只是没有看到它。
codenheim 2014年

@JörgWMittag-我在回答中添加了一些我们的讨论可能感兴趣的内容。关键是我正在画一个类似于传统运行时系统中的传统代码或文本段的类比。
codenheim 2014年

-4

对象分配在堆内存中。分配对象时,所有实例变量的插槽都会在销毁对象时创建并销毁。因此实例变量也分配在堆内存中。方法被调用。


1
这似乎只是重复先前答案中提供的信息。
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.