Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/string/5.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
在Java中优化字符串集合的内存使用_Java_String_Memory Management - Fatal编程技术网

在Java中优化字符串集合的内存使用

在Java中优化字符串集合的内存使用,java,string,memory-management,Java,String,Memory Management,我有大量的名称-值对(大约100k)需要存储在某种缓存中(比如哈希映射),其中的值是一个平均大小约为30k字节的字符串 现在我知道了一个事实,大量的值具有完全相同的字符串数据。为了避免多次分配相同的字符串数据,我希望以某种方式重用以前分配的字符串,从而消耗更少的内存。此外,这需要相当快。i、 e.逐个扫描之前分配的所有值不是一个选项 关于如何解决此问题的任何建议?将在这里(很可能)帮助您。它将同一字符串的多个实例解析为一个副本 编辑:我建议这“很可能”会有帮助。在什么样的情况下不会发生?暂存字符

我有大量的名称-值对(大约100k)需要存储在某种缓存中(比如哈希映射),其中的值是一个平均大小约为30k字节的字符串

现在我知道了一个事实,大量的值具有完全相同的字符串数据。为了避免多次分配相同的字符串数据,我希望以某种方式重用以前分配的字符串,从而消耗更少的内存。此外,这需要相当快。i、 e.逐个扫描之前分配的所有值不是一个选项

关于如何解决此问题的任何建议?

将在这里(很可能)帮助您。它将同一字符串的多个实例解析为一个副本

编辑:我建议这“很可能”会有帮助。在什么样的情况下不会发生?暂存字符串将具有永久存储这些暂存字符串表示的效果。如果问题域是一次性过程,则这可能不是问题。如果这是一个长时间运行的过程(例如web应用程序),那么您很可能会遇到问题


我会犹豫说永远不要实习(我会犹豫说什么都不要做)。然而,也有不理想的情况。

String.intern
显然是布赖恩所说的选择。但是,如果您不想在内存中的所有字符串上实习,可以使用集合来首先查看值是否存在。这是未经测试的代码。当从主地图删除时,您必须计算从反向地图删除

  class Map2<K, V> implements Map<K, V>
  {
    Map<K, V> _map = Maps.newHashMap();
    Set<V, V> _rev = Maps.newHashMap();

    V put(K k, V v) {
      if (_rev.containsKey(v)) {
        V prev = _rev.get(v);
        return _map.put(k, prev);
      } else {
        _rev.put(v, v);
        return _map.put(k,v);
      }
   }
类Map2实现Map
{
Map _Map=Maps.newHashMap();
Set _rev=Maps.newHashMap();
V摆(K,V){
如果(_rev.containsKey(v)){
V prev=_rev.get(V);
返回映射put(k,prev);
}否则{
_修订本(v,v);
返回映射put(k,v);
}
}
不要使用String.intern(多年来一直存在与此相关的各种内存问题)。相反,创建您自己的缓存,类似于String.intern。基本上,您需要一个映射,其中每个键都映射到自身。然后,在缓存任何字符串之前,您需要“intern”它:

private Map myInternMap=new WeakHashMap();
公共字符串实习生(字符串值){
已同步(myInternMap){
WeakReference curRef=myInternMap.get(值);
字符串曲线值=((curRef!=null)?curRef.get():null);
if(曲线值!=null){
返回曲线值;
}
myInternMap.put(值,新WeakReference(值));
返回值;
}
}

注意,对键和值使用weakreference,这样就不会保留对不再使用的字符串的引用。

这在某种程度上取决于如何创建
字符串

一种可能的方法是使用
TreeSet
,它使用
比较器
,可以比较现有的
字符串和新的
字符串的源。使用
SortedSet.tailSet
迭代器
查找现有的
字符串
。或者
NavigableSet.天花板/地板
TreeMap
具有类似设置

我写了一篇关于缓存不可变对象(特别是字符串)的另一种技术的文章,但这更适合于较小的对象


String.intern
存在性能问题。

同意其他人不使用String.intern():一旦将字符串放在那里,它将永远不会消失。查看Xerces的早期版本,了解这是一个坏主意的原因

更好的解决方案是使用WeakHashMap,将值包装在WeakReference中:

private Map<String,WeakReference<String>> _map 
    = new WeakHashMap<String,WeakReference<String>>();

public synchronized String intern(String str)
{
    WeakReference<String> ref = _map.get(str);
    String s2 = (ref != null) ? ref.get() : null;
    if (s2 != null)
        return s2;
    str = new String(str);
    _map.put(str, new WeakReference(str));
    return str;
}
private Map\u Map
=新WeakHashMap();
公共同步字符串实习生(字符串str)
{
WeakReference ref=\u map.get(str);
字符串s2=(ref!=null)?ref.get():null;
如果(s2!=null)
返回s2;
str=新字符串(str);
_map.put(str,新WeakReference(str));
返回str;
}
这段代码来自Java引用对象上的一个实例


编辑:需要在这里创建一个新字符串(我会更新文章),因为原始字符串可能是一个大得多的字符数组的子字符串。我认为JDK1.3已经解决了这个问题,但显然不是(至少不是在1.5中).

您可以压缩字符串。30K字符串应具有良好的压缩比。我编写了一个压缩大字符串的技巧作为练习,但您可以使用压缩数据的字节[]来存储字符串


30K字符串将使用约60KB(每个字符2个字节),因此即使使用getBytes()

< p>你是否真的需要<强>字符串,或者你只需要任何旧的“强>字符序列?如果没有,那么考虑实现一个如我在链接中建议的一个。

String。实习生可以相当慢。它也将字符串放置到永久生成中,这可以很好地导致GC性能。nce问题。当然,永久生成是一个问题。这个问题没有使用它的上下文。如果它是一个独立的应用程序,那么它很可能没问题。否则(比如一个正在运行的web应用程序),那么就不算了。一如既往,解决方案需要在使用它们的环境中进行评估。@Brian Agnew:我建议你编辑并扩展你的答案,然后包括环境?如果你明白我的意思,评论不算。ConcurrentMap有putIfAbsent,这可能很有用。我喜欢这个解决方案,它不会过度使用弱引用等来优化在存储方面,考虑到总数很小,可以只搜索映射中的现有值(比如@Ingo:搜索1000个值而不是执行查找是个坏主意。最初的问题涉及100k个名称-值对。弱引用非常关键。此解决方案在删除映射时会泄漏内存(k,oldv),即使将其替换为(k,newv),因为_rev保留了对oldv的引用。并且您不能删除oldv,因为它可能是必需的
private Map<String,WeakReference<String>> _map 
    = new WeakHashMap<String,WeakReference<String>>();

public synchronized String intern(String str)
{
    WeakReference<String> ref = _map.get(str);
    String s2 = (ref != null) ? ref.get() : null;
    if (s2 != null)
        return s2;
    str = new String(str);
    _map.put(str, new WeakReference(str));
    return str;
}