【问题标题】:Converting collection to array with no extra memory在没有额外内存的情况下将集合转换为数组
【发布时间】:2015-08-30 02:14:51
【问题描述】:

我需要将 Map 转换为 2D 数组,所以我编写了这段代码,但它占用了大量内存,我不知道为什么。

private DataItem[][] convertDataToArrays(boolean[] filter,
                                         Map<Integer, List<T>> dataSet)                           
        double[] data = new double[sizeOfNewVector];
        DataItem[][] reducedData = new DataItem[dataSet.size()][];
        for (int i = dataSet.size() - 1; i >= 0; i--) {
            reducedData[i] = new DataItem[dataSet.get(i).size()];
            for (int j = reducedData[i].length - 1; j >= 0; j--) {
                reducedData[i][(reducedData[i].length - 1) - j] = new DataItem(data);
                dataSet.get(i).remove(j);
            }
            dataSet.remove(i);
        }
        return reducedData;

这里是 DataItem 类:

public class DataItem {

    public double[] data;

    public DataItem(double[] data) {
        this.data = new double[data.length];
        System.arraycopy(data, 0, this.data, 0, data.length);
    }
}

算法应该做什么:

  1. 从列表中取出最后一个元素
  2. 复制它。
  3. 从列表中删除元素
  4. 将副本存储到新的二维数组中
  5. 重复直到列表为空

这应该适用于地图中的所有列表。

问题是,第 3 步只保留元素而不缩小数组,所以当我在转换方法中插入一个巨大的数据集时,我得到 java.lang.OutOfMemoryError: GC 开销限制超出

我需要在没有任何额外内存的情况下执行此操作。有人可以帮帮我吗?

编辑:

我正在使用 ArrayList 和 HashMap。

【问题讨论】:

  • 如果您删除原件,为什么需要复制DataItem 对象?只需将引用复制到结果数组中即可。请在您的情况下指定dataSet 有多大(您总共有多少DataItem 对象)。
  • 我删除了一个我修改“数据”的内部循环。它可以是更短的向量。这对我的问题并不重要,我不想在这里混淆人们。
  • 您将始终需要 一些 额外的内存用于该进程。我不知道,您为MapList 使用了哪些集合,但是每个 类型都会在某个时候缩小大小(尽管不是立即)。 HashMapArrayList 可能会比 TreeMapLinkedList 花费更长的时间,但在变化 LinkedList 开始时会有相当大的内存开销。我认为您的问题实际上是您的工作非常非常接近内存限制。如果你无法支付这里的开销并以某种方式逃脱它,它会在其他地方咬你。
  • 我已经编辑了我使用的集合。这很奇怪,我应该有 1 Gb 的空闲空间(考虑我的数据集有 4 Gb)
  • 另一件事:您确定第 3 步是您的问题吗?由于数据的主要部分似乎在那些大的DataItem 对象中,所以只有引用它们的列表才是问题所在,这真的很奇怪。您确定您没有在此处复制那些 DataItem 对象并且仍然在其他地方引用旧对象,所以最后您创建了 2 个数据副本?

标签: java arrays performance memory collections


【解决方案1】:

你的理论是完全可能的。 ArrayList 确实需要一段时间来缩小用于存储引用的内部数组的大小。你可以通过使用另一个List 实现来避免这种影响,比如LinkedList,它不会显示这种行为,但是它们也有相当大的内存开销,可能会占用你节省的空间。

话虽如此,鉴于您的数据结构,我发现仅ArrayList 中的一些额外引用的开销不太可能将您的内存需求推到最高点。我发现您更有可能创建所有 DataItem 类型的对象的副本,这些对象显然相对较大(从内部数组判断)。如果其他人仍有对原始 DataItem 对象的引用,您对 remove 的调用将从列表中删除他们的引用,但对象本身会一直存在直到所有对它们的引用都被删除了。

我建议检查您的内存占用情况,并使用类似MAT tool 之类的实际工作的较小示例。查看在转换之前之后有多少DataItem 类型的对象。如果它们增加了,我的理论是正确的,你应该避免这个问题,要么不复制对象,而只复制它们的引用(如果可以的话),或者摆脱对旧对象的附加引用。如果我的理论是错误的,请检查内存的哪一部分增加最多以找出罪魁祸首。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-03
    • 2019-11-09
    • 2017-09-14
    • 1970-01-01
    • 2016-07-11
    • 1970-01-01
    • 1970-01-01
    • 2017-09-04
    相关资源
    最近更新 更多