JAVA内存报错解析
1. OutOfMemoeryError
出了程序计数器外,虚拟内存的其他几个区域(JAVA虚拟机栈、本地方法栈、JAVA堆、方法区,运行时常量池《方法区的一部分》、直接内存)都有可能产生这个错误。
1.1 JAVA虚拟机栈/本地方法栈
栈容量由-xss控制
/**
* -Xss2M
* @author Administrator
*
*/
public classJavaVMStackVM {
private void dontStop()
{
while(true)
{
try {
Thread.sleep(1000);
}catch(InterruptedExceptione) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public void stackLeakByThread()
{
while(true)
{
Threadthread= newThread(newRunnable() {
@Override
publicvoid run() {
// TODO Auto-generated method stub
dontStop();
}
});
thread.start();
}
}
public static void main(String args[])
{
JavaVMStackVMoom= newJavaVMStackVM();
oom.stackLeakByThread();
}
}
运行后的报错如下:
1.2 JAVA堆
-Xms JAVA堆内存初始分配内存大小
-Xmx JAVA堆内存可分配内存的最大上限
-XX:+HeapDumpOnOutOfMemoryError
-XX:newSize:表示新生代初始内存的大小,应该小于-Xms的值例如-XX:newSize=20m
-XX:MaxnewSize:表示新生代可被分配的内存的最大上限;当然这个值应该小于 -Xmx的值;
-Xmn:至于这个参数则是对-XX:newSize、-XX:MaxnewSize两个参数的同时配置,也就是说如果通过-Xmn来配置新生代的内存大小
import java.util.ArrayList;
import java.util.List;
/**
* -Xms10m-Xmx10m -XX:+HeapDumpOnOutOfMemoryError
* @author Administrator
*
*/
public classHeapOOM {
public static void main(String args[])
{
List<String>list= newArrayList<String>();
while(true)
{
list.add("123");
}
}
}
设置举例(选自http://unixboy.iteye.com/blog/174173/)
· java -Xmx3550m-Xms3550m -Xmn2g -Xss128k
-Xmx3550m:设置JVM最大可用内存为3550M。
-Xms3550m:设置JVM促使内存为3550m。此值可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xmn2g:设置年轻代大小为2G。整个JVM内存大小=年轻代大小 + 年老代大小 + 持久代大小。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
-Xss128k:设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。
· java -Xmx3550m -Xms3550m -Xss128k -XX:NewRatio=4 -XX:SurvivorRatio=4 -XX:MaxPermSize=16m-XX:MaxTenuringThreshold=0
-XX:NewRatio=4:设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为4,则年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5
-XX:SurvivorRatio=4:设置年轻代中Eden区与Survivor区的大小比值。设置为4,则两个Survivor区与一个Eden区的比值为2:4,一个Survivor区占整个年轻代的1/6
-XX:MaxPermSize=16m:设置持久代大小为16m。
-XX:MaxTenuringThreshold=0:设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代。对于年老代比较多的应用,可以提高效率。如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活时间,增加在年轻代即被回收的概论。
不过从个人来看,具体取值要按照实际调优来决定。
1.3 方法区/运行时常量池
import java.util.ArrayList;
import java.util.List;
/**
* -XX:PermSize=10M-XX:MaxPermSize=10M
* @author Administrator
*
*/
public classRuntimeConstantPoolOOM {
public static void main(String[] args) {
// TODO Auto-generated method stub
List<String>list= newArrayList<>();
int i = 0;
while(true)
{
list.add(String.valueOf(i++).intern());
}
}
}
Jdk8中已经没有永久代的说法,因此这两个参数不会在生效,hotspot中新增了元数据具体可参考http://www.importnew.com/14933.html
在JDK8中设置元数据参数报错如下
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
/**
* -XX:MetaspaceSize=2m-XX:MaxMetaspaceSize=2m JDK8
* -XX:PermSize=10m-XX:MaxPermSize=10m JDK7
* @author Administrator
*
*/
public classJavaMethodAreaOOM {
public static void main(String args[])
{
while(true)
{
Enhancerenhancer= newEnhancer();
enhancer.setSuperclass(OOMObkect.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Objectarg0,Methodarg1,Object[] arg2,MethodProxyarg3)throwsThrowable {
// TODO Auto-generated method stub
returnarg3.invoke(arg0,arg2);
}
});
enhancer.create();
}
}
static class OOMObkect
{
}
}
1.4 直接内存报错
import java.lang.reflect.Field;
import sun.misc.Unsafe;
/**
* -Xms10m-Xmx10m -XX:MaxDirectMemorySize=10m
* @author Administrator
*
*/
public classDirectoryMemoryOOM {
private static final long _1MB = 1024 * 1024;
public static void main(String args[]) throws Exception
{
Fieldunsafe= Unsafe.class.getDeclaredFields()[0];
unsafe.setAccessible(true);
Unsafeun = (Unsafe) unsafe.get(null);
while(true)
{
un.allocateMemory(_1MB);
}
}
}
Unsafe类的操作可阅读:http://www.cnblogs.com/mickole/articles/3757278.html
DirectMemory的http://w作用可参考这个wiki:ww.cnblogs.com/lyftest/p/6564547.html
2. StackOverflowError
2.1虚拟机栈
/**
* -Xss128K
* @author Administrator
*
*/
public classJavaVMStackError {
private int stackLenth = 1;
public void stackLeak()
{
this.stackLenth ++;
stackLeak();
}
public static void main(String[] args) {
// TODO Auto-generated method stub
JavaVMStackErrorstatck=newJavaVMStackError();
try
{
statck.stackLeak();
}
catch(Exceptione)
{
System.out.println(statck.stackLenth);
//e.printStackTrace();
}
}
}