前言
再阅读此文之前,一定要先了解GC垃圾回收机制JVM调优经验
JVM配置方面,一般情况可以先用默认配置,初始参数可以保证一般的应用跑的比较稳定了。
在测试中根据系统运行状况,结合gc日志、内存监控、使用的垃圾收集器等进行合理的调整。
JVM参数说明:
- -Xms:初始堆大小,默认物理内存的1/64
- -Xmx:最大堆大小,默认物理内存的1/4
- -Xmn:新生代内存大小,官方推荐为整个堆的3/8
- -Xss:线程堆栈大小,jdk1.5及之后默认1M,之前默认256k
- -XX:NewRatio=n:设置新生代和年老代的比值。如:为3,表示年轻代与年老代比值为1:3,年轻代占整个年轻代年老代和的1/4
- -XX:SurvivorRatio=n:年轻代中Eden区与两个Survivor区的比值。注意Survivor区有两个。如:8,表示Eden:Survivor=8:1:1,一个Survivor区占整个年轻代的1/8
- -XX:+PrintGC:打印GC日志,内容简单
- -XX:+PrintGCDetails: 打印GC日志,内容详细
- -Xloggc:filename:指定gc日志路径
那么,如何设置参数?
或者说,新生代、老年代应该配置多大最合适呢?
物理内存一定的情况下,新生代设置越大,老年代就越小,Full GC频率就越高,但Full GC时间越短;
相反新生代设置越小,老年代就越大,Full GC频率就越低,但每次Full GC消耗的时间越大。
而我们调优,很大的一部分是为了避免频繁的Full GC。
配置方面,所以我们一般建议如下:
- 因为堆大小默认为-Xms指定的大小,空闲堆内存小于40%时,JVM会扩大堆到-Xmx指定的大小;空闲堆内存大于70%时,JVM会减小堆到-Xms指定的大小。如果在Full GC后满足不了内存需求会动态调整,这个阶段比较耗费资源。所以建议把-Xms和-Xmx的值设置成相等。
- 新生代尽量设置大一些,让对象在新生代多存活一段时间,每次Minor GC 都要尽可能多的收集垃圾对象,防止或延迟对象进入老年代的机会,以减少应用程序发生Full GC的频率。
- 方法区大小的设置,1.6之前的需要考虑系统运行时动态增加的常量、静态变量等,1.7只要差不多能装下启动时和后期动态加载的类信息就行。
代码实现方面
性能出现问题比如程序等待、内存泄漏等都有可能造成内存溢出等情况:
- 避免同时加载大量数据,如一次从数据库中取出大量数据,或者一次从Excel中读取大量记录,可以分批读取,用完尽快清空引用。
- 尽量避免长时间等待外部资源(数据库、网络、设备资源等)的情况,缩小对象的生命周期,避免进入老年代,如果不能及时返回结果可以适当采用异步处理的方式等。
- 避免创建过大的对象及数组:过大的对象或数组在新生代没有足够空间容纳时会直接进入老年代,如果是短命的大对象,会提前出发Full GC。
- 避免产生死循环,产生死循环后,循环体内可能重复产生大量实例,导致内存空间被迅速占满。
- 可以在合适的场景(如实现缓存)采用软引用、弱引用,比如用软引用来为ObjectA分配实例:SoftReference objectA=new SoftReference(); 在发生内存溢出前,会将objectA列入回收范围进行二次回收,如果这次回收还没有足够内存,才会抛出内存溢出的异常。
- 当集合中有对象的引用,这些对象使用完之后要尽快把集合中的引用清空,这些无用对象尽快回收避免进入老年代。