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

Posted

技术标签:

【中文标题】如果声明但未初始化,对象的变量是不是使用内存空间?【英文标题】:Does a variable of an object use memory space if declared but not initialised?如果声明但未初始化,对象的变量是否使用内存空间? 【发布时间】:2019-12-11 00:59:48 【问题描述】:

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

只有少数(

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

这是我的代码示例(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


【问题讨论】:

【参考方案1】:

我有点不同意:

private float connection[];
private float bias;

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

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

在该成员字段中存储 0 或 100.00 或 10394.283 都没有关系:因为 JVM 确保您有足够的内存来容纳所需的位和字节。

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

当然,如果您的数组稍后会容纳 5 或 10 或 1000 个条目,那么这将构成您的大部分内存消耗。但最初,当您创建数百万个“空”Neuron 对象时,您必须为类中存在的每个字段“付费”。

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

一个 BaseNeuron 类,没有全部 4 个字段 一个或多个从该类派生的类,添加他们需要的字段

还要注意,从设计的角度来看,这也可能是更好的选择:“空”值总是意味着:额外的代码来处理它。含义:如果该数组可以为空......那么当然,所有处理该字段的代码都必须在使用它之前检查该数组是否为空。将其与:没有该数组的类与您知道该数组始终设置好并可以使用的类进行比较。

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

【讨论】:

【参考方案2】:

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

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

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

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

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


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

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

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


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

总而言之,在优化和改变我们的设计之前,我认为首先要做的是衡量并确定您是否要优化以获得金牌或坚果。

使用您的实际代码和产生 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 个月):

private String name;
private float propability;

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

【讨论】:

【参考方案3】:

嗯,是的,它们会有所不同,但根据您的用例/jvm 实施/硬件资源,这种差异的重要性尚有待商榷。如果您在具有 500mb 内存和 1 个 cpu 的服务器上运行您的应用程序,并且您的应用程序正在以高速率(随时间推移的对象数量)创建这些对象,并且垃圾收集器在该速率之后无法清理,那么最终是的你会遇到内存问题。因此,从技术上和 Java 语言规范来看,它们会占用内存,但实际上并根据您的用例,这可能不是任何问题。

【讨论】:

以上是关于如果声明但未初始化,对象的变量是不是使用内存空间?的主要内容,如果未能解决你的问题,请参考以下文章

第九章 内存模型和名称空间

JavaScript中Null和undefind区别

JS基本数据类型

java性能优化

C++学习:2类和对象

3CPP面向对象之—— 类对象(封装),继承