2010-09-24 20 views
16

Tôi sẽ sử dụng bộ nhớ đệm dựa trên SoftReference (một điều khá đơn giản bởi chính nó). Tuy nhiên, tôi đã gặp một vấn đề khi viết một bài kiểm tra cho nó.Làm thế nào để làm cho hệ thống java phát hành Soft References?

Mục tiêu của thử nghiệm là kiểm tra xem bộ nhớ cache yêu cầu đối tượng được lưu trong bộ nhớ cache trước đó từ máy chủ một lần nữa sau khi xóa bộ nhớ xảy ra.

Ở đây tôi tìm thấy vấn đề làm thế nào để làm cho hệ thống để phát hành các đối tượng mềm tham chiếu. Gọi System.gc() là không đủ vì các tham chiếu mềm sẽ không được giải phóng cho đến khi bộ nhớ yếu. Tôi đang chạy thử nghiệm đơn vị này trên máy tính để ngân sách bộ nhớ cho máy ảo có thể khá lớn.

================== Đã thêm sau ========================== ====

Cảm ơn tất cả những ai đã quan tâm để trả lời!

Sau khi xem xét tất cả các pro và của Contra tôi đã quyết định đi theo lực lượng cách vũ phu như tư vấn bởi Nandajarnbjo. Nó xuất hiện, tuy nhiên, JVM không phải là câm - nó thậm chí sẽ không cố gắng thu gom rác nếu bạn yêu cầu một khối mà một mình là lớn hơn ngân sách bộ nhớ của VM. Vì vậy, tôi đã sửa đổi mã như sau:

/* Force releasing SoftReferences */ 
    try { 
     final List<long[]> memhog = new LinkedList<long[]>(); 
     while(true) { 
      memhog.add(new long[102400]); 
     } 
    } 
    catch(final OutOfMemoryError e) { 
     /* At this point all SoftReferences have been released - GUARANTEED. */ 
    } 

    /* continue the test here */ 
+0

IMHO bạn đang sử dụng thiết lập thử nghiệm rất dễ vỡ (và không xác định?) Để kiểm tra chức năng tham chiếu mềm Java VM, thay vì thử nghiệm logic ứng dụng của riêng bạn. – Ruben

+0

Có, tôi đồng ý. Đó là một trong những lợi thế của phương pháp này. Tuy nhiên, với cách tiếp cận này bạn làm chính xác những gì các thử nghiệm phải làm - kiểm tra hợp đồng của đơn vị. Nếu tôi lấy một cách khác, nó sẽ kiểm tra nội bộ của đơn vị - đó là không mong muốn vì nó vi phạm đóng gói, và giới thiệu sự phụ thuộc không cần thiết nữa. Nhưng bạn đúng - trong thử nghiệm của tôi nó phụ thuộc vào hành vi của VM trong trường hợp khủng hoảng bộ nhớ. Trong tiêu chuẩn java người ta nói rằng VM * sẽ * phát hành các tham chiếu mềm trước khi ném OutOfMemoryError - tốt, chúng ta hãy hy vọng rằng nó thực sự. – JBM

+2

+1, điều này đã giúp tôi gỡ lỗi một vấn đề mà tôi nghĩ Swing đã bị rò rỉ bộ nhớ nhưng nó đã thực sự chỉ giữ tài liệu tham khảo mềm cho một số đối tượng ứng dụng. – casablanca

Trả lời

0

Bạn có thể đặt rõ ràng tham chiếu mềm thành giá trị rỗng trong thử nghiệm của mình và như mô phỏng rằng tham chiếu mềm đã được phát hành.

Điều này tránh bất kỳ thiết lập thử nghiệm phức tạp nào là phụ thuộc vào bộ nhớ và bộ sưu tập rác.

1
  1. Đặt thông số -Xmx thành một giá trị rất nhỏ .
  2. Chuẩn bị phần mềm tham chiếu
  3. Tạo nhiều đối tượng như có thể. Yêu cầu đối tượng mọi lúc cho đến khi nó hỏi lại đối tượng từ máy chủ.

Đây là thử nghiệm nhỏ của tôi. Sửa đổi theo nhu cầu của bạn.

@Test 
public void testSoftReference() throws Exception { 
    Set<Object[]> s = new HashSet<Object[]>(); 

    SoftReference<Object> sr = new SoftReference<Object>(new Object()); 

    int i = 0; 

    while (true) { 
     try { 
      s.add(new Object[1000]); 
     } catch (OutOfMemoryError e) { 
      // ignore 
     } 
     if (sr.get() == null) { 
      System.out.println("Soft reference is cleared. Success!"); 
      break; 
     } 
     i++; 
     System.out.println("Soft reference is not yet cleared. Iteration " + i); 
    } 
} 
+0

Có thể đặt -Xmx từ trong thử nghiệm không? Tôi không có bất kỳ máy chủ cấu hình nào để chạy thử nghiệm ... – JBM

+0

Về lý thuyết -Xmx không thực sự cần thiết. Tôi đề nghị rằng để chạy thử nghiệm của bạn không lâu. – nanda

-1

Thay vì một vòng lặp chạy dài (theo đề nghị của Nanda), nó có thể là nhanh hơn và dễ dàng hơn để chỉ cần tạo một mảng nguyên thủy rất lớn để cấp phát bộ nhớ hơn dành cho VM, sau đó bắt và bỏ qua những OutOfMemoryError:

