java内存区域

java虚拟机栈

java虚拟机栈是一块线程私有的内存空间。它的生命周期与线程相同。线程执行的基本行为是函数调用,每次函数的调用都是通过java虚拟机传递的。每一次函数调用,都会有一个对应的栈帧用于存储局部变量表、操作数栈、动态链接及方法出口灯信息,该栈帧会被压入java虚拟机栈,每一个函数调用结束,都会有一个栈帧被弹出java栈。

当函数返回时,栈帧从Java栈中被弹出。java方法有两种返回形式,一种是正常的函数返回,使用return指令;另外一种是抛出异常。不管哪种方式,都会导致栈帧被弹出。
java虚拟机提供了参数-Xss来指定线程最大的栈空间,这个参数也直接决定了函数调用的最大深度。java虚拟机栈规定了两种异常情况:如果线程请求的栈深度大于虚拟机所允许的栈深度,将抛出StackOverflowError异常;如果虚拟机可以动态扩展,如果扩展时,无法申请到足够的内存,就会抛出OutOfMemoryError。
注:函数嵌套调用的层次在很大程度上由栈的大小决定,栈越大,函数可以支持的嵌套调用次数就越多。

  1. 局部变量表存放了编译器可知的各种基本数据类型,对象引用类型(不等同于对象本身,可能是一个指向对象起始地址的指针、也可能是指向一个代表对象的句柄或者其他与此对象相关的位置)。局部变量表中的变量只在当前函数中调用中有效,当函数调用结束之后,随着函数栈帧的销毁,局部变量表也会随之销毁。
  2. 栈帧中的局部变量表的槽位是可以重用的,如果一个局部变量表过了其作用域,那么在其作用域之后申明的新的局部变量就有可能会复用过期局部变量的槽位,从而达到节省资源的目的。
java堆

对于大多数应用来讲,Java堆是java虚拟机内存所管理的内存中最大的一块。java堆是被所有线程共享的一块内存区域。此内存区域的唯一目的是存放对象实例,几乎所有的对象都存放在堆中。通过垃圾回收机制,垃圾对象会被自动清理,而不需要显式的释放。
根据垃圾回收的不同,java堆可能有不同的机构,最为常见的一种构成是将整个java堆分为新生代和老年代。其中新生代存放新生对象或者年龄不大的对象,老年代则存放老年对象。新生代有可能分为Eden区,s0区、s1区,s0和s1也被称为from Survivor区和To Survivor区,他们是两块大小相等、可以互换角色的内存空间。如下图:
堆空间的一般结构
在绝大多数的情况下,对象首先分配在eden区,在一次新生代回收后,如果对象还存活,则会进入s01或者s02,之后,每进行一次新生代回收,如果对象还存活,他的年龄就会加1。当年龄达到一定的条件后,就会被认为是老年对象,进入老年代。
根据java虚拟机规定,java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可,就像我们的磁盘空间一样。在实现时,既可以实现成固定大小的,也可以是可扩展的吗,不过当前主流的虚拟机都可以按照可扩展来实现(通过Xmx和-Xms控制)。如果在堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。