Утечка памяти в ссылке на карту weakValue для синхронизированного метода - PullRequest
5 голосов
/ 21 июня 2011

Я создаю интерфейс для одновременного выполнения методов, абстрагируясь от деталей синхронизации (чтобы при необходимости поменять местами распределенную реализацию). Я создал одну реализацию 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.mutexMap.size ());

Ответы [ 2 ]

5 голосов
/ 21 июня 2011

Вы храните точно такой же объект String как ключ и значение. Ключ является сильной ссылкой на объект, и пока существует сильная ссылка на него, слабая ссылка на него не имеет смысла. Определение слабо достижимо ( здесь ) гласит:

Объект является слабо достижимым, если он не является ни сильно, ни мягко достижимым, но может быть достигнут путем обхода слабого ориентира.

Кстати, даже с этим исправленным, я не думаю, что вы можете предположить, что карта всегда будет пустой в конце. Вероятно, он будет почти пустым, но я думаю, что это все, что можно сказать об этом.

1 голос
/ 21 июня 2011

Слабые ссылки будут собираться только тогда, когда JVM абсолютно необходимо больше памяти.

...