GC 的概念
GC是java语言的垃圾回收机制,一般不需要专门编写内存回收和垃圾清理代码,对内存泄露和溢出的问题,也不需要像C程序员那样战战兢兢。这是因为在Java虚拟机中,存在自动内存管理和垃圾清扫机制。
概括地说,该机制对JVM(Java Virtual Machine)中的内存进行标记,并确定哪些内存需要回收,根据一定的回收策略,自动的回收内存,永不停息(Nerver Stop)的保证JVM中的内存空间,防止出现内存泄露和溢出问题。
GC主要有 YoungGC,OldGC,FullGC。(还有G1中独有的Mixed GC,收集整个young区以及部分Old区)
- YoungGC:回收Eden区,有些地方称之为Minor GC,或者简称YGC
- OldGC:回收Old区,只单独回收Old区的只有CMS GC,且是CMS的concurrent collection模式。
- FullGC:收集整个GC堆,也称之为Major GC。
注:如果配置了CMS垃圾回收器,那么jstat中的FGC并不表示就一定发生了FullGC,很有可能是发生了CMS GC,而且每发生一次CMS GC,jstat中的FGC就会+2**。
FullGC 触发条件
- 没有配置 -XX:+DisableExplicitGC的情况下System.gc()会触发FullGC;
- 老年代空间不足
- 方法区空间不足
- 通过Minor GC后进入老年代的平均大小大于老年代的可用内存
- 执行jmap histo : live 或 jmap -dump :live;
注:统计发现之前YGC的平均晋升大小比目前old gen剩余的空间大,触发CMS GC;Metaspace Space使用达到Metaspace阈值是触发CMS GC;
健康的 GC
无论是定位YoungGC,OldGC,FullGC哪一种GC,判断其是否正常主要从两个维度:GC频率和STW时间;
要得到这两个维度的值,我们需要知道JVM运行了多久,执行如下命令即可:
ps -p pid -o etime
可参考的健康的GC状况:
- YoungGC频率5秒/次;
- CMS GC频率不超过1天/次;
- 每次YoungGC的时间不超过30ms;
- FullGC频率尽可能完全杜绝;
G1&CMS时,FullGC回收算法会退化成Serial+SerialOld,即单线程串行回收,且完全STW,影响很大且STW时间完全不可预估,所以FullGC频率尽可能完全杜绝。
如何获取 GC 日志
一般情况可以通过两种方式来获取GC日志,一种是使用命令动态查看,一种是在容器中设置相关参数打印GC日志。
命令动态查看
jstat-gc
统计垃圾回收堆的行为
也可以设置间隔固定时间来打印:
jstat -gc 1262 2000 20
这个命令意思就是每隔2000ms输出1262的gc情况,一共输出20次。
更多详细内容请参考这篇文章:JVM——命令调优
GC 参数
- -XX:+PrintGC 输出GC日志
- -XX:+PrintGCDetails 输出GC的详细日志
- -XX:+PrintGCTimeStamps 输出GC的时间戳(以基准时间的形式)
- -XX:+PrintGCDateStamps 输出GC的时间戳(以日期的形式,如 2017-09-04T21:53:59.234+0800)
- -XX:+PrintHeapAtGC 在进行GC的前后打印出堆的信息
- -Xloggc:../logs/gc.log 日志文件的输出路径
Tomcat设置
我们经常在tomcat的启动参数中添加JVM相关参数,这里有一个典型的示例:
1 | JAVA_OPTS="-server -Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m -XX:SurvivorRatio=4 |
-verbose:gc-Xloggc:$CATALINA_HOME/logs/gc.log
将虚拟机每次垃圾回收的信息写到日志文件中,文件名由file指定,文件格式是平文件,内容和-verbose:gc输出内容相同。
-Djava.awt.headless=true Headless
模式是系统的一种配置模式。在该模式下,系统缺少了显示设备、键盘或鼠标。
-XX:+PrintGCTimeStamps-XX:+PrintGCDetails
设置gc日志的格式
-Dsun.rmi.dgc.server.gcInterval=600000-Dsun.rmi.dgc.client.gcInterval=600000
指定rmi调用时gc的时间间隔
GC日志分析工具
GChisto GC Easy 詳細請訪問:Java GC 分析
如何减少GC出现的次数
- 对象不用时显示置null。
- 少用System.gc()。
- 尽量少用静态变量。
- 尽量使用StringBuffer,而不用String累加字符串。
- 分散对象创建或删除的时间。
- 少用finalize函数。
- 如果需要使用经常用到的图片,可以使用软引用类型,它可以尽可能将图片保存在内存中,供程序调用,而不引起OOM。
- 能用基本类型就不用基本类型封装类。
- 增大-Xmx的值。
在生产环境中,根据需要配置相应的参数来监控JVM运行情况。