完全gc中没有gc的无法访问的char数组

时间:2014-05-22 14:21:31

标签: java memory-management garbage-collection

在我们的一个Java应用程序中,我们看到频繁的Full GCs。每秒2次。

大多数堆空间> 90%归因于char数组分配。
在堆转储分析中,发现所有都在GC根目录下无法访问,但似乎垃圾收集器似乎没有收集。 char数组仅在我们的代码中的一个位置用于IO的方法范围。

我们有默认的年轻到终身比率(1:3)和默认的GC算法 我没有改变这一点,因为这个应用程序在1G堆上运行很长时间了,现在突然连4G也不够用。

我最大的痛点是分配给char数组的90%终身内存,应该是ZERO。有没有人知道这里可能出现什么问题或者如何进一步调试呢? 我似乎空白了

更新1:
在堆分析期间,我可以看到最大的char数组,因此指出了正在分配这些数组的代码。我们正在尝试从大小为100K的DB中读取clobs,而在堆转储中,发现相同的数组大小约为100MB [我仍在研究这个问题。如果我找到任何东西会更新]

我之所以说这些数组是无法访问的,因为我可以在GC根目录下看到它们,在无法访问的头下。如果假设有任何错误,请纠正我。

enter image description here

enter image description here

这是用于从DB读取clobs的代码。

String readFromRSClobStream(Reader reader, int buffersize) throws Exception {       
        String result="";
        char[] buffer=new char[buffersize];
        int count=0;
        while((count=reader.read(buffer))!=-1)
        {
            String buffer_str=new String(buffer);
            result+=buffer_str.substring(0,count);
        }
        return result;
    }

String readFromRSClobStream(Reader reader, int buffersize) throws Exception { String result=""; char[] buffer=new char[buffersize]; int count=0; while((count=reader.read(buffer))!=-1) { String buffer_str=new String(buffer); result+=buffer_str.substring(0,count); } return result; }

泄密嫌疑人观点给了我这个 enter image description here

char []和String都与上面代码中的一个有关。
附: 希望我可以分享完整的hprof

1 个答案:

答案 0 :(得分:2)

我可以想到三种可能的解释:

  • 您的堆转储分析错误,并且数组实际上是可以访问的。

  • 这些大字符数组通常可以通过软引用或弱引用来访问,并且有一些"定相"效果在这里发生。 (通常需要2个或更多GC循环来回收软/弱引用所引用的对象。如果在之后查看堆,GC已经破坏了引用,它将包含所有以前引用的对象...无法访问。

  • 您正在以过高的速率分配阵列和/或其中一些阵列非常大。高比率的对象分配通常会导致大量的年轻空间集合。但是,如果你分配"非常大"对象(或数组),某些JVM上的分配器会将新对象直接放入终身空间。太多可能会引发频繁的完整GC。

(当然,它可能完全不同,但你还没有给我们很多证据可供使用。)

参考文献:"Size Matters"作者:Jon Masamitsu的@ Oracle。


<强>更新

您的readFromRSClobStream方法可能效率低下并且会产生大量垃圾,尤其是bufferSize相对于CLOB大小较小时。每次执行字符串连接时,都会创建一个新的String,并将旧String的内容和buffer中的字符复制到其中。这会为您提供O(N^2)个复制的字符和O(N)对象分配。

因此,如果您正在阅读的CLOB非常大,那么您将分配许多非常大的char[]个对象,这就是(IMO)可能导致GC消化不良的原因。

更有效的方法是这样的:

String readFromRSClobStream(Reader reader, int clobSizeHint, int bufferSize) 
       throws IOException {       
    StringBuilder sb = new StringBuilder(clobSizeHint);
    char[] buffer = new char[buffersize];
    int count = 0;
    while ((count = reader.read(buffer)) != -1) {
        sb.append(buffer, 0, count);
    }
    return sb.toString;
}

如果大小提示正确(即它是以字符表示的实际CLOB大小),则会为您提供O(N)个复制的字符和O(1)对象分配。

即使大小提示不准确(但不是太大),您也会获得O(N)个复制字符和O(logN)分配或更好的分配。 (这是StringBuilder使用的策略,每次填充时至少将内部缓冲区大小加倍。)