1. 运行时数据区域
程序计时器:线程私有的,在操作系统结构中,程序计数器是用于存放下一条指令所在单元的地址的地方。在JVM是一块较少的内存空间,他的作用可以看做是当前线程所执行的字节码的行号指示器。字节码解析器工作时就是通过改变这个计算器的值来选取下一条需要执行的字节码指令。
Java虚拟机栈:是线程私有的,他的生命周期与线程相同,每个方法执被执行的时候都会同时创建一个栈帧,用于存储局部变量表、操作栈、动态链接、方法出口。
本地方法栈:为java中的Native方法服务。
Java堆:所有线程共享的内存区域,存放对象的实例与数组。堆还可以分为新生代与老年代。新生代细分为Eden空间、FromSurvivor空间、To Survivor空间。
Java方法区:所有线程共享,用于存储jVM加载的类信息、常量、静态变量等。
运行时常量:隶属于方法区,用于存在编译期生成的各种字面量和符号引号。
2. 实战:OutOfMemoryError异常
2.1 Java堆异常
代码清单1:
package com.one.jvm;
import java.util.ArrayList;
import java.util.List;
public class HeapOut {
static class ObjectOut {
}
public static void main(String[] args) {
List<ObjectOut> ObjectOuts = new ArrayList<HeapOut.ObjectOut>();
while(true) {
ObjectOuts.add(new ObjectOut());
}
}
}
2.2 虚拟机栈和本地方法栈溢出
在Java虚拟机规则中描述了两种异常
1、如果线程请求的栈深度大于虚拟机所允许的最大深度,将抛出*Error异常
2、如果虚拟机扩展栈时无法申请到足够的空间,则抛出OutOfMemoryError异常
代码清单2:
package com.one.jvm;
public class * {
private int count = 1;
public void countAdd() {
count++;
System.out.println(count);
countAdd();
}
public static void main(String[] args) throws Throwable{
new *().countAdd();
}
}
2.3 运行时常量异常
代码清单3:
package com.one.jvm;
import java.util.ArrayList;
import java.util.List;
public class ConstantPoolOut {
public static void main(String[] args) {
List<String> list = new ArrayList<String>();
int i = 0;
while(true) {
list.add(String.valueOf(i++).intern());
}
}
}
2.4 方法区溢出
代码清单4:
package com.one.jvm;
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 JavaMethodOut {
public static void main( String[] args) {
while(true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ClassObject.class);
enhancer.setUseCache(false);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object arg0, Method arg1, Object[] arg2,
MethodProxy arg3) throws Throwable {
return arg3.invokeSuper(arg0, arg2);
}
});
enhancer.create();
}
}
static class ClassObject {
}
}