JVM内存溢出的测试

本人初学java,看到面试官问道JVM的问题蛮多,就学了一点点,拿出来和大家分享,有什么错误的地方,希望大家指出来。
要解决内存溢出的问题,我们说为什么内存溢出。运行一个大型的软件或游戏,所需要的内存空间远远超过了,你主机内安装的内存所承受的大小,就叫内存溢出。
对于虚拟机而言,JVM包括一组寄存器,一个栈,一个垃圾回收堆一个存储方法域。我们的实例对象会放在java堆空间,普通对象存放在栈中,以及方法区,常量池等等,还有本机内存,所以对于内存溢出的问题,我们已经有眉目从哪些地方找到元凶了。
[color=red]A:java堆溢出[/color]
java堆用于存放我们的实例对象,当我们不断的创建对象,并一直持有对象的引用,防止被gc。
测试如下:
[img]package demo;

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

/**
* VM Args:-verbose:gc -Xms20M -Xmx20M -XX:+HeapDumpOnOutOfMemoryError
* */
public class HeapOverFlow {
public static void main(String[] args) {
List<HeapOverFlow> hofs=new ArrayList<HeapOverFlow>();
while(true){
hofs.add(new HeapOverFlow());
}
}
}
[/img]

设置xms为20m,xmx为20m,执行效果如下:

[img]
[GC 17190K->17190K(20352K), 0.0071101 secs]
[GC 18470K->18469K(20352K), 0.0078375 secs]
[GC 18573K->18558K(20352K), 0.0011519 secs]
[Full GC 18558K->18558K(20352K), 0.0546033 secs]
[Full GC 18558K->17808K(20352K), 0.0763401 secs]
java.lang.OutOfMemoryError: Java heap space
Dumping heap to java_pid4784.hprof ...
Heap dump file created [32577359 bytes in 0.842 secs]
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at java.util.Arrays.copyOf(Arrays.java:2760)
at java.util.Arrays.copyOf(Arrays.java:2734)
at java.util.ArrayList.ensureCapacity(ArrayList.java:167)
at java.util.ArrayList.add(ArrayList.java:351)
at demo.HeapOverFlow.main(HeapOverFlow.java:13)

[/img]
发生这种情况具体还要看是内存泄露还是内存溢出。如果存在内存泄露就要找到泄露点,如果是内存溢出,则需要增加内存空间。至于怎么做,目前还没研究哈。

[color=red]B:虚拟机栈和本地方法栈溢出[/color]

由于在HotSpot虚拟机中并不区分虚拟机栈和本地方法区栈,因此对于HotSpot来说,-Xoss(设置本地方法栈大小)参数是无效的,栈容量由-Xss参数设定。关于虚拟机栈和本地方法区栈,在Java虚拟机规范中描述了两种异常:
如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出StackOverflowError异常
如果虚拟机在扩展栈时无法申请到足够的内存空间,则抛出OutOfMemoryError异常

《深入浅出java虚拟机》这本书中谈到单线程的场景下只能浮现StackOverflowError,那我们就先来看看单线程场景下到底会是什么样子。

[img]public class StackOverFlow {
private int stackLength = 1;
private void stackLeak() {
stackLength++;
stackLeak();
}

public static void main(String[] args) throws Throwable {
StackOverFlow oom = new StackOverFlow();
try {
oom.stackLeak();
} catch (Throwable e) {
System.out.println("stack length:" + oom.stackLength);
throw e;
}
}
}[/img]
设置-Xss128k 运行结果如下:

[img]stack length:2402
Exception in thread "main" java.lang.StackOverflowError
at demo.StackOverFlow.stackLeak(StackOverFlow.java:11)
at demo.StackOverFlow.stackLeak(StackOverFlow.java:12)[/img]

通过不断创建线程耗尽内存也可以呈现出OutOfMemoryError异常,由于在windows下会造成死机,这里我就不试了。

[color=red]C:运行时常量池溢出[/color]

向运行时常量池中添加内容最简单的方式就是使用String.intern()方法。由于常量池分配在方法区内,可以通过-XX:PermSize和-XX:MaxPermSize限制方法区的大小,从而间接限制其中常量池的容量。

例子:
import java.util.ArrayList;
import java.util.List;

/**
* VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M
* @author Administrator
*
*/
public class StaticConstantPoolOF {
public static void main(String[] args) {

List<String> list = new ArrayList<String>();

int i = 0;

while (true) {
list.add(String.valueOf(i++).intern());
}
}
}

运行结果如下:
[img][GC 3159K->2263K(5056K), 0.0005486 secs]
[Full GC 2403K->1018K(5056K), 0.0387303 secs]
[Full GC 1018K->1018K(5056K), 0.0416562 secs]
[Full GC 1033K->242K(5056K), 0.0150953 secs]
Exception in thread "main" java.lang.OutOfMemoryError: PermGen space
at java.lang.String.intern(Native Method)
at demo.StaticConstantPoolOF.main(StaticConstantPoolOF.java:24)[/img]

[color=red]D:方法区溢出[/color]

方法区用于存放Class的相关信息,如类名、访问修饰符、常量池、字段描述、方法描述等。测试这个区域只要在运行时产生大量的类填满方法区,知道溢出。《深入理解Java虚拟机》借助CGlib直接操作字节码运行时,生成了大量的动态类。

当前主流的Spring和Hibernate对类进行增强时,都会使用到CGLib这类字节码技术,增强的类越多,就需要越大的方法区来保证动态生成的Class可以加载到内存。
[img]import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
* VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M
*
*/
public class JavaMethodAreaOOM {
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object object, Method method,
Object[] args, MethodProxy proxy) throws Throwable{
return proxy.invokeSuper(object, args);
}
});
enhancer.create();
}

}

static class OOMObject {

}

}[/img]

工程中要引入cglib-2.2.2.jar和asm-all-3.3.jar。
方法区的内存溢出问题同样存在jdk6和jdk7版本之间的区别,同运行时常量池内存溢出。
方法区溢出也是一种常见的内存溢出异常,一个类如果要被垃圾收集器回收掉,判定条件是非常苛刻的。在经常动态生成大量Class的应用中,需要特别注意类的回收状况。这类场景除了上面提到的程序使用了CGLib字节码增强外,常见的还有:大量JSP或动态生成JSP文件的应用、基于OSGi的应用等。

[color=red]E:本机直接内存溢出[/color]

DirectMemory容量可以通过-XX:MaxDirectMemorySize指定。

import java.lang.reflect.Field;
import sun.misc.Unsafe;

/**
* VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M
*
*/
public class DirectMemoryOOM {

private static final int _1MB = 1024 * 1024;

/**
* @param args
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
public static void main(String[] args) throws IllegalArgumentException,
IllegalAccessException {
// TODO Auto-generated method stub
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);

while(true){
unsafe.allocateMemory(_1MB);
}
}

}
该例子还没测试。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhouyiddd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值