try { 
     long[] foo = new long[Integer.MAX_VALUE]; 
    } 
    catch(OutOfMemoryError e) { 
     // ignore 
    } 

Thao tác này sẽ xóa tất cả các tham chiếu yếu và mềm, trừ khi máy ảo của bạn có nhiều hơn 16GB heap.

+0

không hoạt động: Integer.MAX_VALUE nằm ngoài kích thước mảng có thể có trên VM. – nanda

+0

@nanda, không, nó có thể rất tốt là 'Integer.MAX_VALUE'. Hạn chế duy nhất của kích thước là nó phải là một số nguyên và rằng nó không phải là số âm. – aioobe

+0

Có hai vấn đề với cách tiếp cận này: 1. tối đa. kích thước mảng là JVM phụ thuộc và thường là một vài byte nhỏ hơn 'Integer.MAX_VALUE', xem http://stackoverflow.com/q/3038392/2513200 2. Bộ nhớ có sẵn có thể lớn hơn' Integer.MAX_VALUE' – Hulk

13

Đoạn mã này buộc JVM xóa tất cả các SoftReferences. Và nó rất nhanh để làm.

Nó hoạt động tốt hơn phương pháp Integer.MAX_VALUE, vì ở đây JVM thực sự cố gắng phân bổ nhiều bộ nhớ đó.

try { 
    Object[] ignored = new Object[(int) Runtime.getRuntime().maxMemory()]; 
} catch (OutOfMemoryError e) { 
    // Ignore 
}

Bây giờ tôi sử dụng mã bit này ở mọi nơi tôi cần cho mã kiểm tra đơn vị sử dụng SoftReferences.

Cập nhật: Cách tiếp cận này thực sự chỉ hoạt động với ít hơn 2G bộ nhớ tối đa.

Ngoài ra, bạn cần phải rất cẩn thận với SoftReferences. Nó rất dễ dàng để giữ một tham chiếu cứng do nhầm lẫn sẽ phủ nhận tác dụng của SoftReferences.

Đây là một thử nghiệm đơn giản cho thấy nó hoạt động mọi lúc trên OSX. Sẽ được quan tâm trong việc biết nếu hành vi của JVM là như nhau trên Linux và Windows.


for (int i = 0; i < 1000; i++) { 
    SoftReference<Object> softReference = new SoftReferencelt<Object>(new Object()); 
    if (null == softReference.get()) { 
     throw new IllegalStateException("Reference should NOT be null"); 
    } 

    try { 
     Object[] ignored = new Object[(int) Runtime.getRuntime().maxMemory()]; 
    } catch (OutOfMemoryError e) { 
     // Ignore 
    } 

    if (null != softReference.get()) { 
     throw new IllegalStateException("Reference should be null"); 
    } 

    System.out.println("It worked!"); 
}
+0

Đó là tất nhiên không đánh lừa bằng chứng hoặc nếu Runtime.getRuntime(). MaxMemory() trả về một giá trị ngoài phạm vi tích cực của một int. Ví dụ. nếu maxMemory() trả về một giá trị trong khoảng từ 2GB đến 4GB, quá trình truyền sẽ tạo ra một mảng có kích thước âm. – jarnbjo

+0

Điều này không hoạt động - ít nhất vì maxMemory() trả về số tiền ** byte ** nhưng bạn đang cố gắng phân bổ ** Đối tượng **. Tôi đã thử nó trong bài kiểm tra của mình; nghĩ rằng OOME được ném, bộ sưu tập rác không xảy ra trước đó. Nhưng cách tiếp cận này sẽ hiệu quả hơn việc cấp phát các mảng nhỏ nếu bạn cấp phát các byte thay vì các đối tượng và tính toán kích thước thích hợp cho giá trị maxMemory. – JBM

+1

Hmm, sau khi thử điều này trong vài lần tôi nhận được kết quả không phù hợp - đôi khi nó hoạt động và đôi khi nó không ... Có thể http://stackoverflow.com/questions/457229/how-to-cause-soft-references-to -be-clear-in-java/458224 # 458224 có thể là câu trả lời tại sao. Nếu đó là sự thật thì tốt hơn nên sử dụng phân bổ dựa trên vòng lặp. – JBM

2

Cải thiện sẽ hoạt động với bộ nhớ tối đa hơn 2G. Nó lặp lại cho đến khi xảy ra lỗi OutOfMemory.

@Test 
public void shouldNotHoldReferencesToObject() { 
    final SoftReference<T> reference = new SoftReference<T>(...); 

    // Sanity check 
    assertThat(reference.get(), not(equalTo(null))); 

    // Force an OoM 
    try { 
     final ArrayList<Object[]> allocations = new ArrayList<Object[]>(); 
     int size; 
     while((size = Math.min(Math.abs((int)Runtime.getRuntime().freeMemory()),Integer.MAX_VALUE))>0) 
      allocations.add(new Object[size]); 
    } catch(OutOfMemoryError e) { 
     // great! 
    } 

    // Verify object has been garbage collected 
    assertThat(reference.get(), equalTo(null)); 

} 
Các vấn đề liên quan