GC 优化
JVM监控可以使用以下衡量标准
- 总内存使用情况(MB):即JVM使用的总内存。如果JVM使用了所有可用内存,这项指标可以衡量底层操作系统的整体性能。
- 堆内存使用(MB):即JVM为运行的Java应用所使用的对象分配的所有内存。不使用的对象通常会被垃圾回收器从堆中移除。所以,如果这个指数增大,表示你的应用没有把不使用的对象移除或者你需要更好的配置垃圾回收器的参数。
- 非堆内存的使用(MB):即为方法区和代码缓存分配的所有内存。方法区是用于存储被加载的类的引用,如果这些引用没有被适当的清理,永生代池会在每次应用被重新部署的时候都会增大,导致非堆的内存泄露。这个指标也可能指示了线程创建的泄露。
- 池内总内存(MB):即JVM所分配的所有变量内存池的内存和(即除了代码缓存区外的所有内存和)。这个指标能够让你明确你的应用在JVM过载前所能使用的总内存。
- 线程:即所有有效线程数。举个例子,在Tomcat服务器中每个请求都是一个独立的线程来处理,所以这个衡量指标可以表示当前有多少个请求数,是否影响到了后台低权限的线程的运行。
- 类:即所有被加载的类的总数。如果你的应用动态的创建很多类,这可能是内存泄露的一个原因。
GC 优化的目的:
- 将老年代的对象数量将至最低
- 减少Full GC 的执行时间
老年代GC相对于新生代GC更耗时
Full GC 的执行时间币 Minor GC 要长的多
老年代空间过小会导致 Full GC 频率增加或者 内存溢出
老年代空间过大会导致 Full GC 时间过长
GC 监控工具 jstat 和 HPJMeter
1 | jstat -gcutil |
1 | jstat -gccapacity # 检查内存用量情况 |
堆内存分配
1 | -Xms -Xmx |
非堆内存分配
永久保存的区域, 用于存放Class和Meta信息,Class在被Load的时候被放入该区域。
三种内存溢出异常
- OutOfMemoryError: Java heap space 堆溢出
内存溢出主要存在的问题就是出现在这个情况中。当在JVM中如果98%的时间是用于GS切可以用的Heap size 不足2%的时候将抛出此异常信息 - OutOfMemoryError: PermGen space 非堆溢出(永久保存区域溢出)
这种错误常见在web服务器对jsp进行 pre compile 的时候。如果你的WEB APP 下毒用了大量第三方jar,其大小超过了jvm默认的大小(4M) 那么就会产生此错误信息。 如果web app 用了大量的第三方jar或者应用有太多的class文件而恰好MaxPermSize设置较小,超出了也会导致这块内存的占用过多造成溢出,或者 tomcat 热部署时不清除前面加载的环境,只会将 context 更改为新部署的,非堆存的内容就越来越多。 - OutOfMemoryError: unable to create new native thread 无法创建新的线程
这种现象比较少见,也比较奇怪,主要和jvm与系统内存的比例有关,这种怪事是因为jvm已经被系统分配了大量的内存,并且它至少要占用可用内存的一半。
Java Heap 分为3个区:
- Young
- Old
- Permanent
Jvm 有2个GC线程:
第一个线程负责回收Heap的Young区
第二个线程在Heap不足时,遍历Heap,将Young区升级为Older区,Older区的大小等于 -Xmx 减去 -Xmn,不能将-Xms的值设的过大,因为第二个线程被迫运行会降低 JVM 的性能
GC 优化的经验之谈
- JVM 最好将 -Xms 和 -Xmx 设为相同的值。为了优化GC,最好让 -Xmn值约等于-Xmx 的 1/3 .
- 一个 GUI 程序最好是每 10到20秒间执行一个GC,每次在半秒之内完成
GC 分析
1 |
|
GC 参数
-XX:+PrintGC
输出GC日志-XX:+PrintGCDetails
输出GC的详细日志-XX:+PrintGCTimeStamps
输出GC的时间戳(以基准时间的形式)-XX:+PrintGCDateStamps
输出GC的时间戳(以日期的形式,如年月日)-XX:+PrintHeapAtGC
在进行GC的前后打印出堆的信息-Xloggc:../logs/gc.log
日志文件的输出路径
Tomcat 设置示例
1 | JAVA_OPTS="-server -Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m -XX:SurvivorRatio=4 |