为了触发gc写一段代码,实际上也可以直接使用System.gc():
public class Test { public static void main(String[] args) { byte[] bytes1 = new byte[1024 * 1024]; byte[] bytes2 = new byte[1024 * 1024]; byte[] bytes3 = new byte[1024 * 1024]; byte[] bytes4 = new byte[1024 * 1024]; byte[] bytes5 = new byte[1024 * 1024]; } public static void test(){ test(); } }要在控制台打印gc信息需要我们手动的配一些参数:
-XX:+PrintGCDetails 输出GC的详细日志
-XX:+PrintGCTimeStamps/PrintGCDateStamps 输出GC的时间戳
-XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
-Xloggc:../logs/gc.log 日志文件的输出路径
我这里使用Idea,直接在VM args配置即可:
现在运行上面的程序即可在控制台获得gc信息:
2019-01-24T20:08:25.811+0800: [GC (Allocation Failure) [PSYoungGen: 1019K->488K(1536K)] 1019K->608K(5632K), 0.0036115 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2019-01-24T20:08:25.872+0800: [GC (Allocation Failure) [PSYoungGen: 1504K->488K(1536K)] 1624K->780K(5632K), 0.0016239 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2019-01-24T20:08:25.879+0800: [GC (Allocation Failure) [PSYoungGen: 653K->504K(1536K)] 4017K->3940K(5632K), 0.0009844 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2019-01-24T20:08:25.880+0800: [GC (Allocation Failure) [PSYoungGen: 504K->504K(1536K)] 3940K->3948K(5632K), 0.0006796 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2019-01-24T20:08:25.881+0800: [Full GC (Allocation Failure) [PSYoungGen: 504K->0K(1536K)] [ParOldGen: 3444K->3832K(4096K)] 3948K->3832K(5632K), [Metaspace: 3426K->3426K(1056768K)], 0.0076471 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 2019-01-24T20:08:25.888+0800: [GC (Allocation Failure) [PSYoungGen: 0K->0K(1536K)] 3832K->3832K(5632K), 0.0003390 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2019-01-24T20:08:25.889+0800: [Full GC (Allocation Failure) Exception in thread "main" java.lang.OutOfMemoryError: Java heap space at Test.main(Test.java:17) [PSYoungGen: 0K->0K(1536K)] [ParOldGen: 3832K->3814K(4096K)] 3832K->3814K(5632K), [Metaspace: 3426K->3426K(1056768K)], 0.0065960 secs] [Times: user=0.00 sys=0.00, real=0.01 secs] Heap PSYoungGen total 1536K, used 65K [0x00000000ffe00000, 0x0000000100000000, 0x0000000100000000) eden space 1024K, 6% used [0x00000000ffe00000,0x00000000ffe104d8,0x00000000fff00000) from space 512K, 0% used [0x00000000fff00000,0x00000000fff00000,0x00000000fff80000) to space 512K, 0% used [0x00000000fff80000,0x00000000fff80000,0x0000000100000000) ParOldGen total 4096K, used 3814K [0x00000000ffa00000, 0x00000000ffe00000, 0x00000000ffe00000) object space 4096K, 93% used [0x00000000ffa00000,0x00000000ffdb9a60,0x00000000ffe00000) Metaspace used 3472K, capacity 4496K, committed 4864K, reserved 1056768K class space used 377K, capacity 388K, committed 512K, reserved 1048576K上面的gc信息取一条分析:
[GC/Full GC (Allocation Failure) [PSYoungGen: 1019K->488K(1536K)] 1019K->608K(5632K), 0.0036115 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
最前面的GC/FullGC表示gc类型,GC表示新生代gc(Minor GC),Full GC表示对新生代和老年代一起收集。
[PSYoungGen: 1019K->488K(1536K)]这个表示GC前该内存区域已使用容量-->GC后该内存区域已使用容量,后面圆括号里面的1536K为该内存区域的总容量。
紧跟着后面的1019K->608K(5632K),表示GC前Java堆已使用容量->GC后Java堆已使用容量,后面圆括号里面的5632K为Java堆总容量。
[Times: user=0.00 sys=0.00, real=0.00 secs]分别表示用户消耗的CPU时间,内核态消耗的CPU时间和操作从开始到结束所经过的墙钟时间,CPU时间和墙钟时间的差别是,墙钟时间包括各种非运算的等待耗时,例如等待磁盘I/O、等待线程阻塞,而CPU时间不包括这些耗时。因为这里是测试在几乎一开始就发生了gc,并且设置的堆栈容量都较小,所以看不出时间。
PSYoungGen和ParOldGen分别代表新生代和老年代所使用的垃圾收集器。PSYoungGen表示Parallel Scavenge收集器,ParOldGen表示Parallel Old。要查看当前jvm使用那种收集器可以使用-XX:+PrintCommandLineFlags,命令行下运行即可。
java -XX:PrintCommandLineFlags -version -XX:InitialHeapSize=132485376 -XX:MaxHeapSize=2119766016 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -XX:+UseParallelGC java version "1.8.0_152" Java(TM) SE Runtime Environment (build 1.8.0_152-b16) Java HotSpot(TM) 64-Bit Server VM (build 25.152-b16, mixed mode)其中的-XX:+UseParallelGC表示使用Parallel Scavenge+Serial Old的组合,但是上面是Parallel Scavenge+parallel old的组合,这是为什么???
GC中的参数这里有一篇不错的文章总结gc中的参数,比较详细:GC
内存分配与回收策略对象内存的分配,一般是在堆上进行分配,但是随着JIT技术的发展,部分对象直接在栈上进行内存分配。
在前面的分代收集算法小节处,已经描述了jvm中的分代,将堆分为新生代和老年代。在描述内存分配前,我们先来了解下不同的GC类型: