2012-06-08 30 views
11

Tôi đang đọc một tệp rất lớn và trích xuất một số phần văn bản nhỏ từ mỗi dòng. Tuy nhiên vào cuối của hoạt động, tôi còn lại với rất ít bộ nhớ để làm việc với. Dường như bộ thu gom rác không giải phóng được bộ nhớ sau khi đọc trong tệp.Java không thu thập bộ nhớ rác

Câu hỏi của tôi là: Có cách nào để giải phóng bộ nhớ này không? Hay đây là lỗi JVM?

Tôi đã tạo SSCCE để chứng minh điều này. Nó đọc trong một tệp 1 mb (2 mb trong Java do mã hóa 16 bit) và trích xuất một ký tự từ mỗi dòng (~ 4000 dòng, vì vậy phải khoảng 8 kb). Vào cuối thử nghiệm, 2 mb đầy đủ vẫn được sử dụng!

Việc sử dụng bộ nhớ ban đầu:

Allocated: 93847.55 kb 
Free: 93357.23 kb 

Ngay sau khi đọc trong file (trước khi bất kỳ thu gom rác thải thủ công):

Allocated: 93847.55 kb 
Free: 77613.45 kb (~16mb used) 

Đây là để được mong đợi kể từ khi chương trình được sử dụng rất nhiều tài nguyên để đọc trong tệp.

Tuy nhiên sau đó tôi rác thu thập, nhưng không phải tất cả các bộ nhớ được giải phóng:

