同步方法的weakValue映射引用内存泄漏



我正在创建一个用于并发执行方法的接口,同时抽象掉同步细节(在需要时交换分布式实现)。我创建了一个jvm实现,通过将字符串存储在映射中来确保使用一个引用,即使传入了不同引用的字符串,也可以将它们用作互斥对象。并发性似乎很好,但我惊讶地发现,测试显示引用数从未减少。我认为使用WeakValues()就足以防止内存泄漏,但事实似乎并非如此。有人能指出这次泄漏的原因吗?

public class SynchronousMethodExecutorSynchronizedImpl implements ISynchronousMethodExecutor {
// mutex map to provide string references
final Map<String, String> mutexMap = new MapMaker()
    .weakValues()
    .makeComputingMap(
        new Function<String, String>() {
        @Override
        public String apply(String id) {
            return id;
        }
    });
@Override
public Object doSynchronousMethod(String domain, String id, ISynchronousMethod synchronousMethod) {
    synchronized(mutexMap.get(domain + "." + id))
    {
        return synchronousMethod.execute();
    } 
}

}

以下是在最后一次断言时失败的测试:

public class SynchronousMethodExecutorSynchronizedImplTest extends TestCase {
int counter;
SynchronousMethodExecutorSynchronizedImpl methodExecutor;
@Override
public void before() throws Exception {
    super.before();
    methodExecutor = new SynchronousMethodExecutorSynchronizedImpl();
}
@Test
public void concurrentExecute() throws InterruptedException {
    assertEquals(0, counter);
    for(int i=0; i<1000; i++)
        getConcurrentExecutorThread().start();
    // wait for threads to complete
    Thread.sleep(1000);
    assertEquals(1, methodExecutor.mutexMap.size());
    try
    { 
        final List<long[]> infiniteList = new LinkedList<long[]>(); 
       for(long i = Long.MIN_VALUE; i < Long.MAX_VALUE; i++)
            infiniteList.add(new long[102400]); 
        fail("An OutOfMemoryError should be thrown");
    } 
    catch(OutOfMemoryError e)
    { 
    }
    assertEquals(2000, counter);
    assertEquals(0, methodExecutor.mutexMap.size());
}
// synchronous method
private ISynchronousMethod method = new ISynchronousMethod() {
    @Override
    public Object execute() {
        counter++;  
        return null;
    }
};
/**
 * Executes a line of code.
 * 
 * @return Thread
 */
private Thread getConcurrentExecutorThread() {
    return new Thread() {
        @Override
        public void run() {
            methodExecutor.doSynchronousMethod("TEST", "1", method);
            try
            {
                Thread.sleep(500);
            }
            catch (InterruptedException e)
            {
            }
            methodExecutor.doSynchronousMethod("TEST", new String("1"), method);        
        }
    };
}

}

最后一个断言破坏了测试:assertEquals(0,methodExecutor.mutex Map.size());

您存储的String对象与键和值完全相同。关键是对对象的强引用,只要对它的强引用存在,对它的弱引用就没有意义。弱可达的定义(此处)指出:

如果一个对象既不强也不软可到达,但可以通过遍历弱引用来到达,那么它就是弱可到达的。

顺便说一句,即使更正了这一点,我认为你也不能假设地图最后总是空的。它可能会接近空,但我认为这就是它的全部内容。

只有当JVM绝对需要更多内存时,才会收集弱引用。

相关内容

  • 没有找到相关文章

最新更新