专注收集记录技术开发学习笔记、技术难点、解决方案
网站信息搜索 >> 请输入关键词:
您当前的位置: 首页 > 移动开发

内存储器溢出的种类

发布时间:2010-05-30 04:54:34 文章来源:www.iduyao.cn 采编人员:星星草
内存溢出的种类
如果JVM里运行的程序, 它的内存堆和持久存储区域的都满了,这个时候程序还想创建对象实例的话,垃圾收集器就会启动,试图释放足够的内存来创建这个对象。这个时候如果垃圾收集器 没有能力释放出足够的内存,它就会抛出OutOfMemoryError内存溢出错误。

在抛出内存溢出错误的时候,一般都会提示内存泄露的种类,

1.堆(heap)内存泄漏
  java.lang.OutOfMemoryError: Javaheap space:
  大家都比较熟悉 ,通过设置-Xms2048m -Xmx4096m可以解决

2.栈(stack)内存泄漏:
  stack over flow当前线程运行期间维护的中间变量等信息过多,
  例如常见的死循环引起stack over flow

3.方法区(permanent heap)内存泄漏

  java.lang.OutOfMemoryError: PermGen space:
  发生的原因和类型装载、类型卸载有直接的关系,
  通过设置-XX:MaxNewSize=256m -XX:MaxPermSize=256m可以解决。  一般情况下,当服务器内存过小,而提供了大量的访问服务时,  可能会缓存过多的数据对象造成堆内存溢出,当web应用不断扩大,
  加载的lib库达到一定大小(4M)后,
  就容易报PermGen OOM,也就是方法区溢出。

在Linux服务器中将参数写入环境变量:
export CATALINA_OPTS="-Xms2048m -Xmx4096m"
export JAVA_OPTS="-XX:MaxNewSize=256m -XX:MaxPermSize=256m"

Xmx 最大不要超过服务器物理内存的80%


1. 栈溢出实验

什么时候会让 Java 栈溢出啊?栈的基本特点就是 FILO(First In Last Out),如果 in 的太多而 out 的太少,就好 overflow 了。而 Java Method Stack 的功能就是保存每一次函数调用时的“现场”,即为入栈,函数返回就对应着出栈,所以函数调用的深度越大,栈就变得越大,足够大的时候就会溢出。所以模拟 Java Method Stack 溢出,只要不断递归调用某一函数就可以。
public class Test {

    private int stackLength = 0;

    public void stackOverflow() {
        ++stackLength;
        stackOverflow();
    }

    public static void main(String[] args) throws Throwable {
        Test test = new Test();

        try {
            test.stackOverflow();
        } catch (Throwable e) {
            System.out.println("stack length: " + test.stackLength);
            throw e;
        }
    }
}

运行结果
stack length: 1052
Exception in thread "main" java.lang.StackOverflowError
    at com.sinosuperman.main.Test.stackOverflow(Test.java:8)
    at com.sinosuperman.main.Test.stackOverflow(Test.java:9)
    at com.sinosuperman.main.Test.stackOverflow(Test.java:9)
    at com.sinosuperman.main.Test.stackOverflow(Test.java:9)
    at com.sinosuperman.main.Test.stackOverflow(Test.java:9)
    ...

2. Heap 内存溢出

堆是用来存储对象的,当然对象不一定都存在堆里(由于逃逸技术的发展)。那么堆如果溢出了,一定是不能被杀掉的对象太多了。模拟 Heap 内存溢出,只要不断创建对象并保持有引用存在即可。
package com.sinosuperman.main;

import java.util.ArrayList;
import java.util.List;

public class Test {

    private static class HeapOomObject {
    }

    public static void main(String[] args) {
        List<HeapOomObject> list = new ArrayList<HeapOomObject>();
        while (true) {
           list.add(new HeapOomObject());
        }
    }
}

运行结果

[GC 17024K->14184K(49088K), 0.1645899 secs]
[GC 26215K->29421K(49088K), 0.0795283 secs]
[GC 35311K(49088K), 0.0095602 secs]
[Full GC 43400K->37709K(49088K), 0.1636702 secs]
[Full GC 49088K->45160K(49088K), 0.1609499 secs]
[GC 45312K(49088K), 0.0265257 secs]
[Full GC 49088K->49087K(49088K), 0.1656715 secs]
[Full GC 49087K->49087K(49088K), 0.1656147 secs]
[Full GC 49087K->49062K(49088K), 0.1976727 secs]
[GC 49063K(49088K), 0.0287960 secs]
[Full GC 49087K->49087K(49088K), 0.1901410 secs]
[Full GC 49087K->49087K(49088K), 0.1673056 secs]
[Full GC 49087K->316K(49088K), 0.0426515 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    at com.sinosuperman.main.Test.main(Test.java:14)


3. Method Area 内存溢出

也就是 Non-heap,是用来存储 Object Class Data、常量、静态变量、JIT 编译后的代码等。如果该区域溢出,则说明某种数据创建的实在是太多了。模拟的话,可以不断创建新的 class,直到溢出为止。

以下代码使用到 cglib-2.2.2.jar 和 asm-all-3.0.jar。

package com.sinosuperman.main;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class Test {
    static class MethodAreaOomObject {
    }
    public static void main(String[] args) {
        while(true){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(MethodAreaOomObject.class);
            enhancer.setUseCache(false);
            enhancer.setCallback(new MethodInterceptor() {
                public Object intercept(Object obj, Method method, Object[] args,
                        MethodProxy proxy) throws Throwable {
                    return proxy.invoke(obj, args);
                }
            });
            enhancer.create();
        }
    }
}

运行结果

Exception in thread "main" net.sf.cglib.core.CodeGenerationException:   java.lang.reflect.InvocationTargetException-->null
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:237)
    at net.sf.cglib.proxy.Enhancer.createHelper(Enhancer.java:377)
    at net.sf.cglib.proxy.Enhancer.create(Enhancer.java:285)
    at com.sinosuperman.main.Test.main(Test.java:24)
Caused by: java.lang.reflect.InvocationTargetException
    at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    at java.lang.reflect.Method.invoke(Method.java:597)
    at net.sf.cglib.core.ReflectUtils.defineClass(ReflectUtils.java:384)
    at net.sf.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:219)
    ... 3 more
Caused by: java.lang.OutOfMemoryError: PermGen space
    at java.lang.ClassLoader.defineClass1(Native Method)
    at java.lang.ClassLoader.defineClassCond(ClassLoader.java:631)
    at java.lang.ClassLoader.defineClass(ClassLoader.java:615)
    ... 8 more

4. Runtime Constant Pool in Method Area 内存溢出
在运行时产生大量常量就可以实现让 Method Area 溢出的目的。运行是常量可以用 String 类的 intern 方法,不断地产生新的常量。

package com.sinosuperman.main;

import java.util.ArrayList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        int i = 0;
        while (true) {
            list.add(String.valueOf(i++).intern());
        }
    }
}

运行结果

Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
    at java.lang.String.intern(Native Method)
    at com.sinosuperman.main.Test.main(Test.java:12)

结语
在实际编码中要尽量避免此类错误。不过大多数程序设计的结构比这里的示例要复杂的多,使得问题被隐藏。但 JVM 的内存溢出问题本质上大都可归结为以上这几种情况。
友情提示:
信息收集于互联网,如果您发现错误或造成侵权,请及时通知本站更正或删除,具体联系方式见页面底部联系我们,谢谢。

其他相似内容:

热门推荐: