关于如何计算JVM内存收集(long Gc + full Gc)?

时间:2015-02-12 09:54:39

标签: java garbage-collection jvm

    package ringBuffer;  

import java.io.BufferedWriter;  
import java.io.FileNotFoundException;  
import java.io.FileOutputStream;  
import java.io.FileWriter;  
import java.io.IOException;  
import java.io.OutputStreamWriter;  
import java.io.RandomAccessFile;  
import java.nio.MappedByteBuffer;  
import java.nio.channels.FileChannel;  
import java.nio.channels.FileChannel.MapMode;  

public class PerformanceWriteTest {  
    /** 
     * @param args 
     */  
    public static void main(String[] args) {  
        String outputFile = "F:\\test\\ioTest.txt";  
        Long length = 0L ;   
        Long totalTime = 0L;  

        for (int j = 0; j < 5; j++) {  
                StringBuffer sb = new StringBuffer();  
                for (Integer i = 0; i < 1000000; i++) {  
                    sb.append(j+i.toString() + "V");  
                }  
                sb.append("S");  
                length = (long) sb.toString().length() ;  
                long start =  System.currentTimeMillis() ;  
                appendFileTest(outputFile,sb.toString());  
                totalTime = totalTime + (System.currentTimeMillis() - start) ;  
        }  
        System.out.println(" Total Data is : " + length*5/1000 + " Kbytes! ") ;  
        System.out.println(" Total Time is : " + totalTime) ;  
        System.out.println(" Averge Speed is :" + length*5/(totalTime*1000) + " Kbytes");  
    }  

    private static void appendFileTest(String outputFile, String msgs) {  
//             append1(outputFile, msgs) ;  //FileOutputStream  
//             append2(outputFile, msgs) ;  //FileWriter  
             append3(outputFile, msgs) ;  //RandomAccessFile   
//             append4(outputFile, msgs) ;  //RandomAccessFile   
    }  

    private static void append1(String outputFile, String msgs) {  
        BufferedWriter out = null;     
        try {     
             out = new BufferedWriter(new OutputStreamWriter(     
                      new FileOutputStream(outputFile, true)));     
             out.append(msgs) ;  
            } catch (Exception e) {     
                e.printStackTrace();     
            } finally {     
                try {     
                    out.close();     
                } catch (IOException e) {     
                    e.printStackTrace();     
                }     
            }     
    }  

    private static void append2(String outputFile, String msgs) {  
        try {     
            FileWriter writer = new FileWriter(outputFile, true);    
            writer.write(msgs);     
            writer.close();     
        } catch (IOException e) {     
            e.printStackTrace();     
        }     
    }  

    private static void append3(String outputFile, String msgs) {  
          try {     
                RandomAccessFile randomFile = new RandomAccessFile(outputFile, "rw");       
                long fileLength = randomFile.length();      
                randomFile.seek(fileLength);    
                randomFile.writeBytes(msgs);     
                randomFile.close();     
            } catch (IOException e) {     
                e.printStackTrace();     
            }     
    }  

    private static void append4(String outputFile, String msgs) {  
          try {     
                mbb.position(pos) ;  
                mbb.put(msgs.getBytes());  
                pos = pos + msgs.getBytes().length ;  
                raf.close();     
            } catch (IOException e) {     
                e.printStackTrace();     
            }     
    }  

    static RandomAccessFile raf ;  
    static MappedByteBuffer mbb  ;  
    static Integer pos = 0 ;  
}  

use -XX::+PrintGC option, the result is :
[GC 32704K->2928K(124992K), 0.0024320 secs]
[GC 35632K->5200K(124992K), 0.0020096 secs]
[GC 29268K->5200K(124992K), 0.0014802 secs]
[GC 37904K->9792K(157696K), 0.0035590 secs]
[GC 60504K->9840K(157696K), 0.0008594 secs]
[GC 75248K->28224K(224640K), 0.0079131 secs]
[GC 159040K->30572K(224768K), 0.0014706 secs]
[GC 159705K->37516K(355008K), 0.0029869 secs]
[GC 299148K->46668K(355008K), 0.0031385 secs]
[GC 308300K->48980K(511296K), 0.0010842 secs]
[GC 467604K->55860K(511616K), 0.0036752 secs]

可以看出有11次GC,但是,当我使用jstat -gcutil pid时 结果是:

