java 中三种内存溢出错误的处理方法
第一种 OutOfMemoryError: PermGen space
发生这种问题的原意是程序中使用了大量的 jar 或 class,使 java 虚拟机装载类的空间不够,与 Permanent Generation space 有关。解决这类问题有以下两种办法:
增加 java 虚拟机中的 XX:PermSize 和 XX:MaxPermSize 参数的大小,其中 XX:PermSize 是初始永久保存区域大小,XX:MaxPermSize 是最大永久保存区域大小。如针对 tomcat6.0,在 catalina.sh 或 catalina.bat 文件中一系列环境变量名说明结束处(大约在 70 行左右) 增加一行:
JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m"
如果是 windows 服务器还可以在系统环境变量中设置。感觉用 tomcat 发布 sprint+struts+hibernate 架构的程序时很容易发生这种内存溢出错误。使用上述方法,我成功解决了部署 ssh 项目的 tomcat 服务器经常宕机的问题。清理应用程序中 web-inf/lib 下的 jar,如果 tomcat 部署了多个应用,很多应用都使用了相同的 jar,可以将共同的 jar 移到 tomcat 共同的 lib 下,减少类的重复加载。这种方法是网上部分人推荐的,我没试过,但感觉减少不了太大的空间,最靠谱的还是第一种方法。
第二种 OutOfMemoryError: Java heap space
发生这种问题的原因是 java 虚拟机创建的对象太多,在进行垃圾回收之间,虚拟机分配的到堆内存空间已经用满了,与 Heap space 有关。解决这类问题有两种思路:
检查程序,看是否有死循环或不必要地重复创建大量对象。找到原因后,修改程序和算法。 我以前写一个使用 K-Means 文本聚类算法对几万条文本记录(每条记录的特征向量大约 10 来个)进行文本聚类时,由于程序细节上有问题,就导致了 Java heap space 的内存溢出问题,后来通过修改程序得到了解决。
增加 Java 虚拟机中 Xms(初始堆大小)和 Xmx(最大堆大小)参数的大小。如:
set JAVA_OPTS= -Xms256m -Xmx1024m
第三种 OutOfMemoryError:unable to create new native thread
在 java 应用中,有时候会出现这样的错误:OutOfMemoryError: unable to create new native thread.这种怪事是因为 JVM 已经被系统分配了大量的内存(比如 1.5G),并且它至少要占用可用内存的一半。有人发现,在线程个数很多的情况下,你分配给 JVM 的内存越多,那么,上述错误发生的可能性就越大。
那么是什么原因造成这种问题呢?
每一个 32 位的进程最多可以使用 2G 的可用内存,因为另外 2G 被操作系统保留。这里假设使用 1.5G 给 JVM,那么还余下 500M 可用内存。这 500M 内存中的一部分必须用于系统 dll 的加载,那么真正剩下的也许只有 400M,现在关键的地方出现了:当你使用 Java 创建一个线程,在 JVM 的内存里也会创建一个 Thread 对象,但是同时也会在操作系统里创建一个真正的物理线程(参考 JVM 规范),操作系统会在余下的 400 兆内存里创建这个物理线程,而不是在 JVM 的 1500M 的内存堆里创建。在 jdk1.4 里头,默认的栈大小是 256KB,但是在 jdk1.5 里头,默认的栈大小为 1M 每线程,因此,在余下 400M 的可用内存里边我们最多也只能创建 400 个可用线程。
这样结论就出来了,要想创建更多的线程,你必须减少分配给 JVM 的最大内存。还有一种做法是让 JVM 宿主在你的 JNI 代码里边。
给出一个有关能够创建线程的最大个数的估算公式:
对于 jdk1.5 而言,假设操作系统保留 120M 内存:
对于栈大小为 256KB 的 jdk1.4 而言,
对于这个异常我们首先需要判断下,发生内存溢出时进程中到底都有什么样的线程,这些线程是否是应该存在的,是否可以通过优化来降低线程数; 另外一方面默认情况下 java 为每个线程分配的栈内存大小是 1M,通常情况下,这 1M 的栈内存空间是足足够用了,因为在通常在栈上存放的只是基础类型的数据或者对象的引用,这些东西都不会占据太大的内存, 我们可以通过调整 jvm 参数,降低为每个线程分配的栈内存大小来解决问题,例如在 jvm 参数中添加-Xss128k
将线程栈内存大小设置为 128k。
评论