同期の詳細を抽象化しながら、メソッドを同時に実行するためのインターフェイスを作成しています(必要に応じて分散実装をスワップする)。私は文字列をマップに格納して、異なる参照の文字列が渡されても1つの参照が使用されるようにするために、文字列をmutexとして使用することを可能にする単一のjvm実装を作成しました。テストが参照カウントを示すことが決して減少していないことを見て驚いた。私はWeakValues()を使ってメモリリークを防ぐのに十分と思っていましたが、そうではないようです。誰もこのリークの原因となる可能性があることを指摘できますか?ここでsynchronizedメソッドのweakValueマップ参照のメモリリーク
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.mutexMap.size());
非常に良いサー!私は新しいString(id)を格納するように変更しました。それは魅力のように機能します! –
なぜ地図が常に空ではないとお考えですか? –
@Anthony:私は間違っている可能性がありますが、弱い参照がいつクリアされるかは保証されていないと思います。 – ColinD