JVM运行时内存布局

JVM运行时数据区大致情况如下:
JVM运行时内存布局
程序计数器:或者说PC计数器 Program counter register.记录的是当前线程执行的字节码的位置。需要明确的是,JVM是基于栈的架构(还有事基于寄存器架构),所以任何操作都需要通过入栈和出栈来完成。Java中虚拟机的多线程是通过线程轮流切换的方式来执行,因此当发生线程切换后需要记住当前线程执行到了哪一步,这样以便线程再次切换回来继续执行之前的操作。多线程执行过程中会不停的发生线程上下文的切换,为了方便每个线程每次发生切换后都能够在正确的位置执行,单独的为每个线程分配一个程序计数器,这样不会在多线程环境下发生干扰,程序计数器是JVM中唯一一个不会发生OutOfMemoryErrror的区域。

Java栈:JVM是栈式结构,和PC计数器一样,它是线程私有的。Java栈用于存储栈帧,而栈帧中存储的是局部变量表、操作数栈、动态链接、方法出口等信息。JVM堆中存储的是对象实例,JVM栈中局部变量表用于存储各种原始数据类型、对象引用、以及返回类型等信息。Java栈允许被设置为固定大小或者动态扩展。如果栈被设置为固定大小,则一旦线程请求分配的栈容量超过JVM所允许的最大值时,会抛出*Error异常,反之抛出OutOfMemoryError。

本地方法栈:主要用于支持本地方法。java中很多方法的实现都被标注为native实际上是调用了本地方法或者说通过JNI方式调用底层C/C++方法。Java虚拟机规范并没有明确规定要求本地方法栈的具体实现方式,甚至有些虚拟机实现直接将本地方法栈和java栈合二为一。

Java堆:Java堆是被所有线程共享的一块内存区域,用于存储对象实例数据的内存区,同时也是GC执行垃圾回收的重点区域。GC的性能会在极大程序上影响堆区的使用,因此大家可以考虑,是否一定需要将对象的实例放在堆区。淘宝基于OpenJDK深度定制的TaoBaoVm,创新的设计了GCIH(GC invisible heap)实现off-heap,将生命周期较长的对象直接从堆区移到堆外,并且GC不能管理GCIH内部的java对象,来降低GC的回收频率,提升GC回收效率。除此之外,JVM自带的逃逸分析和栈上分配等优化技术,也降低了GC的回收频率和提升GC的回收效率。这样一来,堆区就不在是java对象内存分配的唯一区域了。
存储在堆中的对象可分为两类:一类是生命周期较短的瞬时对象,这类对象的创建和消亡都非常迅速。还有一类对象的生命周期非常长,可能会随着JVM一直存在。针对这两种不同生命周期的对象,分代垃圾回收因此而来。一般分为新生代和老年代。新生代又分为:Eden、From Survivor、To Survivor,这块后续准备对GC收集器进行一些记录的时候在详细说明。

方法区:和Java堆区一样,也是所有线程共享的区域,用来存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码缓存等信息。方法区需要和永久代区分开来。每个虚拟机对方法区的实现是不一样的,仅HotSpot用永久代来实现了方法区,这样可以对方法区和其他堆一样来实现垃圾回收,不用单独的去设计相关垃圾回收器。