为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

摇曳的蔷薇 2023-05-10 15:23:08
我有一个班级,我想使用大约 10 到 10 万个。因此,我不想不必要地浪费内存位置。只有少数(<100)我需要 2 个特殊变量。如果我只是声明它们,但不初始化它们,我是否需要与初始化它们相同的内存使用量?如果是的话,我是否有另一种选择(除了让它们成为自己的类之外)来减少内存使用?这是我的代码示例(我name只propability需要几次):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}
查看完整描述

3 回答

?
狐的传说

TA贡献1804条经验 获得超3个赞

我不得不有点不同意:

private float connection[];
private float bias;

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

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

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

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

当然,如果您的数组稍后将包含 5、10 或 1000 个条目,那么这将弥补您的大部分内存消耗。但是最初,当您刚刚创建数百万个“空”Neuron对象时,您必须为类中存在的每个字段和任何字段“付费”。

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

  • BaseNeuron没有所有 4 个字段的类

  • 从该类派生的一个或多个类,添加他们需要的字段

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

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


查看完整回答
反对 回复 2023-05-10
?
忽然笑

TA贡献1806条经验 获得超5个赞

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

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

在执行构造函数之前初始化所有实例字段(so nameand too)。propability这些是用默认值初始化的:

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

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


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

如果是的话,我是否有另一种选择(除了让它们成为自己的类之外)来减少内存使用?

如果这些对象有一些公共数据,则在实例之间共享这些数据。依赖共享数据不变性的轻量级模式说明了这种
做法
对象String使用它。


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

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

使用您的实际代码和main()产生 100 万个神经元的方法,我注意到大约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 万。

如果该类声明了十几个未使用的字段,情况就会有所不同。将浪费不可忽略的内存量,并且整个类在设计方面根本不明确:低内聚性。

但事实并非如此。


查看完整回答
反对 回复 2023-05-10
?
弑天下

TA贡献1818条经验 获得超8个赞

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



查看完整回答
反对 回复 2023-05-10
  • 3 回答
  • 0 关注
  • 178 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信