1、java堆的内存泄露和内存溢出
内存泄露:一个生命周期长的对象引用一个生命周期短的对象,当生命周期短的对象不再被使用时,不能被GC进行回收。
内存溢出:对象的生命周期长,一直占用内存,新的对象无法再被创建,就会抛出OutOfMemoryError的异常。
/**
* VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
*/
public class HeapOOM {
static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<OOMObject>();
while (true) {
list.add(new OOMObject());
}
}
}
这就是一个内存溢出的例子。可以使用Memory Analyzer进行解析。设置虚拟机的最大内存 -Xmx20m 最小内存-Xms20m 以及生成Heap文件用于分析的指令-XX:+HeapDumpOnOutOfMemoryError
2:jvm栈和本地方法栈的溢出造成的StackOverFlowError以及OutOFMemoryError异常
通过设置-Xss128k设置JVM的一个线程的栈的大小是128K,当方法中的变量超过这个限制的时候,就会抛出异常例如:
package outofmemory;
/**
* VM Args:-Xss128k
*/
public class JavaVMStackSOF {
private int stackLength = 1;
public void stackLeak() {
stackLength++;
stackLeak();
}
public static void main(String[] args) throws Throwable {
JavaVMStackSOF oom = new JavaVMStackSOF();
try {
oom.stackLeak();
} catch (Throwable e) {
System.out.println("stack length:" + oom.stackLength);
throw e;
}
}
}
抛出异常的原因:由于不断的递归,栈的容量不断被使用直到不能再被分配空间,就会抛出StackOverFlowError的异常。这个是在单线程的情况下
对于多个线程来说,当java的堆和方法区的共享内存是固定时,程序计数器的内存是可以忽略的,如果每个线程的栈很大,则线程就会很少,如果适当的减少栈的容量
则JVM可以创建更多的线程接受用户的请求。所以有的时候,为了JVM能创建更多的线程,我们需要减少堆和栈的容量。
多个线程创建引发的异常如下 java.lang.OutOfMemoryError: unable to create new native thread
package outofmemory;
/**
* VM Args:-Xss2M
*/
public class JavaVMStackOOM {
private void dontStop() {
while (true) {
}
}
public void stackLeakByThread() {
while (true) {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
dontStop();
}
});
thread.start();
}
}
public static void main(String[] args) throws Throwable {
JavaVMStackOOM oom = new JavaVMStackOOM();
oom.stackLeakByThread();
}
}
3:方法区和运行时常量池抛出的OutOfMemoryError异常的情况
方法区主要负责类的加载存储,运行时常量池主要存储常量和符号引用,如果在程序运行期间不断的向常量池加入值,超出常量池的大小就会抛出异常。例如:
package outofmemory;
import java.util.ArrayList;
import java.util.List;
/**
* VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M
*/
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
// 使用List保持着常量池引用,避免Full GC回收常量池行为
List<String> list = new ArrayList<String>();
// 10MB的PermSize在integer范围内足够产生OOM了
int i = 0;
while (true) {
list.add(String.valueOf(i++).intern());
}
}
}
将方法区的大小设置为最小值-XX:PermSize=10M 最大值 -XX:MaxPermSize=10M 加快异常的抛出
因为String.intern()在程序运行的时候,将字符串不断的加入运行时常量池中
抛出java.lang.OutOfMemoryError: PermGen space
介绍一下String.intern()方法,如果常量池中有这个值则直接取出,若没有,则将值放入到常量池中。
4、本机内存溢出
/**
* VM Args:-Xmx20M -XX:MaxDirectMemorySize=10M
*/
public class DirectMemoryOOM {
private static final int _1MB = 1024 * 1024;
public static void main(String[] args) throws Exception {
Field unsafeField = Unsafe.class.getDeclaredFields()[0];
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
while (true) {
unsafe.allocateMemory(_1MB);
}
}
}
如果使用NIO来进行操作,可能造成本地的内存溢出