Allocated: 93847.55 kb 
Free: 91214.78 kb (~2 mb used! That's the entire file!) 

Tôi biết rằng bằng tay gọi thu rác không cung cấp cho bạn bất kỳ đảm bảo (trong một số trường hợp nó là lười biếng). Tuy nhiên điều này đã xảy ra trong ứng dụng lớn hơn của tôi, nơi tập tin chiếm hầu như tất cả bộ nhớ có sẵn, và làm cho phần còn lại của chương trình hết bộ nhớ mặc dù cần thiết. Ví dụ này xác nhận sự nghi ngờ của tôi rằng dữ liệu thừa đọc từ tệp không được giải phóng.

Đây là SSCCE để tạo ra các bài kiểm tra:

import java.io.*; 
import java.util.*; 

public class Test { 
    public static void main(String[] args) throws Throwable { 
     Runtime rt = Runtime.getRuntime(); 

     double alloc = rt.totalMemory()/1000.0; 
     double free = rt.freeMemory()/1000.0; 

     System.out.printf("Allocated: %.2f kb\nFree: %.2f kb\n\n",alloc,free); 

     Scanner in = new Scanner(new File("my_file.txt")); 
     ArrayList<String> al = new ArrayList<String>(); 

     while(in.hasNextLine()) { 
      String s = in.nextLine(); 
      al.add(s.substring(0,1)); // extracts first 1 character 
     } 

     alloc = rt.totalMemory()/1000.0; 
     free = rt.freeMemory()/1000.0; 
     System.out.printf("Allocated: %.2f kb\nFree: %.2f kb\n\n",alloc,free); 

     in.close(); 
     System.gc(); 

     alloc = rt.totalMemory()/1000.0; 
     free = rt.freeMemory()/1000.0; 
     System.out.printf("Allocated: %.2f kb\nFree: %.2f kb\n\n",alloc,free); 
    } 
} 
+3

Trừ khi bạn đang làm điều gì đó rất bất thường mà không ai khác có khả năng làm, "lỗi jvm" không nên là giả định đầu tiên của bạn. –

+1

Đặc biệt liên quan đến gc. –

+0

Bạn mong đợi System.gc() để giải phóng toàn bộ bộ nhớ như thế nào? Bạn vẫn đang sử dụng các chuỗi trong al, vì vậy chúng không thể được giải phóng. –

Trả lời

21

Khi thực hiện một chuỗi con, chuỗi con bạn giữ một tham chiếu đến các mảng char của chuỗi gốc (tối ưu hóa điều này làm cho việc xử lý nhiều chuỗi con của một chuỗi rất nhanh). Và như vậy, khi bạn giữ các chất nền trong danh sách al, bạn sẽ giữ toàn bộ tập tin trong bộ nhớ. Để tránh điều này, hãy tạo một String mới bằng cách sử dụng hàm tạo để nhận một chuỗi làm đối số.

Vì vậy, về cơ bản tôi muốn đề nghị bạn làm

while(in.hasNextLine()) { 
     String s = in.nextLine(); 
     al.add(new String(s.substring(0,1))); // extracts first 1 character 
    } 

Mã nguồn của chuỗi (String) constructor rõ ràng khẳng định rằng việc sử dụng của nó là để cắt "hành lý":

164  public String(String original) { 
    165   int size = original.count; 
    166   char[] originalValue = original.value; 
    167   char[] v; 
    168   if (originalValue.length > size) { 
    169    // The array representing the String is bigger than the new 
    170    // String itself. Perhaps this constructor is being called 
    171    // in order to trim the baggage, so make a copy of the array. 
    172    int off = original.offset; 
    173    v = Arrays.copyOfRange(originalValue, off, off+size); 
    174   } else { 
    175    // The array representing the String is the same 
    176    // size as the String, so no point in making a copy. 
    177    v = originalValue; 
    178   } 
    179   this.offset = 0; 
    180   this.count = size; 
    181   this.value = v; 

Cập nhật: vấn đề này đã biến mất với OpenJDK 7, Cập nhật 6. Những người có phiên bản mới hơn không gặp sự cố.

+0

Hm ... Thú vị. Đó là một sự tối ưu hóa lạ mà chuỗi con thực hiện. Nhưng nó giải thích những gì đang xảy ra. Ngoài ra có vẻ như là một báo cáo lỗi về nó: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4513622 – tskuzzy

+0

Như tôi nhớ lại, điều này đã có mặt trong các phiên bản đầu tiên của java (1.02) và tại thời gian nó được xem như là một tối ưu hóa thông minh. Vấn đề là nó làm cho việc cắt xén phức tạp hơn. –

+0

Tôi có thể thấy lý do đằng sau nó vì nó làm giảm chuỗi con thành một hoạt động 'O (1)'. Nhưng điều này gần như có vẻ như một rò rỉ bộ nhớ với tôi. – tskuzzy

-1

System.gc() không đảm bảo rằng JVM sẽ thu thập rác - đó chỉ là lời khuyên cho JVM mà nó có thể thử và thu thập rác. Vì có rất nhiều bộ nhớ đã có sẵn, JVM có thể bỏ qua lời khuyên và tiếp tục chạy cho đến khi cảm thấy cần phải làm như vậy.

Đọc thêm tại các tài liệu http://docs.oracle.com/javase/6/docs/api/java/lang/System.html#gc()

Một câu hỏi mà nói về nó có sẵn tại When does System.gc() do anything

6

Hãy chắc chắn để không giữ tài liệu tham khảo, bạn không cần nữa.

Bạn vẫn có tham chiếu đến alin.

Thử thêm al = null; in = null; trước khi gọi cho bộ thu gom rác.

Ngoài ra, bạn cần nhận ra cách substring được triển khai. substring giữ chuỗi gốc và chỉ sử dụng độ lệch và độ dài khác nhau cho cùng một mảng char[].

al.add(new String(s.substring(0,1))); 

Không chắc chắn có cách nào sao chép chuỗi con hơn một cách thanh lịch hay không. Có lẽ s.getChars() cũng hữu ích hơn cho bạn.

Kể từ Java 8, chuỗi con hiện hiện đang sao chép các ký tự. Bạn có thể tự xác minh rằng hàm tạo gọi số Arrays.copyOfRange.

+0

Tôi cần sử dụng các chuỗi trong 'al'. Tôi chỉ muốn phần còn lại của tập tin được thu gom rác. – tskuzzy

+0

Ít nhất trong JDK của tôi, 'chuỗi mới' sẽ cắt xâu chuỗi khi độ dài khác nhau. –

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