不止面试02-JVM内存模型面试题详解


第一部分:面试题

本篇文章我们将尝试回答以下问题:

  1. 描述一下jvm的内存结构
  2. 描述一下jvm的内存模型
  3. 谈一下你对常量池的理解
  4. 什么情况下会发生栈内存溢出?和内存溢出有什么不同?
  5. 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