目录
①堆大小的设置;②回收器的选择;③打印GC日志。
参数设置示例:
当一组对象生成时,内存申请过程如下:
OOM(Out Of Memory)异常一般主要有如下2种原因:
调优手段主要是通过 控制堆内存的各个部分的比例 和 GC策略 来实现的,下面来看看设置不合理会导致什么后果。
1)新生代设置过小
新生代GC次数非常频繁,增大系统消耗;导致大对象直接进入旧生代,占据了旧生代剩余空间,诱发Full GC
2)新生代设置过大
新生代设置过大会导致旧生代过小(堆总量是一定的),从而诱发Full GC;新生代GC耗时大幅度增加。一般来说新生代占整个堆1/3比较合适
3)Survivor设置过小
导致对象从eden直接到达旧生代,降低了在新生代的存活时间
4)Survivor设置过大
导致eden过小,增加了GC频率(eden满,GC;Survivor满,不诱发GC)。另外,通过-XX:MaxTenuringThreshold = n 来控制新生代存活时间,尽量让对象在新生代被回收
由内存管理和垃圾回收可知,新生代和旧生代有多种GC策略和组合搭配,对开发来说是个难题,JVM提供了两种较为简单的GC策略的设置方式。
1)吞吐量优先
JVM以吞吐量为指标,由-XX:GCTimeRatio = n来设置
2)暂停时间优先
JVM以暂停时间为指标,由-XX:MaxGCPauseRatio = n来设置
-Xmn2g:设置年轻代大小为2G。在整个堆内存大小确定的情况下,增大年轻代会减小年老代,反之亦然。
-XX:MaxTenuringThreshold = 7(Threshold n门槛,阈):表示一个对象如果在Survivor区,移动了7次还没有被垃圾回收就进入老年代。如果设置为0,则年轻代对象不经过Survivor区,直接进入年老代,对于需要大量常住内存的应用,这样做可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象在年轻代存活时间,增加对象在年轻代被垃圾回收的概率,减少Full GC的频率,可以在某种程度上提高服务器的稳定性。
年轻代:Eden + Survivor(from)+ Survivor(to)
垃圾回收:GC
年老代:在年轻代中经历了N次(可配置)垃圾回收后仍然存活的对象,就会被复制到年老代。因此,可以认为年老代中存放的 都是一些生命周期较长的对象。
垃圾回收:Full GC
持久代:用于存放静态类型数据,如:Java class、Method等。持久代对垃圾回收没有显著影响。
①JVM会试图为相关Java对象在年轻代的Eden区中初始化一块内存区域。
②当Eden区空间足够时,内存申请结束。否则,执行下一步。
③JVM试图释放在Eden区中所有不活跃的对象(Young GC)。释放后若Eden空间仍然不足以放入新对象,JVM则试图将部分Eden区中的活跃对象放入Survivor区。
④Survivor区被用来作为Eden区及年老代的中间交换区域。当年老代空间足够时,Survivor区中存活了一定次数的对象会被转移到年老代。
⑤当年老代空间不够时,JVM会在年老代进行完全的垃圾回收(Full GC)。
⑥Full GC后,若Survivor区及年老代仍然无法存放从Eden区复制过来的对象,则会导致JVM无法再Eden区为新生成的对象申请内存,即出现OOM,即Out Of Memory
①年老代溢出,表现为java.lang.OutOfMemoryError:Javaheapspace
这是最常见的情况,产生原因是:设置的内存参数Xmx过小或程序的内存泄露及使用不当问题。
例如:循环上万次的字符串处理、创建上千万个对象、在一段代码内申请上百M甚至上G的内存。还有的时候虽然不会报内存溢出,却会使系统不间断的垃圾回收,也无法处理其他请求。这种情况下除了检查程序、打印堆内存等方法排查,还可以借助一些内存分析工具,如:MAT
②持久代溢出,表现为java.lang.OutOfMemoryError:PermGenspace
通常是由于持久代设置过小,动态加载了大量Java类而导致溢出,解决方法为由将参数-XX:MaxPermSize调大(一般245M能满足绝大多数应用程序需求)。将部分Java类放到容器共享区(例如:Tomcat share lib)去加载的办法也是一个思路,但前提是容器里部署了多个应用,且这些应用有大量的共享类库。
以上知识是Jdk1.8以前,Jdk1.8去除了Perm持久区。
--------------------------------------------以上内容节选自《深入分析Java Web技术内幕》一书