JVM内存是我们在系统部署、优化、问题排查中的一项重要内容,在最近支持的几个项目中,多次出现与JVM内存相关的问题,因此有必要加强一下实施人员对JVM内存的理解,从而更好地应对今后可能再次出现的问题。
关于JVM内存相关理论的文章,网上有很多,下面的内容是参照网络文章并结合我们实际工作中的问题做出的一些总结,重点在JVM的内存结构上,供大家参考。
公式一
JVM在我们看来,就是一个java进程,无论我们的系统使用的是tomcat、weblogic、还是websphere,系统起来后对应的就是一个java进程。这个java进程的内存组成可以粗略的用下面的公式来表示:
JAVA进程内存(或者叫作JVM内存)=堆内存+类存储内存+堆栈内存+其它内存
堆内存:就是heap内存,就是通过-Xms和-Xmx设置的那部分内存,这部分内存主要存储java对象,例如new生成出来的对象,这部分内存可以通过垃圾回收器进行回收。
类存储内存:是专门存储java类的内存,例如系统启动时装载的第三方jar包,应用服务器本身的类,编译jsp后形成的java类等,都是存在这个区域的,这部分内存基本不会被垃圾回收器回收,只有在某些特殊情况下才会被回收。不同厂商对于这部分内存的实现不尽相同,例如Sun的jdk中,是通过-XX:PermSize和-XX:MaxPermSize来设置的,而IBM的jdk则没有这个区域,而是存在堆内存中的。
堆栈内存:即stack内存,主要用于存储线程用到的相关数据,栈中主要存放一些基本类型的变量(int, short, long, byte, float, double, boolean, char)和对象句柄。
线程堆栈内存:通过-Xss参数来指定,默认值在256KB到756KB不等,假设线程堆栈设置为-Xss512k,系统运行期间共有200个线程,那么线程堆栈总的占用内存大概就是100M,运行线程数量越多,堆栈占用内存也就越大。
其它内存:主要包括JIT、JNI、NIO等,这些内存比较复杂,这里不做过多介绍。
公式二
上面介绍的JAVA进程内存都是指用户空间的内存,除了用户空间内存,还有一部分内核空间内存,内核空间内存供操作系统使用,对于应用程序来说是透明的不可访问的。
对于上面的公式,可以进一步细化为如下公式:
JAVA进程内存=用户空间内存(堆内存+类存储内存+堆栈内存+其它内存)+内核空间内存
有了这个公式,我们在理解以下问题时就比较清晰了。
问题一:32位jdk和64位jdk的最大内存问题。
32位jdk的-Xmx值最大可以设置多大,这个问题是我们在实施过程中经常遇到的。
对于32位系统,单个进程内存的最大寻址空间为2的32次方,也就是4GB内存,其中一部分是用户空间内存,一部分是内核空间内存。对于windows系统,内核空间默认是预留2GB,用户空间最大只有2GB,用户空间除了有堆内存,还有其它类型的内存,所以-Xmx的最大值一定是小于2G,通常最大能设置到1536M。
对于linux系统,内核空间默认是预留1GB,用户空间最大可以到3GB,所以-Xmx的最大值一定是小于3G,通常至少可以设置到2048M以上。
对于64位系统,单个进程内存的最大寻址空间为2的64次方,非常大了,基本可以当作没有限制,所以-Xmx的最大值可以认为是无限大。
注:可以通过java –Xmx****** -version来测试最大可设置的堆内存。
问题二:内存溢出的分类及应对。
当出现内存溢出时,首先要搞清楚是哪部分内存溢出,这样才能采取相应的应对措施。
A.堆内存溢出
错误日志:
java.lang.OutOfMemoryError: Java heap space
解决方案:检查-Xmx设置是否合理;通过工具分析javacore和heapdump定位问题。
B.类存储内存溢出
该类问题通常只出现在sun的jdk中,错误日志大致如下:
java.lang.OutOfMemoryError: PermGen space
解决方案:增加-XX:MaxPermSize的值。
C.堆栈内存溢出
错误日志:
java.lang.StackOverflowError
解决方案:通常是代码中有死循环或循环次数太多导致,也可以适当增加-Xss参数值。
D.用户空间内存溢出(也叫本机内存溢出)
错误日志:
java.lang.OutOfMemoryError: Failed to fork OS thread java.lang.OutOfMemoryError:requested 32756 bytes for ChunkPool::allocate.Out of swap space? The system is out of physical RAM or swap space In 32 bit mode, the process size limit was hit.
解决方案:
1、物理内存不足,增加物理内存。
2、Jdk的bug,升级或更换jdk。
3、物理内存充足的情况下出现本机内存溢出,一定是在32位系统中才会出现,最好的解决方案就是换成64位系统,如果无法更换的话只能通过适当调整JAVA用户空间内存中各组成部分的大小来尝试解决。
例如在32位windows系统中,用户空间内存最大值为2G,堆内存-Xmx设置了1536M,这样其它非堆的内存部分只可以使用不到500M的内存,如果500M不够用的话,就会出现本机内存溢出,这时可以将-Xmx调小,例如调小到1024M(保证够用不会出现堆内存溢出),这样非堆的内存就增加到了1024M,问题有可能就得到解决。
关于本机内存溢出的原因可能会比较复杂,需要根据实际情况具体问题具体分析。
通过以上总结和分析,希望大家再次遇到内存溢出问题时能理清思路,对症下药。目前内存价格相对便宜,很多客户配置的物理内存都会超过4G,为了减少内存溢出问题的发生,可以建议用户都采用64位的操作系统,相关的jdk、应用服务器、数据库也都采用64位的,这样也可以使资源得到充分利用。
作者:云竹