第2章 Java内存区域与内存溢出异常
===============运行时数据区=================================================
1.运行时数据区包含:方法区、虚拟机栈、本地方法栈、堆、程序计数器。
2.程序计数器是一块较小的内存空间,它可以看做是当前线程执行的字节码的行号指示器。在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条线程中的指令。它是“线程私有”内存。如果线程正在执行JAVA方法,计数器记录正在执行的虚拟机字节码指令的地址;如果是NATIVE方法,则为空。
3.JAVA虚拟机栈也是线程私有的,他的生命周期与线程相同。虚拟机栈描述的是Java方法执行的内存模型;每个方法在执行的同时会创建一个栈帧,用于存储局部变量表、操作数栈、动态链接、方法出口等信息。局部变量表存放了编译器可知的各种基本数据类型(boolean,byte,char,short,int,float,long,double)、数据引用类型(reference)和返回地址returnAddress类型(指向一条字节码指令的地址)。
4.局部变量表所需要的内存空间在编译期间完成分配,当进入一个方法时,这个方法需要在桢中分配多大的局部变量空间是完全确定的,在方法运行期间不会改变局部变量表的大小。
5.对于JVM STACK,如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverFlowError,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常。
6.本地方法栈和虚拟机栈发挥的作用相似,区别是JVMSTACK为java方法服务,NATIVE METHOD STACK为Native方法服务。
7.Java堆是被所有线程共享的区域,堆分为:新生代、和年老代。在细点新生代分为Eden空间、From Survivor空间、To Survivor空间。Java堆中可能划分出多个线程私有的分配缓冲区(ThreadLocal Allocation Buffer,TLAB)。Java堆可以处于物理上不连续的内存空间。
8.方法区(永久代)是各个线程共享的内存区域,它用来存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。运行时常量池(Runtime COnstant Pool)是方法区的一部分,用于存放编译器生成的各种字面量和符号引用。运行期间也可能将新的常量放入池中,例如String的.intern()方法。
9.直接内存一般不认为是运行时数据区的部分,在JDK中新加入的NIO类,引入了一种基于通道Channel与缓冲区的IO方式,他可以使用Native函数库直接分配堆外内存。
10.虚拟机需要一个new指令时,会去检查这个指令的参数能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已经被加载、解析和初始化过。如果没有,那么必须先执行相应的类加载过程。
11.对象所需内存的大小在类加载完成后便可完全确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。
12.对象使用Reference来定位,reference根据JVM实现常分为两种。(1)句柄,即指向句柄池里的一个地址,句柄池包含对象实例数据指针和对象类型数据指针,句柄池再指向方法区和实例池。(2)实际地址。即指向Java堆中实际对象的地址。
=======================常见溢出=================================
堆溢出
1.-xms和-xmx参数用来设置堆大小的扩展范围。
2.溢出代码
List
虚拟机栈和本地方法栈溢出
1.如果线程请求的栈深度大于虚拟机所允许的最大深度,将会StackOverFlow异常。
2如果虚拟机栈在扩展时无法申请到足够的内存空间,将会抛出OOM异常。
3.溢出代码
使用-Xss参数减少栈内存的容量。
定义大量的本地变量,增大此方法中本地变量表的长度。
/**
@author zzm
*/
public class JavaVMStackSOF{
private int stackLength=1;
public void stackLeak(){
stackLength++;
stackLeak();
}
public static void main(String[] args) throws Exception{
JavaVMStackSOF javaVM=new JavaVMStackSOF ();
javaVM.stackLeak();
}
}
注解:1.虽然stackLength是成员变量,但是方法执行时仍然会压栈。
2.对于递归调用,java没有做尾递归优化。递归方法是不会使用同一栈帧的,每次递归都会压入新的栈帧。
方法区和运行时常量池溢出
例如反复执行string.intern或者通过cglib创建Class
直接内存溢出
NIO分配内存
附录:
http://www.oschina.net/question/12_31996 10个重要JVM参数