1. 方法区:由类装载子系统查找并加载类到内存时,会提取类信息、常量、静态变量放入方法区中。虽然虚拟机规范把方法区描述为堆的一个逻辑部分,但它有个别名叫Non-Heap(非堆)。在HotSpot虚拟机中,方法区也被叫做"永久代"(Permanent Generation),可用-XX:PermSize=10M -XX:MaxPermSize=10M来设置其大小(因为有限制,更容易出现内存溢出问题,所以HotSpot官方也有放弃永久代的规划了)。J9、JRockit则是使用进程可用内存的上限。
2.Java堆:堆是被所有线程共享的一块区域,在虚拟机启动时创建,所有对象的实例以及数组都在这里分配内存,JVM会根据new指令在堆中开辟一个确定类型的对象内存空间。堆也是垃圾收集器(GC)管理的主要区域,还可以细分为:新生代和老年代。可用-Xms20M -Xmx30M来设置堆的最小值(JVM启动时的值)和最大值。
3.程序计数器:程序计数器是一块较小的内存空间,它可以看作是当前线程所执行的字节码的行号指示器。每一个线程都有一个程序计数器,当线程执行Java程序时,程序计数器的内容总是下一条将被执行的指令的地址。
4.Java虚拟机栈:与程序计数器一样,Java虚拟机栈也是线程私有的,它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型:每个方法在执行的同时都会创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等信息。可用-Xss10M来设置栈大小
5.本地方法栈:本地方法栈与虚拟机栈所发挥的作用非常相似,区别不过是虚拟机栈为JVM执行Java方法服务,而本地方法栈则为JVM使用到的本地方法服务。
方法区内存溢出:
/**
* VM Args:-XX:PermSize=10M -XX:MaxPermSize=10M
*
* @author zzm
*/
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());
}
}
}
堆内存溢出:
/**
* VM Args:-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
*
* @author zzm
*/
public class HeapOOM {
static class OOMObject {
}
public static void main(String[] args) {
List<OOMObject> list = new ArrayList<OOMObject>();
while (true) {
list.add(new OOMObject());
}
}
}
栈内存溢出:
/**
* VM Args:-Xss2M
*
* @author zzm
*/
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();
}
}