手动实现内存泄漏的 MultiMap

在这篇文章里,我将描述一个遇到的一个低级的内存泄漏的问题

原生的 java6 和 7

仅用到了原生的JDK,无任何第三方依赖包。

假设你定义了 Map<String, Set<String>> 这样的一个变量. 变量 value 的类型只要求是一个 Collection 就行。

下面的方法初始化了一个  Map<String, Set<String>>:

1 2 3 4 5 6 7 8 9 10 11 12

private final Map<String, Set<String>> m_ids = new HashMap<String, Set<String>>( 4 );   
private Set getSetByNameFaulty( final String id ) {     
    Set res = m_ids.get( id );     
    if ( res == null )     {         
        res = new HashSet( 1 );         
        m_ids.put( id, res );    
    }     
    return res; 
}

下面方法检查了 map 中对应的 key 是否有值,如果有,直接返回 set,如果没有,则新建一个 set 并存到 map 中

1 2 3 4 5 6 7

private void populateJava67() {     
    getSetByNameFaulty( "id1" ).add( "property1" );     
    getSetByNameFaulty( "id1" ).add( "property2" );     
    getSetByNameFaulty( "id1" ).add( "property3" );     
    getSetByNameFaulty( "id2" ).add( "property1" ); 
}

接下来写一段能访问到 map 中数据的代码:

1 2 3 4

private boolean hasPropertyFaulty( final String id, final String property ) {     
    return getSetByNameFaulty( id ).contains( property ); 
}

这个方法在代码质量监测工具中没什么问题。 但是,它是有致命缺陷的: 如果你查询了一个不存在的Key,getSetByNameFaulty 这个方法会在 map 中创建一个空的 set. 这绝对有问题的. 我们应该控制 getSetByNameFaulty 何时写入数据

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28

private Set getSetByName( final String id, final boolean isWrite ) {     
    Set res = m_ids.get( id );     
    if ( res == null )     {         
        if ( isWrite ) {             
            res = new HashSet( 1 );             
            m_ids.put( id, res );         
        } else {
            res = Collections.emptySet();     
        }
    }     
    return res; 
}   
private boolean hasProperty( final String id, final String property ) {     
    return getSetByName( id, false ).contains( property ); 
}   
private void populateJava67Better() {     
    getSetByName( "id1", true ).add( "property1" );     
    getSetByName( "id1", true ).add( "property2" );     
    getSetByName( "id1", true ).add( "property3" );     
    getSetByName( "id2", true ).add( "property1" ); 
}

Java 8

java 8 在 Map 接口中新增了下面的方法(实际上还新增了很多方法):

1

V getOrDefault(Object key, V defaultValue)

这个方法返回了 map 中的value,如果map中不存在key,则返回默认值. 这个 Map 结构在这个方法调用之后并不会改变,它让我们可以简单明了的访问 Map 中的数据

1 2 3 4

private boolean hasPropertyJava8( final String id, final String property ) {     
    return m_ids.getOrDefault( id, Collections.emptySet() ).contains( property ); 
}

Google Guava

Google Guava library 定义了一个 MultiMap 接口,并提供了一些列的实现。它的实现类的数量差不多跟JDK 的 Collections 实现类的数量一样了

MultiMap.get 定义如下

1

Collection get(K key)

如果key存在,它会返回所有的值,如果key 不存在,它会返回一个空的Set. 下面用 Google Guava 来实现获取 map 中的 set,可以看出,我们也不用关注 map 中的 set

1 2 3 4 5 6 7 8 9 10 11

private final Multimap m_idsMM = HashMultimap.create();   
private void addPropertyGuava( final String id, final String property ) {     
    m_idsMM.put( id, property ); 
}   
private boolean hasPropertyGuava( final String id, final String property ) {     
    return m_idsMM.get( id ).contains( property ); 
}

总结

  • 当实现一个 multilevelMap,非常容易造成内存泄漏的问题. 开发者应该足够重视,并且最外层的 map 要将读写数据的操作分开
  • 新语言或者第三方工具,像 java 8 或者 Google Guava,已经提供了足够多的选择可以让你在实现 multilevelMap 时避免内存泄漏

翻译来源:http://java-performance.info/possible-memory-leak-in-multimap/

微信扫码关注:

转载于:https://my.oschina.net/igeeker/blog/817328

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值