S0     S1     E      O      P     YGC     YGCT    FGC    FGCT     GCT   
  0.00   0.00  12.01   0.00  11.93      0    0.000     0    0.000    0.000
  0.00   0.00  12.01   0.00  11.93      0    0.000     0    0.000    0.000
  0.00  54.32   5.86   0.01  11.94      1    0.002     0    0.000    0.002
  0.00  54.32   5.86   0.01  11.94      1    0.002     0    0.000    0.002
 96.58   0.00   0.00   0.01  11.94      2    0.004     0    0.000    0.004
 96.58   0.00  21.54   0.01  11.94      2    0.004     0    0.000    0.004
  0.00  96.58   0.00   0.01  11.94      3    0.006     0    0.000    0.006
  0.00  96.58  82.32   0.01  11.94      3    0.006     0    0.000    0.006
 10.57   0.00   0.00  10.61  11.94      4    0.009     0    0.000    0.009
 10.57   0.00   0.00  10.61  11.94      4    0.009     0    0.000    0.009
 10.57   0.00  41.35  10.61  11.94      4    0.009     0    0.000    0.009
 10.57   0.00  41.35  10.61  11.94      4    0.009     0    0.000    0.009
  0.00  11.46  40.92  10.61  11.94      5    0.010     0    0.000    0.010
  0.00  11.46  40.92  10.61  11.94      5    0.010     0    0.000    0.010
  0.00  11.46  40.92  10.61  11.94      5    0.010     0    0.000    0.010
  0.00  11.46  93.70  10.61  11.94      6    0.010     0    0.000    0.010
  8.22   0.00   0.00  31.82  11.94      6    0.018     0    0.000    0.018
  8.22   0.00  18.62  31.82  11.94      6    0.018     0    0.000    0.018
  8.22   0.00  18.62  31.82  11.94      6    0.018     0    0.000    0.018
  8.22   0.00  18.62  31.82  11.94      6    0.018     0    0.000    0.018
  8.22   0.00  70.07  31.82  12.06      6    0.018     0    0.000    0.018
  8.22   0.00  70.07  31.82  12.06      6    0.018     0    0.000    0.018
  8.22   0.00  89.85  31.82  12.07      6    0.018     0    0.000    0.018
  8.22   0.00  89.85  31.82  12.07      7    0.018     0    0.000    0.018
  0.00  34.09   0.00  32.41  12.07      7    0.020     0    0.000    0.020
  0.00  34.09  19.26  32.41  12.07      7    0.020     0    0.000    0.020
  0.00  34.09  19.26  32.41  12.07      7    0.020     0    0.000    0.020
  0.00  34.09  53.07  32.41  12.07      7    0.020     0    0.000    0.020
  0.00  34.09  53.07  32.41  12.07      7    0.020     0    0.000    0.020
  0.00  34.09  53.07  32.41  12.07      7    0.020     0    0.000    0.020
  0.00  34.09  79.83  32.41  12.07      7    0.020     0    0.000    0.020
  0.00  34.09  79.83  32.41  12.07      7    0.020     0    0.000    0.020
  1.98   0.00  10.34  43.02  12.07      8    0.023     0    0.000    0.023
  1.98   0.00  10.34  43.02  12.07      8    0.023     0    0.000    0.023
  1.98   0.00  10.34  43.02  12.07      8    0.023     0    0.000    0.023
  1.98   0.00  23.53  43.02  12.07      8    0.023     0    0.000    0.023
  1.98   0.00  23.53  43.02  12.07      8    0.023     0    0.000    0.023
  1.98   0.00  36.71  43.02  12.07      8    0.023     0    0.000    0.023

仅为GC的8倍。

我的问题是:

  1. 为什么gc时代不一样?
  2. 使用jstat -gccapacity pid,我可以获得NGCMX :695616.0 OGCMX :1391296.0
    那么如何计算JVM的内存收集?由年轻的Gc和完整的Gc分开。 谢谢!

1 个答案:

答案 0 :(得分:1)

垃圾收集器完成收集所花费的总时间取决于几个因素,但仅举几例

  • 正在使用的收集器的类型 - ParallelGC,G1GC,CMS等。
  • 收集地区 - Young gen或Old gen
  • GC时在该地区的实时设置 - 更多活动对象 标记它们所需的时间
  • 区域大小 - 区域越大,所需的时间越长 紧凑

在YGCT的情况下,我可以猜测,随着越来越多的物体可以到达(它们被标记为实时),标记它们并将它们移动到幸存者空间增加所需的时间。但要确定这一点,我们需要获得有关GC的更多详细信息。我建议您添加以下标志以获取有关GC的更多信息。

-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintHeapAtGC 
-XX:+PrintTenuringDistribution -Xloggc:/path-to-logs-dir/gc.log

使用这些标记,您将了解更多详细信息,并希望更好地了解GC停顿时间较长的原因。

