内存溢出的分类以及实现方法
来源:互联网 发布:python 运维脚本应用 编辑:程序博客网 时间:2024/06/11 22:33
java中三种常见内存溢出错误的处理方法
转自http://outofmemory.cn/java/OutOfMemoryError/PermGen-space-Java-heap-space-unable-create-new-native-thread
相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的认识。
在解决java内存溢出问题之前,需要对jvm(java虚拟机)的内存管理有一定的认识。jvm管理的内存大致包括三种不同类型的内存区域:Permanent Generation space(永久保存区域)、Heap space(堆区域)、Java Stacks(Java栈)。其中永久保存区域主要存放Class(类)和Meta的信息,Class第一次被Load的时候被放入PermGen space区域,Class需要存储的内容主要包括方法和静态属性。堆区域用来存放Class的实例(即对象),对象需要存储的内容主要是非静态属性。每次用new创建一个对象实例后,对象实例存储在堆区域中,这部分空间也被jvm的垃圾回收机制管理。而Java栈跟大多数编程语言包括汇编语言的栈功能相似,主要基本类型变量以及方法的输入输出参数。Java程序的每个线程中都有一个独立的堆栈。容易发生内存溢出问题的内存空间包括:Permanent Generation space和Heap space。
第一种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代码里边。
给出一个有关能够创建线程的最大个数的估算公式:
(MaxProcessMemory - JVMMemory - ReservedOsMemory) / (ThreadStackSize) = Number of threads
对于jdk1.5而言,假设操作系统保留120M内存:
1.5GB JVM: (2GB-1.5Gb-120MB)/(1MB) = ~380 threads1.0GB JVM: (2GB-1.0Gb-120MB)/(1MB) = ~880 threads
对于栈大小为256KB的jdk1.4而言,
1.5GB allocated to JVM: ~1520 threads1.0GB allocated to JVM: ~3520 threads
对于这个异常我们首先需要判断下,发生内存溢出时进程中到底都有什么样的线程,这些线程是否是应该存在的,是否可以通过优化来降低线程数; 另外一方面默认情况下java为每个线程分配的栈内存大小是1M,通常情况下,这1M的栈内存空间是足足够用了,因为在通常在栈上存放的只是基础类型的数据或者对象的引用,这些东西都不会占据太大的内存, 我们可以通过调整jvm参数,降低为每个线程分配的栈内存大小来解决问题,例如在jvm参数中添加-Xss128k
将线程栈内存大小设置为128k。
写Java代码分别使堆溢出,栈溢出
我们知道,在JAVA中,可以使用关键字new来创建Java对象。例如,
ArrayList list = new ArrayList();
实际上,在创建完上面的一个对象后,在JVM中,会把new出来的对象存放在堆内存中,
同时,在方法栈中存放着对象的引用关系。
如果想要堆溢出,比较简单,可以循环创建对象或大的对象;
如果想要栈溢出,可以递归调用方法,这样随着栈深度的增加,JVM 维持着一条长长的方法调用轨迹,
直到内存不够分配,产生栈溢出。
因此,可以使用下面简单的代码实现堆溢出和栈溢出。
public class Test { public void testHeap(){ for(;;){ ArrayList list = new ArrayList (2000); } } int num=1; public void testStack(){ num++; this.testStack(); } public static void main(String[] args){ Test t = new Test (); t.testHeap(); t.testStack(); }}
另外,Java虚拟机的堆大小如何设置:命令行
java –Xms128m //JVM占用最小内存
–Xmx512m //JVM占用最大内存
–XX:PermSize=64m //最小堆大小
–XX:MaxPermSize=128m //最大堆大小
让Java虚拟机Perm区内存溢出的方法
转自http://blog.csdn.net/dahlwuyn/article/details/11098877
package test;import java.io.*;import java.util.*;//内存溢出测试public class OOM {public void heapOOM(){//堆内存溢出//List<int[]>list=new LinkedList<int[]>();//while(true){//list.add(new int[1024]);//不断创造可存活的对象//}ByteClassLoader loader=new ByteClassLoader();javaClass java=javaClass.open("bin\\test\\OOM.class");List<Class<?>>list=new LinkedList<Class<?>>();for(int j=0;true;j++){java.replaceName("OOM"+j);//Perm区内存溢出Class<?>clazz=loader.load(java.getClassByte());list.add(clazz);Object obj=null;try {obj=clazz.newInstance();} catch (InstantiationException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}System.out.println(obj);}}}class ByteClassLoader extends ClassLoader{public Class<?>load(byte[]classByte){return defineClass(null, classByte, 0, classByte.length);}}class javaClass{private byte[]javaClassByte;public javaClass(byte[]classByte){javaClassByte=classByte;}public String getClassName(){//获取字节流的类的名int classNameOff=0x10;//类名偏移位置int nameLengthOff=0x0e;//类名字节长度偏移,两个字节的数表示长度int length=(javaClassByte[nameLengthOff]&0xff)<<8;length+=(0xff&javaClassByte[nameLengthOff+1]);String name=new String(javaClassByte,classNameOff,length);return name;}public void replaceName(String newName){//以新类名替换旧类名String oldName=getClassName();//旧类名int nlength=newName.length();//新类名的长度int olength=oldName.length();//旧类名的长度byte[]temp=new byte[javaClassByte.length-olength+nlength];//将类名长度偏移位置之前字节复制到新字节流中System.arraycopy(javaClassByte, 0, temp, 0, 0x0e);temp[0x0e]=(byte)(nlength>>8);//填写新的类名长度值temp[0x0f]=(byte)nlength;//填写新类名System.arraycopy(newName.getBytes(), 0, temp, 0x10, nlength);//复制其余字节到新字节中System.arraycopy(javaClassByte, 0x10+olength, temp,0x10+nlength ,javaClassByte.length-0x10-olength);javaClassByte=temp;}public byte[]getClassByte(){return javaClassByte;}public static javaClass open(String file){//打开类文件fileFileInputStream fis=null;try {fis = new FileInputStream(new File(file));byte[]temp=new byte[fis.available()];fis.read(temp);return new javaClass(temp);} catch (Exception e) {return null;} finally {if(fis!=null){try {fis.close();} catch (IOException e) {e.printStackTrace();}}}}}
- 内存溢出的分类以及实现方法
- tomcat内存溢出原因分析与解决以及java内存溢出、栈溢出的原因与排查方法
- Tomcat 的JVM 内存溢出分类
- Java内存溢出分类
- LeveListDrawable实现多图片显示以及防止图片内存溢出方法
- jvm的运行内存和对应的内存溢出分类
- java内存溢出和栈溢出实例以及内存溢出和内存泄露的区别
- mark/reset方法的使用以及导致内存溢出的原因(java)
- java 内存溢出 栈溢出的原因与排查方法
- java 内存溢出 栈溢出的原因与排查方法
- java 内存溢出 栈溢出的原因与排查方法
- java内存溢出 栈溢出的原因与排查方法
- java中内存溢出的处理方法
- 注意一个检查内存溢出的方法
- Android 解决内存溢出的方法
- C++检查内存溢出的方法
- 防止加载图片内存溢出的方法
- 内存溢出bug的修复方法
- Opencv2 imread() 读取图片失败
- [数据结构]树状数组
- CXF生成调用webservice的客户端
- 阿里云服务器部署架构
- 未能找到类型“Microsoft.VisualBasic.PowerPacks.ShapeContainer”和“Microsoft.VisualBasic.PowerPacks.LineShape”
- 内存溢出的分类以及实现方法
- Spark简介
- DxDesigner中设定大十字光标
- SQL server 2005将远程数据库导入到本地的方法
- GCC+宏及C++
- Service初探与异步消息处理机制
- 悟空学Linux专栏----第42篇
- Python开发之路第一步-安装
- socket 中listen函数参数的解释