java虚拟机在执行java程序的过程中会把它所管理的内存划分为若干个不同的区域。
根据java虚拟机规范的规定,java虚拟机所管理的内存包括线程私有的虚拟机栈(VM Stack)、本地方法栈(Native Method Stack)、程序计数器(Program Counter Register)和线程共享的堆(Heap)、方法区(Method Area)。
java虚拟机所管理线程私有的内存,它的生命周期与线程相同。包括以下内存模型:
虚拟机栈描述的是java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程就对应着一个帧栈在虚拟机中从入栈到出栈的过程。虚拟机栈占用的为操作系统的内存。当虚拟机方法栈空间不足时,会抛出StackOverflowError的错误,在Sun JDK中可通过-Xss来指定其大小。
本地方法栈与虚拟机栈所发挥的作用非常相似,其区别是虚拟机栈为虚拟机执行java方法(即字节码)服务,而本地方法栈是为虚拟机使用到的Native方法服务。
堆在虚拟机启动时创建,存放对象实例及数组值,是虚拟机管理的内存中最大的一块。为了让内存回收更加高效,SUN JDK从1.2开始对堆中的内存进行分代管理,分为新生代(New Generation)和旧生代(Old Generation或Tenuring Generation) 。
大多数情况下java程序中新建的对象都是从新生代分配内存,旧生代用于存放新生代中经过多次垃圾回收仍然存活的对象,例如缓存对象,新建的对象也有可能在旧生代上直接分配内存。主要有两种情况:大对象和大的数组对象,可以通过在启动参数上设置-XX:PretenureThreshold=1024(单位为字节,默认为0)代表超过多大就在旧生代分配。
堆是线程共享的,因此在堆上分配内存需要加锁,这导致创建对象开销比较大。当堆上空间不足时,会触发GC,如果GC后空间仍然不足,则抛出OutOfMemory错误信息。SUN JDK为了提升内存分配的效率,会为每个新创建的线程在新生代的Eden Space(属于新生代的内存)上分配一块独立的控件,这块控件称为TLAB(Thread Local Allocation Buffer),其大小根据jvm计算所得,在TLAB上分配内存时不需要加锁,因此jvm在给线程中的对象分配内存时会尽量在TLAB上分配,如果对象太大或TLAB已经用完,则仍然在堆上进行分配。
方法区存放的是已被虚拟机加载的类信息、常量、静态变量、即时编译后的代码等数据。在sun jdk1.8之前这块区域代表Permanet Generation,又称为持久代,默认最小值为16MB,最大值为64MB,可通过-XX:PermSize及-XX:MaxPermSize来指定最小值和最大值,在jdk1.8中持久代被元数据区代替,没有持久代了。