使用jstat,了解每个GC中收集的内存并非易事。我已经执行了你的代码(将循环计数器增加到2Million)并使用此命令-gc每秒收集jstat -gc 5700 1000个统计数据,结果如下(我删除了几列以使其简洁)< / p>

S0C    S1C    S0U    S1U      EC       EU        OC         OU      YGC    YGCT    FGC    FGCT     GCT
5120.0 5120.0  0.0    0.0   33280.0   2662.6   87552.0      0.0        0    0.000   0      0.000    0.000
5120.0 5120.0  0.0    0.0   33280.0   2662.6   87552.0      0.0        0    0.000   0      0.000    0.000
5120.0 5120.0  0.0    0.0   33280.0   2662.6   87552.0      0.0        0    0.000   0      0.000    0.000
5120.0 5120.0  0.0    0.0   33280.0   2662.6   87552.0      0.0        0    0.000   0      0.000    0.000
3584.0 5120.0  64.0   0.0   265216.0   0.0     102912.0   37158.5      8    0.035   1      0.007    0.043
512.0  4608.0  0.0    0.0   407552.0 48864.8   128000.0   37159.2     10    0.054   3      0.019    0.072
4608.0 4608.0  0.0    0.0   419328.0 263176.3  126464.0   18944.2     11    0.063   4      0.023    0.087
4608.0 4608.0  0.0    0.0   594944.0 475327.6  209920.0   70145.4     12    0.092   5      0.034    0.127
4096.0 4608.0  0.0    32.0  594944.0 384988.3  209920.0   107009.4    13    0.099   5      0.034    0.134

正如你在第5行所看到的那样,在那一秒钟内,发生了8个年轻的基因发生,并且没有办法只用这些统计数据知道每个GC的年轻大小。当然,您可以将间隔减少到100毫秒,如果您很幸运,您可能会找到内存,但它并不总是可靠,但使用上面提到的GC标记,信息始终是精确的。

以下是带有程序标志的GC输出

{Heap before GC invocations=1 (full 0):
 PSYoungGen      total 38400K, used 33280K [0x00000000d5980000, 0x00000000d8400000, 0x0000000100000000)
  eden space 33280K, 100% used [0x00000000d5980000,0x00000000d7a00000,0x00000000d7a00000)
  from space 5120K, 0% used [0x00000000d7f00000,0x00000000d7f00000,0x00000000d8400000)
  to   space 5120K, 0% used [0x00000000d7a00000,0x00000000d7a00000,0x00000000d7f00000)
 ParOldGen       total 87552K, used 0K [0x0000000080c00000, 0x0000000086180000, 0x00000000d5980000)
  object space 87552K, 0% used [0x0000000080c00000,0x0000000080c00000,0x0000000086180000)
 Metaspace       used 2454K, capacity 4494K, committed 4864K, reserved 1056768K
  class space    used 265K, capacity 386K, committed 512K, reserved 1048576K
0.207: [GC (Allocation Failure) 
Desired survivor size 5242880 bytes, new threshold 7 (max 15)
[PSYoungGen: 33280K->2736K(38400K)] 33280K->2744K(125952K), 0.0178152 secs] [Times: user=0.00 sys=0.00, real=0.02 secs]



Heap after GC invocations=1 (full 0):
 PSYoungGen      total 38400K, used 2736K [0x00000000d5980000, 0x00000000d8400000, 0x0000000100000000)
  eden space 33280K, 0% used [0x00000000d5980000,0x00000000d5980000,0x00000000d7a00000)
  from space 5120K, 53% used [0x00000000d7a00000,0x00000000d7cac020,0x00000000d7f00000)
  to   space 5120K, 0% used [0x00000000d7f00000,0x00000000d7f00000,0x00000000d8400000)
 ParOldGen       total 87552K, used 8K [0x0000000080c00000, 0x0000000086180000, 0x00000000d5980000)
  object space 87552K, 0% used [0x0000000080c00000,0x0000000080c02000,0x0000000086180000)
 Metaspace       used 2454K, capacity 4494K, committed 4864K, reserved 1056768K
  class space    used 265K, capacity 386K, committed 512K, reserved 1048576K
}

从上面可以清楚地看出,在GC

之前,它只有一个年轻的GC作为完整的GC是0 GC之后的

Young Gen usage is 33MB, Old Gen is 0 MB Young Gen is 2.7MB and Old Gen is 8k

很明显,在第一次GC之后,~30.5MB被释放。所以GC日志是你最好的选择,并注意他们理解分配率和GC模式。

希望这会有所帮助:)