如果声明但未初始化,对象的变量是否使用内存 space?

Does a variable of an object use memory space if declared but not initialised?

我有一个class,我想使用大约十到十万个。因此,我不想不必要地浪费内存位置。

只有少数(<100)我需要 2 个特殊变量。如果我只是声明它们,但不初始化它们,我是否需要与初始化它们相同的内存使用量?

如果是,我是否有其他选择(除了让它们成为自己的class)来减少内存使用?

这是我的代码示例(namepropability 我只需要几次):

public class Neuron {
    
  private String name;
  private float propability;
  private float connection[];
  private float bias;

  public Neuron(float[] connection, float bias) {
    this.connection = connection;
    this.bias = bias;
  }

  public Neuron(int propability, String name, float[] connection, float bias) {
    this.name = name;
    this.propability = propability;
    this.connection = connection;
    this.bias = bias;
  }

    //more code

}

即使没有在构造函数中使用:

public Neuron(float[] connection, float bias) {
    this.connection = connection;
    this.bias = bias;
}

所有实例字段(因此 namepropability 也是)在执行构造函数之前初始化。 这些是用默认值初始化的:

private String name; // null
private float propability; // 0F

但是这些默认值没有任何成本(null0)。
所以不要为此烦恼。


I have a Class which I want to use some ten to 100 thousands of. Therefor I don't unnecessarily wanna waste memory location for.

And if yes, do I have another option (besides making them an own class) to reduce memory usage?

如果这些对象有一些共同的数据,在实例之间共享这些数据。
依赖共享数据不变性的 flightweight 模式说明了这种做法。
The String objects use that.


完全同意GhostCat的说法:不使用的字段占用内存。不多,但他们消耗。但这对 Java.
中的许多 classes 来说是正确的 例如,我们不会用数组替换所有列表,因为数组通常消耗较少。我们将这样做是因为在我们的特定情况下,列表内存占用是一个 真正 的担忧。

综上所述,在优化和改变我们的设计之前,我认为首先要做的是衡量和判断你是要优化以获得金子还是坚果。

使用您的实际代码和产生 100 万个神经元的 main() 方法,我注意到消耗了大约 131 Mo

  public static void main(String[] args) {
    long beforeUsedMem=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
    List<Neuron> neurons = new ArrayList<>();
    for (int i = 0; i < 1_000_000; i++) {
      neurons.add(new Neuron(new float[] { 0, 15.4F, 1.1F, 2.1F, 3.4F, 4.5F, 8.9F, 158.9F, 8648.9F, 80.9F, 10.9F, 1450.9F, 114.9F, 14.5F, 4444.4F }, 1.9F));
    }
    long afterUsedMem=Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory();
    long actualMemUsed=(afterUsedMem-beforeUsedMem)/1_000_000;
    System.out.println(actualMemUsed + " Mo");
  }

客观来说,偏低。

通过删除未使用的字段,它下降到 124 Mo(更少 7 Mo):

private String name;
private float propability;

131 Mo 和 124 Mo 对于如此多的创建对象来说是非常低的值:100 万。
如果 class 声明了十几个未使用的字段,情况就会有所不同。将浪费不可忽略的内存量,总体而言 class 在设计方面根本不清楚:低内聚。
但事实并非如此。

嗯,是的,它们会有所不同,但根据您对 case/jvm implementation/hardware 资源的使用情况,这种不同有多大值得商榷。如果您 运行 在具有 500mb 内存和 1 cpu 的服务器上运行您的应用程序并且您的应用程序正在以高速率创建这些对象(随着时间的推移对象数量)并且垃圾收集器无法在该速率之后进行清理,然后是的,最终你会 运行 进入内存问题。因此,从技术上讲,根据 Java 语言规范,它们会占用内存,但实际上,根据您的用例,这可能不是任何问题。

我有点不同意:

private float connection[];
private float bias;

第一个(数组)是 reference 类型。换句话说:指向某个内存区域的(潜在)指针。 显然:只要那个指针指向null("nowhere"),就不需要额外的内存。

但是请不要误会,您的对象本身需要适合内存。意思是:当你实例化一个新的 Neuron 对象时,JVM 请求的内存量正好是存储 Neuron 对象所需的内存量。这意味着:分配了一些内存以适应该数组引用,当然:您的 float 原始值的内存,它们都会立即分配。

在该成员字段中存储的是 0、100.00 还是 10394.283 并不重要:因为 JVM 确保您有足够的内存来容纳所需的位和字节。

因此:当您确实有 百万 个对象时,对象中的每个浮点字段都会增加 32 位。无论该字段中的值来自何处。

当然,如果您的数组稍后将容纳 5、10 或 1000 个条目,那么这将弥补您的大部分内存消耗。但最初,当您刚刚创建数百万个 "empty" Neuron 对象时,您必须 "pay" 为 class.

中存在的每个字段。

含义:当 100 个 Neuron 对象中只有 1 个需要这两个字段时,您 可以 决定:

  • A BaseNeuron class 没有全部 4 个字段
  • 一个或多个 class 派生自 class,添加他们需要的字段

另请注意,从设计的角度来看,这也是更好的选择:"empty" 值始终意味着:额外的代码来处理它。意思是:如果该数组可以为空……那么当然,所有处理该字段的代码都必须在使用它之前检查该数组是否为空。将其与以下内容进行比较:class 没有该数组,而 class 您 知道 数组始终已设置并准备好使用。

我并不是说你绝对必须改变你的设计,但正如所解释的:你可以减少你的内存占用,并且你可以通过这样做使你的设计更清晰。