3

我为自定义表编写了一个自定义索引,该表使用 500MB 的堆来存储 500k 个字符串。只有 10% 的字符串是唯一的;其余的都是重复的。每个字符串的长度为 4。

我如何优化我的代码?我应该使用另一个集合吗?我试图实现一个自定义字符串池来节省内存:

public class StringPool {

    private static WeakHashMap<String, String> map = new WeakHashMap<>();

    public static String getString(String str) { 
        if (map.containsKey(str)) {
            return map.get(str);
        } else {
            map.put(str, str);
            return map.get(str);
        }
    }
}

private void buildIndex() {
        if (monitorModel.getMessageIndex() == null) {
            // the index, every columns create an index
            ArrayList<HashMap<String, TreeSet<Integer>>> messageIndex = new ArrayList<>(filterableColumn.length);
            for (int i = filterableColumn.length; i >= 0; i--) {
                // key -> string,   value -> treeset, the row wich contains the key
                HashMap<String, TreeSet<Integer>> hash = new HashMap<>();
                messageIndex.add(hash);
            }
            // create index for every column
            for (int i = monitorModel.getParser().getMyMessages().getMessages().size() - 1; i >= 0; --i) {
                TreeSet<Integer> tempList;

                for (int j = 0; j < filterableColumn.length; j++) {
                    String value  = StringPool.getString(getValueAt(i, j).toString());
                    if (!messageIndex.get(j).containsKey(value)) {
                        tempList = new TreeSet<>();
                        messageIndex.get(j).put(value, tempList);
                    } else {
                        tempList = messageIndex.get(j).get(value);
                    }

                    tempList.add(i);
                }
            }
            monitorModel.setMessageIndex(messageIndex);
        }
    }
4

2 回答 2

5

无需提出自定义池。只需使用String.intern().

于 2012-08-15T17:21:13.120 回答
4

您可能想在分析器中检查您的内存堆。我的猜测是内存消耗主要不是在字符串存储中,而是在许多TreeSet<Integer>情况下。如果是这样,您可以通过使用原始数组(int[]short[]byte[],具体取决于您存储的整数值的实际大小)进行大幅优化。或者您可以查看原始集合类型,例如FastUtilTrove提供的集合类型。

如果您确实发现字符串存储存在问题,我会假设您希望将应用程序扩展到超过 500k 字符串,或者特别严格的内存限制要求您对短字符串进行重复数据删除。

正如 Dev 所说,String.intern()将为您删除重复的字符串。然而,有一个警告——在 Oracle 和 OpenJDK 虚拟机中,String.intern()会将这些字符串存储在 VM 永久代中,这样它们将来就不会被垃圾收集。如果:

  1. 您存储的字符串在 VM 的整个生命周期中都不会改变(例如,如果您在启动时读取静态列表并在应用程序的整个生命周期中使用它)。
  2. 您需要存储的字符串可以舒适地适合 VM 永久代(为类加载和 PermGen 的其他使用者提供足够的空间)。更新:见下文。

如果其中任何一个条件为假,则构建自定义池可能是正确的。但我的建议是你考虑一个简单HashMap的代替WeakHashMap你目前使用的。您可能不希望这些值在缓存中时被垃圾收集,并WeakHashMap添加另一个级别的间接(以及关联的对象指针),从而进一步增加内存消耗。

更新:有人告诉我 JDK 7 将实习字符串 ( String.intern()) 存储在主堆中,而不是像早期的 JDK 那样存储在 perm-gen 中。String.intern()如果您使用的是 JDK 7,那么风险会更小。

于 2012-08-15T18:13:20.427 回答