在Java中,在没有初始化的情况下声明类级实例变量会花费内存吗?
例如:如果我没有使用int i;
对其进行初始化,i = 5;
会使用任何内存吗?
详细说明:
我有一个巨大的超级类,许多不同的(没有足够的不同,有自己的超类)子类扩展。有些子类不使用超类声明的每一个原语。我可以简单地将这些原语保留为未初始化,并仅在必要的子类中初始化它们以节省内存吗?
答案 0 :(得分:72)
您的类中定义的所有成员都具有默认值,即使您没有显式初始化它们,因此它们也会使用内存。
例如,默认情况下,每个int
都会初始化为0
,并且会占用4
个字节。
对于班级成员:
int i;
与:
相同int i = 0;
以下是JLS关于实例变量的说法:
如果类T的字段a是实例变量,则创建新的实例变量a并将其初始化为默认值 (§4.12.5),作为每个新创建的对象的一部分T类或任何类的子类T(§8.1.4)。在完成对象(第12.6节)的任何必要的最终化之后,当实例变量不再被引用时,实例变量将不再存在。
答案 1 :(得分:17)
是的,虽然您没有为其分配任何值,但内存会分配。
int i;
需要32 bit
内存(分配)。无论你是否使用它。
有些子类不使用超类声明的每个单元。我可以简单地将这些原语保留为未初始化,只在必要的子类中初始化它们以节省内存吗?
同样,无论你在哪里初始化,内存都会分配。
只需要注意的是,只需找到未使用的基元并将其删除即可。
修改强>
再添加一个不同于原始引用的点,默认值为null
,它带有
4 bytes(32-bit)
8 bytes on (64-bit)
答案 2 :(得分:11)
最初的问题是关于类级别的变量,答案是他们确实使用了空间,但是看看方法范围的变量也很有趣。
我们举一个小例子:
public class MemTest {
public void doSomething() {
long i = 0; // Line 3
if(System.currentTimeMillis() > 0) {
i = System.currentTimeMillis();
System.out.println(i);
}
System.out.println(i);
}
}
如果我们看一下生成的字节码:
L0
LINENUMBER 3 L0
LCONST_0
LSTORE 1
好的,正如预期的那样,我们在代码中的第3行分配一个值,现在如果我们将第3行更改为(并因编译器错误而删除第二个println):
long i; // Line 3
...并检查字节码然后没有为第3行生成任何内容。因此,答案是此时没有使用内存。事实上,当我们分配给变量时,LSTORE仅在第5行发生。因此,声明未分配的方法变量不会使用任何内存,实际上也不会生成任何字节码。它相当于在您首次分配声明时进行声明。
答案 3 :(得分:6)
是。在您的班级中,即使您没有初始化,变量也会指定为默认值。
在这种情况下,您int
个变量将分配给0
,每个变量将占用4 bytes
。
答案 4 :(得分:4)
Java语言规范和Java虚拟机规范都没有指定答案,因为它是一个实现细节。事实上,JVMS §2.7 specifically says:
对象的表示
Java虚拟机不要求任何特定的对象内部结构。
理论上,符合要求的虚拟机可以使用一组位标记来实现具有大量字段的对象,以标记哪些字段已设置为非默认值。最初不会分配任何字段,标志位将全为0,并且对象将很小。首次设置字段时,相应的标志位将设置为1,并且将调整对象的大小以为其腾出空间。 [垃圾收集器已经提供了必要的机制,用于暂时停止运行代码,以便在堆周围重新定位活动对象,这对于调整它们来说是必要的。]
在实践中,这不是一个好主意,因为即使它节省了内存,也是复杂而缓慢的。访问字段需要临时锁定对象以防止由于多线程而导致损坏;然后读取当前标志位;如果该字段存在,则计算设置位以计算所需字段相对于对象基数的当前偏移量;然后阅读该领域;最后解锁对象。
因此,没有通用的Java虚拟机可以做这样的事情。一些具有过多字段的对象可能会从中受益,但即使它们也不能依赖它,因为它们可能需要在不能这样做的公共虚拟机上运行。
在首次实例化对象时为所有字段分配空间的平面布局简单快速,因此这是标准。程序员假设对象以这种方式分配,因此相应地设计他们的程序以最好地利用它。同样,虚拟机设计人员也会进行优化以快速实现这种使用。
最终,字段的平面布局是惯例,而不是规则,尽管无论如何你都可以依赖它。
答案 5 :(得分:2)
在Java中,当您声明类属性String str;
时,您声明对对象的引用,但它没有指向任何对象,除非您影响它的值str=value;
。但正如您可能猜到的那样,即使没有指向内存位置,引用也会占用一些内存。