学习《深入理解Java虚拟机 JVM高级特性域最佳实践》,学习到了JVM中常见的OutOfMemory和StackOverFlow产生的机理,感觉非常有用。
1.平时代码运行时遇到这两种错误后就可以根据具体情况去适时地调整JVM参数来处理问题
2.平时写代码的时候也会多加注意,不要让代码产生这两种异常
下面就记录下,当作学习笔记。
首先必须了解JVM运行时数据区域
方法区
用于存储已被JVM加载的类信息,常量,静态变量,即时编译器编译后的代码,线程共享。
运行时常量池
方法区一部分。存放编译期生成的各种字面量和符号引用。
虚拟机栈
内部创建栈帧,来存放局部变量表,操作数栈,动态链接,方法出口等,线程私有。
本地方法栈(HotSpot不区分虚拟机栈和本地方法栈)
类似虚拟机栈,但是只为Native方法服务。
堆
存放实例对象和数组,线程共享。
程序计数器
存放当前线程执行的字节码的行号。
1.产生堆溢出
堆是存放实例对象和数组的地方,当对象多过设置的堆大小,同时避免GC回收即可。最大内存块Xmx和最小内存块Xms一样,堆就不可扩展了。将new出的对象放到List中可防止GC回收。
/*
* VM args: -Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
* Xms equals Xmx lead to head value can't extend
*/
import java.util.ArrayList;
import java.util.List;
public class HeapOOM {
static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<OOMObject>();
while (true) {
list.add(new OOMObject());
}
}
}
2.产生虚拟机栈或本地方法栈StackOverFlow
当请求的栈深度超过JVM允许最大深度即可,用Xss设置
/*
*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;
}
}
}
3.产生虚拟机栈或方法栈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) {
JavaVMStackOOM oom = new JavaVMStackOOM();
oom.stackLeakByThread();
}
}
4.运行时常量池异常
使用String.interm()填充常量池。intern的左右是如果该常量不再常量池中,则添加到常量池,否则返回该常量引用。常量池是方法区一部分,运行时可限制方法区PermSize和最大方法区MaxPermSize大小
/*
* VM args: -XX:PermSize=10M -XX:MaxPermSize=10M
*/
import java.util.List;
import java.util.ArrayList;
public class RuntimeConstantPoolOOM {
public static void main(String[] args) {
//keep reference,avoid GC collect
List<String> list = new ArrayList<String>();
//10M PermSize in integer range enough to lead to OOM
int i = 0;
while (true) {
list.add(String.valueOf(i++).intern());
}
}
}
5.方法区溢出
通过CGLib将大量信息放到方法区
/*
* VM args: -XX:PermSize=10M -XX:MaxPermSize=10M
*/
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 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 obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
return proxy.invokeSuper(obj, args);
}
});
enhancer.create();
}
}
static class OOMObject() {
}
}