5

我已经实现了一个简单的B-Tree,它将 long 映射到 int。现在我想使用以下方法估计它的内存使用情况(仅适用于 32 位 JVM):

class BTreeEntry {

    int entrySize;
    long keys[];
    int values[];
    BTreeEntry children[];
    boolean isLeaf;
    ...
    /** @return used bytes */
    long capacity() {
        long cap = keys.length * (8 + 4) + 3 * 12 + 4 + 1;
        if (!isLeaf) {
            cap += children.length * 4;
            for (int i = 0; i < children.length; i++) {
                if (children[i] != null)
                    cap += children[i].capacity();
            }
        }
        return cap;
    }
}
/** @return memory usage in MB */
public int memoryUsage() {
    return Math.round(rootEntry.capacity() / (1 << 20));
}

但是我尝试了例如 7mio 条目,并且 memoryUsage 方法报告的值比 -Xmx 设置允许的值高得多!例如,它说 1040 (MB),我设置了 -Xmx300!JVM 是否能够以某种方式优化内存布局,例如。对于空数组或我的错误可能是什么?

更新 1:好的,引入 isLeaf 布尔值大大减少了内存使用量,但仍不清楚为什么我观察到的值高于 Xmx。(您仍然可以通过对所有构造函数使用 isLeaf == false 来尝试此操作)

更新2:嗯,有些事情很不对劲。当增加每个叶子的条目时,会假设内存使用量减少(当对两者都进行压缩时),因为较大的数组涉及较少的引用开销(并且 btree 具有较小的高度)。但是如果我使用 500 而不是每片叶子 100 个条目,则 memoryUsage 方法会报告增加的值。

4

1 回答 1

0

哦,嘘……新鲜空气解决了这个问题;)

当条目已满时,它将被拆分。在我原来的拆分方法checkSplitEntry中(我想避免浪费内存)我犯了一个很大的内存浪费错误:

// left child: just copy pointer and decrease size to index
BTreeEntry newLeftChild = this;
newLeftChild.entrySize = splitIndex;

这里的问题是,旧的子指针仍然可以访问。因此,在我的 memoryUsage 方法中,我数了一些孩子两次(尤其是当我没有压缩时!)。所以,如果没有这个技巧,一切都会好起来的,我的 B-Tree 将更加高效,因为垃圾收集器可以完成它的工作!

于 2013-04-09T10:16:38.277 回答