JVM内存调优——记一次本地开发环境JVM调优过程
近来开发用的电脑是一部内存4g,32位系统的Windows 7。在2017年,这样的配置可以说是非常朴素了(这里顺便赞扬下自己的艰苦奋斗精神)。同时在开发项目的时候,也不可避免的会遇到JVM内存溢出的尴尬。
通常,我们会在编译器得到一个错误信息如: java.lang.OutOfMemoryError: PermGen space。按字面意思去理解就是我们的“永久代”(Permanent Generation)空间不足。因此,我们第一个想到的当然是去增加“永久代”的大小。我们JVM“永久代”可用内存上限大概是物理内存的1/4。你可能会想,这个还不简单,4g的系统可用内存大概1.5~2g,1.5~2g/4=384~512m,不就是把参数调大嘛,-XX:MaxPermSize=384 M,考虑了实际可用空间会更小一些,你还预留了冗余,完美!但是,实际情况并这么简单。
这里列下可能使用到的参数,常见参数种类(配置内存):(-Xms 、-Xmx、-XX:newSize、-XX:MaxnewSize、-Xmn)、(-XX:PermSize、-XX:MaxPermSize)。参数的配置是分组的,前者是用来配置堆区的,后者是用来配置非堆区的。
第一组配置参数:-Xms 、-Xmx、-XX:newSize、-XX:MaxnewSize、-Xmn
1、-Xms :表示java虚拟机堆区内存初始内存分配的大小,通常为操作系统可用内存的1/64大小即可,但仍需按照实际情况进行分配。有可能真的按照这样的一个规则分配时,设计出的软件还没有能够运行得起来就挂了。
2、-Xmx: 表示java虚拟机堆区内存可被分配的最大上限,通常为操作系统可用内存的1/4大小。但是开发过程中,通常会将 -Xms 与 -Xmx两个参数的配置相同的值,其目的是为了能够在java垃圾回收机制(GC)清理完堆区后不需要重新分隔计算堆区的大小而浪费资源。
一般来讲对于堆区的内存分配只需要对上述两个参数进行合理配置即可,但是如果想要进行更加精细的分配还可以对堆区内存进一步的细化,那就要用到下面的三个参数了-XX:newSize、-XX:MaxnewSize、-Xmn。当然这源于对堆区的进一步细化分:新生代、中生代、老生代。java中每新new一个对象所占用的内存空间就是新生代的空间,当java垃圾回收机制对堆区进行资源回收后,那些新生代中没有被回收的资源将被转移到中生代,中生代的被转移到老生代。而接下来要讲述的三个参数是用来控制新生代内存大小的。
1、-XX:newSize:表示新生代初始内存的大小,应该小于 -Xms的值;
2、-XX:MaxnewSize:表示新生代可被分配的内存的最大上限;当然这个值应该小于 -Xmx的值;
3、-Xmn:至于这个参数则是对 -XX:newSize、-XX:MaxnewSize两个参数的同时配置,也就是说如果通过-Xmn来配置新生代的内存大小,那么-XX:newSize = -XX:MaxnewSize = -Xmn,虽然会很方便,但需要注意的是这个参数是在JDK1.4版本以后才使用的。
上面所述即为java虚拟机对外提供的可配置堆区的参数,接下来讲述java虚拟机对非堆区内存配置的两个参数:
1、-XX:PermSize:表示非堆区初始内存分配大小,其缩写为permanent size(持久化内存)
2、-XX:MaxPermSize:表示对非堆区分配的内存的最大上限。
以上需要特别注意的是对于-Xmx和XX:MaxPermSize两个之和不能超过JVM的内存限制(以这里的例子4g,32位win7来说,大概就是1.5~2g左右),所以,这两个参数过大,过小都不行(用着“艰苦朴素”的电脑,内存分配必须精打细算)过小的时候(使用默认的64m,80m),启动稍大的项目就会报错java.lang.OutOfMemoryError: PermGen space;设置过大(我先后尝试了1024m,1024m;640m,640m以及512m,512m三组参数),你的eclipse可能无法启动。发现最适合4G内存win 7系统的-Xmx和XX:MaxPermSize的一组参数是256m,256m。项目启动后,使用到的内存空间大概在200m上下浮动。
另外,默认空余堆内存小于40%时,JVM会增大堆直到-Xmx的最大限制;默认空余堆内存大于70%时,JVM会减小堆直到-Xms的最小限制。在服务器上,指定-Xms等于-Xmx可以避免java的GC机制频繁调整堆的大小。