面试必问的JVM知识-JVM特性学习
运行时数据区Java虚拟机在执行Java程序的过程中会将其管理的内存划分为若干个不同的区域 , 这些区域有各自的用途 , 以及创建和销毁的时间 , 有的区域随着虚拟机进程的启动而一直存在 , 有些区域则是依赖用户线程的启动和结束而建立和销毁 。 Java虚拟机所管理的内存包括下面几个区域 。
文章插图
内存划分
其中阴影部分的为所有线程共享的数据区 , 非阴影部分的为线程隔离的数据区 。
程序计数器程序计数器(Program Counter Register)是一块较小的内存空间 , 它可以看作是当前线程所执行的 字节码的行号指示器 , 字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令 , 它是程序控制流的指示器 , 分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成 。
在任何一个时间点 , 一个处理器只会执行一条线程中的指令 。 为了线程切换后能回到正确的位置 , 每条线程都需要有一个独立的程序计数器 , 各线程之间的计数器互不影响 。 所以说程序计数器这部分的内存空间是每个线程私有的 。
还有要注意:当线程正在执行的是一个Java方法 , 这个计数器记录的是正在执行的虚拟机字节码指令的地址;当正在执行的是本地(Native)方法 , 这个计数器值则应为空(Undefined) 。 并且这块内存区域不会出现OutOfMemoryError 。
Java虚拟机栈Java虚拟机栈(Java Virtual Machine Stack)为线程私有的 , 它的生命周期与线程相同 。 每个方法被执行的时候 , Java虚拟机都会同步创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态连接、方法出口等信息 。 每一个方法被调用直至执行完毕的过程 , 就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程 。
文章插图
Java虚拟机栈
局部变量表存放了编译期可知的各种Java虚拟机基本数据类型(boolean、byte、char、short、int、 float、long、double)、对象引用 。
局部变量表所需要的内存在编译期间就完成了分配 , 在方法运行期间不会改变局部变量表的大小 。
如果线程请求的栈深度大于虚拟机所允许的深度 , 将抛出StackOverflowError异常;如果Java虚拟机栈容量可以动态扩展 , 当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常 。
本地方法栈本地方法栈为虚拟机使用到的本地(Native)方法服务 。 其作用于Java虚拟机栈相似 。 当然 , 本地方法栈也会在栈深度溢出或者栈扩展失败时分别抛出StackOverflowError和OutOfMemoryError异常
Java堆Java堆是被所有线程共享的一块内存区域 , 在虚拟机启动时创建 。 此内存区域的唯一目的就是存放对象实例 , Java世界里"几乎"所有的对象实例都在这里分配内存 。 当然随着技术发展 , 对象实例也有可能在其他地方分配 。
将Java堆细分的目的只是为了更好地回收内存 , 或者更快地分配内存 。
Java堆可以处于物理上不连续的内存空间中 , 但在逻辑上它应该被视为连续的 。 但对于大对象(典型的如数组对象) , 多数虚拟机实现出于实现简单、存储高效的考虑 , 很可能会要求连续的内存空间 。
如果在Java堆中没有内存完成实例分配 , 并且堆也无法再扩展时 , Java虚拟机将会抛出OutOfMemoryError异常 。
文章插图
堆
上图为Java堆中的结构 。
关于对象在堆中的分配原则:
对象优先在Eden区分配
大对象直接进入老年代
长期存活的对象将进入老年代
推荐阅读
- Java学习:Java学习到什么程度可以进行面试
- 程序员面试金典17.05_go_字母与数字
- 安卓春招面经:二本渣院面试网易被拒,最终获腾讯阿里offer
- 「6」进大厂必须掌握的面试题-Hibernate
- 震惊!京东T4大佬面试整整三个月,才写了两份java面试笔记
- 安卓面试必备的JVM虚拟机制详解,看完之后简历上多一个技能
- 面试官问:MySQL 的自增 ID 用完了,怎么办?
- 「3」Java面试-Servlet
- 大数据hbase面试宝典(三)
- jvm系列五:垃圾回收机制之对象进入老年代时机