每日一面——谈谈你对JVM的运行时数据区的理解
Posted 有理想的菜鸡
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了每日一面——谈谈你对JVM的运行时数据区的理解相关的知识,希望对你有一定的参考价值。
菜鸡每日一面系列打卡24天
每天一道面试题目
助力小伙伴轻松拿offer
坚持就是胜利,我们一起努力!
题目描述
谈谈你对JVM的运行时数据区的理解。
题目分析
从今日起,菜鸡的每日一面系列进军Java虚拟机(以下简称JVM)了。Java之所以是跨平台的语言,可以说JVM是最大的功臣,没有之一。JVM屏蔽了底层的细节,从而使Java达到了“一次编译,到处运行”的目的。
上面这段话如果说得通俗一点就是,我们编写的Java程序都是运行在JVM上的。接下来,就让我们从超高频面试题——JVM运行时数据区开始,逐步了解JVM这个熟悉又陌生的老朋友吧!
题目解答
首先需要明确的是,JVM其实是一个概念,而不是一个实物。用Java语言做一个形象的比喻,它更像是Java中的类,而不是对象实例。后续关于JVM的非概念层面的所有描述,如果不特殊指明,都是基于HotSpot(JVM的一种主流实现)来说的。
虚拟机是相对于物理机来说的一个概念,虚拟机不是看得见摸得着的机器,其本质其实就是一段程序,并没有想象的那么神秘,如果感兴趣,你甚至可以翻阅HotSpot的源码,你会发现,它主要是由C++实现的,这可能是基于效率的考量,也可能是历史原因,在此不作深究。这段话的主要目的就是说明,虚拟机其实就是一段相对来说比较复杂的代码而已,不要惧怕它。
既然JVM本质上就是一段程序,那么它最终还是要落到实际的物理机上运行,它可以管理物理机的一部分内存,并在执行Java程序时,将其划分为若干个不同的数据区域,这也就是我们今天要讲到的JVM运行时数据区。
经典的JVM运行时数据区主要分为五部分:程序计数器,虚拟机栈,本地方法栈,堆,方法区。这些区域有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是依赖用户线程的启动和结束而建立和销毁。
我们通过周志明老师的《深入理解Java虚拟机》第3版中的一张图,来仔细学习一下各个区域之间的关系。
这张图堪称经典,它简洁明了地阐释了JVM运行时数据区不同区域之间的关系,虽然随着时间的推移,HotSpot的更新会和这张图上表明的关系略有出入,但不能说这张图过时了,它是一种内存管理的设计思想,应该把这张图深深印在脑海中好好消化。
接下来,我们对这五个内存区域逐一进行概况。
程序计数器:是当前线程所执行的字节码的行号指示器。它是程序控制流的指示器,分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这个计数器来完成。它是线程私有的,同时,它也是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。
虚拟机栈:Java方法执行的线程内存模型。它是线程私有的,每个方法被执行的时候,JVM都会同步创建一个栈帧用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。在栈深度溢出或者栈扩展失败时分别抛出StackOverFlowError和OutOfMemoryError异常。
本地方法栈:与虚拟机栈类似,区别在于本地方法栈是为Native方法而非Java方法服务。在栈深度溢出或者栈扩展失败时分别抛出StackOverFlowError和OutOfMemoryError异常。
堆:用于存放对象实例。Java世界里“几乎”所有的对象实例都在这里分配内存。它是线程共享的,也是垃圾回收的主要场所。有关垃圾回收的具体知识将在后续文章中进行讲解。如果在Java堆中没有内存完成实例分配,并且堆也无法再扩展时,将会抛出OutOfMemoryError异常。
方法区:用于存储已被虚拟机加载的类型信息、常量、静态变量、即时编译器编译后的代码缓存等数据。它是线程共享的。如果方法区无法满足新的内存分配需求时,将抛出OutOfMemoryError异常。
参考资料
《深入理解Java虚拟机》第3版 周志明
以上便是菜鸡对JVM运行时数据区的一些总结,供大家参考。
学习 | 工作 | 分享
以上是关于每日一面——谈谈你对JVM的运行时数据区的理解的主要内容,如果未能解决你的问题,请参考以下文章