【问题标题】:Prevent memory allocation for NULL references in java防止在java中为NULL引用分配内存
【发布时间】:2015-05-12 09:54:14
【问题描述】:

团队,

我们所知道的是 NULL 引用将在 32 位机器中占用 4 字节的堆内存。

假设我有数百万个用于下面这个类的对象,但只有一些索引填充了有效对象。让我们采取最坏的情况,即一个索引填充了 255。因此,为 NULL 引用分配了 (1,000,000 * 254 * 4) 个字节,这对于 1GB RAM 机器来说是昂贵的。

Class A{
  A [] arrayRef = new A [255];
}

Class A{
  A ref1, ref2, ref3, ... ref255;
}

任何项目有什么用;它保存了数百万个 NULL 引用并为此分配了 400 万个字节。

所以

  1. 在 java/jvm 中有没有办法创建惰性空引用内存?是指在使用 new 关键字为其分配有效对象之前不为 NULL 引用分配内存?
  2. 这种为未初始化的引用分配内存在 C、javascript 等所有语言中是否常见?任何语言都反对这个概念?

关于地图的另一个用例;假设 Map 的大小已经增长了一千万,然后在运行时缩小到一万,所以初始化的 Map Array 大小将保持原样,空引用会占用内存;可以优化。

最后为所有反对的选民

如果 NULL 引用不需要内存;那么 HashMap 总是可以用 Array[Integer.MaxSize] 初始化;然后在不因掩蔽而重新散列和冲突的情况下查找桶

更新:

看起来 Array 在使用之前需要顺序内存来满足其大小。因此,尽管我们在其中有 NULL 引用,但大多数编程语言都无法阻止分配内存。 但我不明白为什么是一样的;就类引用而言也是预期的??

【问题讨论】:

  • 这段代码看起来令人不安。
  • 这不是特别大的内存量。为什么不使用数组或 Java 集合之一?
  • @Gergely Bacso 我没明白你说的打扰是什么意思..?
  • 行为相同,但代码更简单,更易于管理。
  • @KanagaveluSugumar 好吧,您的问题是 100 个对象,而不是 10000 个。即使是 10000 个,根据您的数字也只有 10MB。也许您可以准确地描述您的应用程序实际上想要做什么。过早的优化是不成熟的。

标签: java memory memory-management jvm


【解决方案1】:

您一开始就不应该使用 256 个字段。相反,您应该使用数组。

class A
{
  A ref = new A[256];
}

整个数组的大小被初始化为 256,所有值都设置为null

这可能会多出一些字节,但这应该不会有什么坏处——这真的不应该是个问题。


从这里开始,我们可以解决您的问题:如果有 256 个“空”插槽确实是个问题,并且您确信大多数时候这 256 个插槽中实际上只有很小一部分是使用时,您可以切换到一种技术,您可以使用必须自己实现的稀疏数组之类的技术 - 或者您只需使用 Map<Integer, A> 在索引和插槽之间进行映射。

您只需要注意以适当的方式处理缺失值。

所以你可以这样做

class A
{
    Map<Integer, A> ref = new HashMap<>();
    void setValue(int idx, A value) {
        if(value==null) {
            ref.remove(idx);
        } else {
            ref.put(idx, value);
        }
    }
    A getValue(int idx) {
        return ref.get(idx);
    }
    Optional<A> getOptionalValue(int idx) {
        return Optional.ofNullable(ref.get(idx));
    }
}

但我可以想象,除非您使用 数百万A 对象,否则这种方法总体上会更糟。

回答您的其他问题:

所描述的方法是我能想到的实现这一目标的唯一方法。

特别是,您无法阻止引用分配它需要的内存来保存任何引用,即使它当前只保存一个空引用。

这对于大多数其他语言来说很常见,尤其是当您处理使用数组的上述概念时。 (相信我,您不想要 256 个单独的字段,没有任何语言。)但是其他(高级)语言也有类似 Java 的 Map,例如 Python 的 dict可以添加键/值对,然后可以再次检索。

【讨论】:

  • 谢谢!!至少你得到了回应。但是我的问题没有解决。如果我只初始化了一些 NULL 引用,这将为 NULL 引用提供更多内存。
  • @KanagaveluSugumar 100 的 A 对象将占用 100KB 的内存,这是 10GB 机器总 RAM 的 0.005%。 RAM 使用量可以忽略不计。不要过早优化。
  • @ColonelThirtyTwo 谢谢!但是对于 1GB RAM 来说,0.05% 的成本很高,而且会超过 100 个对象。也许我会重新提出我的问题。
【解决方案2】:

您可以使用参考列表,例如

final List<A> refList = new ArrayList<A>();

这样您从 0 个引用开始,随着您添加每个引用,列表会根据需要增长。

【讨论】:

  • 谢谢!!但我想知道/维护我已初始化的引用/属性的基数/基数,例如,如果我想初始化类的第 255 个引用/属性,那么将初始化第 255 个索引的 ArrayList/Array,其余的数组在 arrayList 中将为 NULL。你能告诉我有没有办法可以避免在 JVM 中为 NULL 引用创建内存?或者有其他语言支持吗?
  • @KanagaveluSugumar 您可以创建这样的数据结构,但我怀疑除非您的设备内存非常低,否则不值得付出努力。我的 1 美分是 250KB 的 PC 内存值 1 美分,你应该花适当的时间优化内存使用。
  • 值得,如果可以的话,HashMap 总是可以初始化 Array[Integer.MaxSize];然后在不因掩蔽而重新散列和冲突的情况下进行存储桶查找。
  • @KanagaveluSugumar HashMap 可以做到这一点,但除非你非常小心并且只有几个条目,否则它将使用更多的内存。假设 Integer 被缓存,HashMap 每个引用使用接近 32 个字节。您只需要触摸最大容量的 1/8 即可最终使用更多内存而不是更少。
  • @KanagaveluSugumar 我不明白你的问题。查找不会改变 HashMap,冲突是通过 Java 7 中的链表和 Java 8 中的树来处理的。
猜你喜欢
  • 2011-11-03
  • 1970-01-01
  • 1970-01-01
  • 2016-03-15
  • 1970-01-01
  • 1970-01-01
  • 2018-01-23
相关资源
最近更新 更多