在解决这个问题前,要对jvm的内存机制有一定的了解。
对于Jvm的内存大致分为三个部分:permanent generation space(永久保存区域),heap space(堆内存),java stacks(java栈)。还有一个本地内存,就是jvm用于其内部操作的 本地内存,如jni以及第三方的模块,jdbc的加载就用的是本地内存
永久保存去存放的是应用启动时加载的class以及meta信息,class第一次被Load的是时候就是放在permanent space的,class需要存储的还有方法和静态属性。
堆区域主要存放的是对象,主要是非静态属性。
栈主要存放的就是变量以及输入输出参数。
每个线程都有独立的堆栈。
(1)OutOfMemrmory:permGen Space
发生这个主要是因为需要加载的类和jar太多,jvm装在类空间不够,与permanent generation space有关。解决办法:
1.增加jvm的permanent space大小,即:-XX:PermSize和XX:MaxPermSize的大小,这个在tomcat/bin/下面的catalina.sh中添加
JAVA_OPTS=" -XX:PermSize=64M -XX:MaxPermSize=128m"
2.删去不用的jar包,如多个应用公用一个tomcat,且有多个jar包是公用的,那么可以将公用的提取出,放到tomcat下,统一加载。
(2)OutOfMemrmory:java heap space
原因:创建对象太多,在垃圾回收之间虚拟内存分配的堆内存空间已用完,用户代码内存不释放(创建的session存放的东西太大而且不释放;用户分配太多的内存;创建太多的线程);还有就是很重要的一个内存泄露的问题(jdbc没有close;分配好的对象没有释放和close)与heap space有关。
解决:
1.检查程序,是否有死循环,或者不必要的创建对个对象,特别还有就是jdbc的创建问题,无用的对象没有释放链接,修改程序;
2.增加java虚拟机的xms(初始化对内存大小),Xmx(最大堆内存大小)
如:set JAVA_OPTS= -Xms256m -Xmx1024m
(3)OutOfMemoryError:unable to create new native thread
本地内存
那么是什么原因造成这种问题呢?
每一个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代码里边。
32位的操作系统,理论最大值2的32次方
进程的内存:
java内存 + 本地内存 + 加载的可执行文件和库 + 操作系统的预留内存
给出一个有关能够创建线程的最大个数的估算公式:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
对于jdk1.5而言,假设操作系统保留120M内存:1.5GB JVM: (2GB-1.5Gb-120MB)/(1MB) = ~380 threads
1.0GB JVM: (2GB-1.0Gb-120MB)/(1MB) = ~880 threads
对于栈大小为256KB的jdk1.4而言,
1.5GB allocated to JVM: ~1520 threads
1.0GB allocated to JVM: ~3520 threads
如果我没有记错的话,在2000/XP/2003里头有一个启动选项,好像是: /PAE /3G ,可以让用户进程最大内存扩充至3G,这时操作系统只能占用最多1G的虚存。那样应该可以让JVM创建更多的线程。