5

Tôi đang tạo một giao diện để thực thi đồng thời, trong khi trừu tượng hóa các chi tiết đồng bộ hóa (Để hoán đổi để thực hiện phân tán khi cần). Tôi đã tạo một triển khai jvm duy nhất cho phép các chuỗi được sử dụng làm mutex bằng cách lưu trữ chúng trong một bản đồ để đảm bảo một tham chiếu được sử dụng, ngay cả khi các chuỗi tham chiếu khác nhau được truyền vào. ngạc nhiên khi thấy rằng thử nghiệm đã cho thấy số lượng tham chiếu không bao giờ giảm. Tôi giả sử sử dụng WeakValues ​​() sẽ là đủ để ngăn chặn rò rỉ bộ nhớ, nhưng có vẻ như đó không phải là trường hợp. Bất cứ ai có thể chỉ ra những gì có thể gây ra rò rỉ này?Rò rỉ bộ nhớ trong thông tin bản đồ yếu cho phương thức đã đồng bộ

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(); 
    } 
} 

}

Đây là bài kiểm tra đó là thất bại vào sự khẳng định cuối cùng:

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);   
     } 

    }; 
} 

}

khẳng định cuối cùng này là những gì phá vỡ kiểm tra: assertEquals (0 , methodExecutor.mutexMap.size());

Trả lời

5

Bạn đang lưu trữ chính xác cùng một đối tượng String làm cả khóa và giá trị. Chìa khóa là một tham chiếu mạnh mẽ cho đối tượng, và miễn là một tham chiếu mạnh mẽ đến nó tồn tại, sự tham chiếu yếu đến nó là vô nghĩa. Định nghĩa của yếu thể truy cập (here) cho rằng:

Một đối tượng là một cách yếu ớt có thể truy cập nếu nó không phải là mạnh mẽ hay nhẹ nhàng có thể truy cập nhưng có thể đạt được bằng cách đi qua một tham chiếu yếu.

Bằng cách này, ngay cả khi điều chỉnh này tôi không nghĩ rằng bạn có thể giả định rằng bản đồ sẽ luôn trống vào cuối. Nó có lẽ sẽ gần như trống rỗng, nhưng tôi nghĩ đó là tất cả những gì có thể nói về nó.

+0

Rất tốt thưa ngài! Tôi đã thay đổi nó để lưu trữ một String mới (id), và nó hoạt động như một sự quyến rũ! –

+0

Tại sao bạn nói rằng bản đồ sẽ không luôn trống? –

+0

@Anthony: Tôi có thể sai, nhưng tôi không nghĩ có bất kỳ sự đảm bảo nào về thời điểm các tham chiếu yếu sẽ bị xóa. – ColinD

1

Tham chiếu yếu sẽ chỉ được thu thập khi JVM hoàn toàn cần thêm bộ nhớ.

+0

Nhưng trong thử nghiệm của tôi tôi rõ ràng không nếu một OutOfMemoryError không xảy ra, nhưng khẳng định đó không phá vỡ. Tôi đặt xác nhận cuối cùng mà chạy sau khi OOM xảy ra, và tôi mong đợi có các giá trị được loại bỏ nhưng họ không. –

+1

Lỗi OutOfMemoryError xảy ra vì bạn đang lưu trữ một danh sách các tham chiếu mạnh mẽ. Nghĩa là, infiniteList của bạn là một tham chiếu mạnh mẽ và chứa các tham chiếu mạnh mẽ đến các mảng dài. –

+0

Tham chiếu mềm được thu thập khi JVM cần thêm bộ nhớ. Tài liệu tham khảo yếu có thể được thu thập ngay cả khi không. – ColinD