在进行学员回访的时候,碰巧提到一些Java工程师面试题:Java的堆,栈,方法区你都楚吗?这里上海Java培训班学员在面试时都这样做了回答。
问题分析:Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。回答套路熟悉时,几个方面都会说到。
程序计数器
上海Java培训班:程序计数器(Program Counter
Register)是一块较小的内存空间,它可以看做是当前线程所执行的字节码的行号指示器。字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。字节码指令、分支、循环、跳转、异常处理、线程恢复等基础功能都要依赖这个计数器来完成。每条线程都有一个独立的程序计数器,各条线程之间计数器互不影响,独立存储。如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器值则为空(Undefined)。此内存区域是唯一一个在Java虚拟机中没有规范任何OutOfMemoryError情况的区域。
Java虚拟机栈
1. Java虚拟机栈也是线程私有的,它的生命周期与线程相同(随线程而生,随线程而灭)。2.
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩展,如果扩展时无法申请到足够的内存,就会抛出OutOfMemoryError异常;(当前大部分JVM都可以动态扩展,只不过JVM规范也允许固定长度的虚拟机栈)。3.
Java虚拟机栈描述的是Java方法执行的内存模型:每个方法执行的同时会创建一个 栈帧。 对于我们来说,主要关注的stack栈内存,就是虚拟机栈中
局部变量表部分。
栈帧
上海Java培训班:栈帧是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧用于存储
局部变量表、操作数栈、动态链接、方法返回等信息。
每个方法从调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。在活动线程中,只有位于栈顶的栈帧才是有效的,称为
当前栈帧,与这个栈帧相关联的方法称为 当前方法。执行引擎运行的所有字节码指令都只针对当前栈帧进行操作。
本地方法栈
本地方法栈(Native Method
Stack)与虚拟机栈所发挥的作用是非常相似的,它们之间的区别不过是虚拟机栈为虚拟机执行Java方法服务(也就是字节码)服务,而本地方法栈为虚拟机使用到的Native方法服务。Java虚拟机规范对本地方法栈使用的语言、使用方法与数据结构并没有强制规定,因此可以由虚拟机自由实现。例如:HotSpot虚拟机直接将本地方法栈和虚拟机栈合二为一。同虚拟机栈相同,Java虚拟机规范对这个区域也规定了两种异常情况StackOverflowError
和 OutOfMemoryError异常。
堆
上海Java培训班:1. Java堆是被所有线程共享的一块内存区域,在虚拟机启动时创建,是虚拟机所管理的内存中最大的一块。此内存区域的唯一目的就是
“存放对象实例和数组”,几乎所有的对象实例和数组都在这里分配内存。2. Java堆是垃圾收集器管理的主要区域,也称为GC
垃圾堆。后面会专门分析GC算法。从内存回收的角度看,由于现在收集器基本都采用分代收集算法,所以Java堆可以细分为:新生代、老生代;从内存分配的角度看,线程共享的Java堆可能划分出多个线程私有的分配缓冲区(TLAB);不论如何划分,都与存放的内容无关,无论哪个区域,存储的仍然是对象实例和数组。3. 如果在堆中没有内存完成实例分配,并且堆上也无法再扩展时,将会抛出OutOfMemoryError异常。4. 内存泄露和内存溢出。
内存泄露 : 指程序中动态分配内存给一些临时对象,但是对象不会被GC所回收,它始终占用内存。即 被分配的对象可达但已无用,可用内存越来越少。
内存溢出 :
指程序运行过程中无法申请到足够的内存而导致的一种错误。内存溢出通常发生于老年代或永久代垃圾回收后,仍然无内存空间容纳新的Java对象的情况。但这只能是内存溢出的一种诱因,不是唯一因素。