不止面试02-JVM内存模型面试题详解
第一部分:面试题
本篇文章我们将尝试回答以下问题:
- 描述一下jvm的内存结构
- 描述一下jvm的内存模型
- 谈一下你对常量池的理解
- 什么情况下会发生栈内存溢出?和内存溢出有什么不同?
- String str = new String(“abc”)创建了多少个实例?
第二部分:深入原理
ok,开始。怎们还是先讲原理,再说答案。如果时间不足,也可以直接跳到最后看答案。
本次分享我们主要围绕jvm内存结构展开,这也是java面试必考知识点之一。所以我们先来看看jvm内存结构到底是啥样子。
1. jvm内存模型
我们首先看下面这张图:
这张图是虚拟机的结构图,当我们在讨论jvm内存模型时,指的就是中间五彩的那条区域:运行时数据区(runtime data area)。
我们把这个区域单独画出来,如下图所示:
现在我们来看看每个区域的用途
1.1 栈(Stack)
每当一个线程去执行方法时,就会同时在栈里面创建一个栈帧(Stack Frame)用于存储局部变量表、操作数栈、动态链接、方法出口等。
每一个方法从被调用到执行完成的过程,都对应着一个栈帧从入栈到出栈的过程。
栈是线程私有的,每个线程在栈中保有自己的数据,别的线程无法访问。
在栈中我们可能遇到两种异常:StackOverflowError和OutOfMemoryError
StackOverflowError是指线程请求的栈深度大于虚拟机所允许的深度
OutOfMemoryError是指栈扩展时无法申请到足够的内存
这两种异常我们会在后面的文章中详细讲到。
1.2 本地方法栈(Native Method Stack)
本地方法栈和栈差不多,区别只在于本地方法栈为Native方法服务。
Native 方法就是一个Java调用非Java代码的接口,比如JNI。
本地方法栈也是线程私有的。
1.3 程序计数器(PC Register)
要理解程序计数器,我们需要知道java代码最终都要编译成一条条的字节码,然后由字节码解释器一条条的执行的。而程序计数器可以看作是当前线程所执行的字节码的行号计数器。
如果正在执行的是一个java方法,那么这个计数器记录的是正在执行的字节码指令地址。如果正在执行的是Native方法,那么计数器的值为Undefined。
程序计数器也是线程私有的,每条线程都有一个独立的程序计数器,各线程的程序计数器互不影响。
程序计数器是唯一一个不会OOM的内存区域。
1.4堆(Heap)
堆应该是java内存中占用空间最大的一个区域,大家喜闻乐见的垃圾回收就主要发生在这个区域。
堆的唯一作用就是存放对象,不过并非所有对象都在堆中。这个我们以后会讲到。
堆如果空间不足,就会抛出OOM异常。
堆是可以让多个线程共享的。
1.5 方法区(Method Area)
方法区也是可以让多个线程共享的。
方法区主要用来存放类的版本,字段,方法,接口、常量池和运行时常量池。
常量池里存储着字面量和符号引用。
还记得我们在JVM虚拟机种类
JVM的Heap Memory和Native Memory
JAVA8里的native heap究竟是个什么概念?
java用这样的方式生成字符串:String str = "Hello",到底有没有在堆中创建对象?
Java内存模型(JMM)总结
JVM内存溢出详解
系列文章总目录:https://mp.weixin.qq.com/s/56JgXLArTAEDj1f3y4arLA