5

同期の詳細を抽象化しながら、メソッドを同時に実行するためのインターフェイスを作成しています(必要に応じて分散実装をスワップする)。私は文字列をマップに格納して、異なる参照の文字列が渡されても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());

答えて

5

キーと値の両方が同じStringオブジェクトを保存しています。キーはオブジェクトへの強い参照であり、それに対する強い参照が存在する限り、それに対する弱い参照は無意味です。それはどちらも強くもそっと到達可能であるが、弱参照をトラバースすることで到達できる場合

オブジェクトが弱到達可能である:弱到達可能here)の定義は、と述べています。

ところで、これを修正しても、最後に地図が常に空になるとは思わないと思います。それはおそらく空に近いだろうが、私はそれについて言えることはすべてだと思う。

+0

非常に良いサー!私は新しいString(id)を格納するように変更しました。それは魅力のように機能します! –

+0

なぜ地図が常に空ではないとお考えですか? –

+0

@Anthony:私は間違っている可能性がありますが、弱い参照がいつクリアされるかは保証されていないと思います。 – ColinD

1

弱い参照は、JVMが絶対より多くのメモリを必要とする場合にのみ収集されます。

+0

私のテストでは、OutOfMemoryErrorが発生していないにもかかわらず明示的に失敗していますが、そのアサーションは中断しません。私は、OOMが発生した後に実行される最後のアサーションを入れました。値が削除されることを期待していますが、そうではありません。 –

+1

強い参照のリストを格納しているため、OutOfMemoryErrorが発生しています。つまり、あなたのinfiniteListは強力な参照であり、longの配列への強い参照を含んでいます。 –

+0

ソフトリファレンスは、JVMがより多くのメモリを必要とするときに収集されます。弱い参照は収集されなくても収集されます。 – ColinD

関連する問題