Estoy creando una interfaz para ejecutar métodos al mismo tiempo, mientras abstrae los detalles de sincronización (Para intercambiar para una implementación distribuida cuando sea necesario). Creé una implementación de jvm única que permite que Strings se use como mutex al almacenarlos en un mapa para garantizar que se utiliza una referencia, incluso si se transmiten cadenas de referencias diferentes. La concurrencia parece funcionar bien, sin embargo yo estaba sorprendido al ver que la prueba mostraba que el recuento de referencia nunca está disminuyendo. Supuse que usar WeakValues () sería suficiente para evitar fugas de memoria, pero parece que no es el caso. ¿Alguien puede señalar qué podría estar causando esta fuga?Fuga de memoria en la referencia de mapa weakValue para el método sincronizado
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();
}
}
}
Aquí está la prueba de que está fallando en el último afirmación:
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);
}
};
}
}
Esta última afirmación es lo que rompe la prueba: assertEquals (0 , methodExecutor.mutexMap.size());
¡Muy bien señor! Lo cambié para almacenar un nuevo String (id), ¡y funciona como un amuleto! –
¿Por qué dice que el mapa no siempre estará vacío? –
@Anthony: podría estar equivocado, pero no creo que haya ninguna garantía sobre cuándo exactamente se eliminarán las referencias débiles. – ColinD