要了解java GC 机制就需要先了解java运行时是怎样分配内存的
程序计数器
程序计数器(Program Counter Register)是一个比较小的内存区域,用于指示当前线程锁执行的字节码到了第几行,可以理解为当前线程所执行的字节码的行号指示器。在JVM中,字节码指示器就是通过改变程序计数器的值来确定接下来需要执行的指令。比如循环、分支等功能就是靠程序计数器来完成的。
由于一个CPU在同一时间只会执行一个线程中的指令,所以当线程暂挂时,未来保证在回复后能够正确的继续执行,所以每个线程的程序计数器都是独立的,也就是说每个线程都会一个私有的程序计数器,以确保多个线程在执行的时候互不影响。
如果虚拟机正在执行一个java方法,程序计数器记录的是字节码指令对应的地址;如果正在执行的是一个native(由C语言编写的方法)方法,则计数器的值为Undefined。由于程序计数器只是记录当前指令地址,不会存在溢出的情况,所以程序计数器是所有JVM内存区域中唯一一个没有定义OutOfMemoryError的区域。
JVM栈(java虚拟机栈)
JVM栈描述的是java方法执行的内存模型:一个线程的每个方法在执行的同时,都会创建一个栈帧(Statck Frame),栈帧中存储有局部变量表、操作栈、动态链接、方法出口等。当方法被调用的时候,栈帧在JVM栈中入栈,方法执行结束时出栈,也就是说每一个方法被调用的过程就会对应一个栈帧从入栈道出栈的过程。
通常说java内存分为栈和堆,这的栈指的就是JVM栈或者说JVM栈中的局部变量表。
局部变量表中存储着方法的相关局部变量,包括各种基本数据类型,对象的引用,返回地址等。在局部变量表中,只有long和double类型会占 用2个局部变量空间(Slot,对于32位机器,一个Slot就是32个bit),其它都是1个Slot。需要注意的是,局部变量表是在编译时就已经确定,方法运行所需要分配的空间在栈帧中是完全确定的,在方法的生命周期内都不会改变。
JVM栈中定义了两种异常:StatckOverFlowError 和 OutOfMemoryError。如果线程调用的栈深度大于虚拟机允许的最大深度,则抛出StatckOverFlowError,但是多数JVM都允许动态扩展JVM栈的大小,所以线程可以一直申请,直到内存不足,此时会抛出 OutOfMemoryError。
一个线程对应一个JVM栈,所以JVM栈是线程私有的。
本地方法栈(Native Method Statck)
本地方法栈在作用,运行机制,异常类型等方面都和JVM栈相同,唯一的区别是JVM栈运行的是java方法,本地方法栈运行的是native方法。同样的,本地方法栈也是线程私有的。目前很多虚拟机都讲JVM栈和本地方法栈放在一起使用。
本人会继续总结堆、方法区、java对象访问方式以及详细讲解GC机制,目前时间有限,但会持续更新,欢迎指教。