Java收集和内存优化

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

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

public class StringPool { private static WeakHashMap 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>> 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> hash = new HashMap(); messageIndex.add(hash); } // create index for every column for (int i = monitorModel.getParser().getMyMessages().getMessages().size() - 1; i >= 0; --i) { TreeSet 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); } } 

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

如果你确实发现字符串存储有问题,我会假设你要扩展你的应用程序超过500k字符串,或者特别严格的内存限制要求你重复删除甚至短字符串。

正如Dev所说, String.intern()将为你重复删除字符串。 但有一点需要注意 – 在Oracle和OpenJDK虚拟机中, String.intern()会将这些字符串存储在VM永久生成中,以便将来不会对它们进行垃圾收集。 如果符合以下条件,这是合适的(并且有用)

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

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

更新:我被告知JDK 7在主堆中存储了字符串( String.intern() ),而不像早期的JDK那样存储在perm-gen中。 如果你使用的是JDK 7,这会降低String.intern()风险。

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