int i;
如果我不使用i = 5;
进行初始化,那么它是否会使用任何内存?细节:我有一个非常庞大的超类,许多不同的子类(不足以拥有自己的超类)是扩展的。某些子类不使用超类声明的每个原始类型。我是否可以将这些未初始化的基元保留为仅在必要的子类中初始化以节省内存?
int i;
如果我不使用i = 5;
进行初始化,那么它是否会使用任何内存?null
。翻译:@spoko 原始数据类型和类不同。原始数据类型默认会分配内存,但对于引用类型则不然。引用类型的默认值是 null
。 - Suresh Atta是的,即使您没有分配任何值,内存仍然会分配。
int i;
它需要占用 32 位
的内存(分配),不管你是否使用它。
有些子类不会使用超类声明的每一个原始类型,我可以简单地将这样的原始类型保持未初始化,并且仅在必要的子类中进行初始化以节省内存吗?
同样地,无论您在何处初始化,都会分配内存。
唯一需要注意的是找到未使用的原始类型并将其删除。
编辑:添加一个更重要的点,即与原始类型不同,默认情况下引用值为null
,它占用了内存。
4 bytes(32-bit)
8 bytes on (64-bit)
原始问题讨论了类级别变量,答案是它们确实使用空间,但查看方法范围的变量也很有趣。
我们来看一个小例子:
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行不会生成任何内容。因此,在这一点上不使用任何内存。实际上,只有在第5行赋值给该变量时才会发生 LSTORE 操作。因此,声明未赋值的方法变量不会使用任何内存,并且实际上不会生成任何字节码。它相当于在首次分配变量时进行声明。
是的。即使您不初始化它们,类级变量也会分配其默认值。
在这种情况下,您的int
变量将被赋值为0
,每个变量占用4字节
。
对象的表示
Java虚拟机不强制要求对象具有任何特定的内部结构。
理论上,符合规范的虚拟机可以使用一组位标志来实现具有许多字段的对象,以标记已设置为非默认值的字段。最初不会分配任何字段,标志位将全部为0,对象将很小。当第一次设置字段时,相应的标志位将被设置为1,并且对象将被调整大小以为其腾出空间。[垃圾收集器已经提供了必要的机制,以暂停运行中的代码,以便在堆中重新定位活动对象,这对于调整其大小是必要的。]
实际上,这并不是一个好主意,因为即使它节省了内存,它也很复杂和缓慢。访问字段需要临时锁定对象以防止多线程导致的损坏;然后读取当前标志位;如果字段存在,则计算所需字段相对于对象基址的当前偏移量来计算;然后读取字段;最后解锁对象。在Java中,当您声明类属性(例如String str;
)时,您声明了一个对象的引用,但除非将值赋给它str=value;
,否则它尚未指向任何对象。但是,正如您可能猜到的那样,即使没有指向内存位置,引用本身也会消耗一些内存。