簡體   English   中英

Java不是垃圾收集內存

[英]Java not garbage collecting memory

我正在讀取一個非常大的文件,並從每一行中提取一小部分文本。 但是在操作結束時,我的內存很少。 在讀取文件后,垃圾收集器似乎無法釋放內存。

我的問題是:有沒有辦法釋放這段記憶? 或者這是一個JVM錯誤?

我創建了一個SSCCE來證明這一點。 它讀取1 mb(由於16位編碼而在Java中為2 mb)文件並從每行中提取一個字符(~4000行,因此應該是大約8 kb)。 在測試結束時,仍然使用了完整的2 mb!

最初的內存使用情況:

Allocated: 93847.55 kb
Free: 93357.23 kb

讀取文件后立即(在任何手動垃圾收集之前):

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

這是預料之中的,因為程序正在使用大量資源來讀取文件。

然而,我垃圾收集,但不是所有的內存都被釋放:

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

我知道手動調用垃圾收集器不會給你任何保證(在某些情況下它是懶惰的)。 然而,這發生在我的大型應用程序中,其中文件幾乎占用了所有可用內存,並且導致程序的其余部分盡管需要它而耗盡內存。 這個例子證實了我懷疑從文件中讀取的多余數據沒有被釋放。

以下是生成測試的SSCCE:

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

在創建子字符串時, 您的子字符串會保留對原始字符串的char數組的引用 (此優化可以非常快速地處理字符串的許多子字符串)。 所以,當你保持你的子串的al名單,你讓你的整個文件在內存中。 要避免這種情況,請使用以字符串作為參數的構造函數創建一個新String。

所以基本上我建議你這樣做

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

String(String)構造函數的源代碼明確指出它的用法是修剪“行李”:

  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;

更新:這個問題在OpenJDK 7,Update 6中消失了。擁有更新版本的人沒有問題。

確保不再保留您不再需要的參考文獻。

你仍然有alin引用。

嘗試添加al = null; in = null; al = null; in = null; 在調用垃圾收集器之前。

此外,您需要了解 substring的實現方式。 substring保留原始字符串,並且只對同一個char[]數組使用不同的偏移量和長度。

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

不確定是否有更優雅的方式復制子字符串。 也許 s.getChars()對你也更有用。

從Java 8中,子確實現在復制的字符。 您可以驗證構造函數是否調用Arrays.copyOfRange

System.gc()不保證JVM會進行垃圾收集 - 它只是對JVM的建議,它可以嘗試並進行垃圾收集。 由於已經有很多內存可用,JVM可能會忽略建議並繼續運行直到感覺需要這樣做。

閱讀更多文檔http://docs.oracle.com/javase/6/docs/api/java/lang/System.html#gc()

關於它的另一個問題可以在什么時候使用System.gc()做什么

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM