面试题首页 > JVM面试题

JVM内存模型面试题

001JDK1.6、JDK1.7、JDK1.8 内存模型演变?

JDK1.7相对于JDK1.6,主要的变化就是将永久代中的字符串常量池移到堆内存中,交由堆管理。我们知道堆是JVM内存管理的主要区域,那么将字符串常量池放到堆内存中更方便高效的对字符串常量进行管理和垃圾回收。
而JDK1.8相对于JDK1.7来说,主要区别有两点,一是将虚拟机栈和本地方法栈合二为一了,二是移除永久代,增加了元数据区,元数据区使用本地内存,只受计算机内存大小的限制。而永久代使用的还是堆内存空间,受堆内存大小的限制。

002什么是程序计数器?

记录正在执行的虚拟机字节码指令的地址。为了线程切换后能恢复到正确的位置,每个线程都需要一个独立的程序计数器,各个线程之间互不影响,独立存储,这也就是所谓的“线程私有区域”。

003什么是虚拟机栈?

描述方法执行的内存模型,每个方法在执行时都会创建一个栈帧,每个栈帧存放的是局部变量表,操作数栈,动态链接,方法出口等信息,方法被调用到执行完成对应的是一个栈帧从入栈到出栈的过程。是线程私有的。

004什么是本地方法栈?

为虚拟机使用到的Native方法服务。注在HotSpot虚拟机中直接就把本地方法栈和虚拟机栈合二为一了。

005什么是方法区?

存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。在jdk1.8中已经去除了永久代,改用只受计算机本地内存大小限制的元空间来实现方法区,元空间参数(-XX:MetaspaceSize=512M -XX:MaxMetaspaceSize=1024M)。

006什么是运行时常量池?

运行时常量池是方法区的一部分。Class文件中除了有类的版本、字段、方法、接口等描述信息外,还有一项信息是常量池,用于存放编译器生成的各种字面量和符号引用,这部分内容将在类加载后进入方法区的运行时常量池中存放。

007Java 8 中 PermGen 为什么被移出 HotSpot JVM了?

1. 由于PermGen内存经常会溢出,引发恼人的 java.lang.OutOfMemoryError: PermGen,因此 JVM 的开发者希望这一块内存可以更灵活地被管理,不要再经常出现这样的 OOM。
2. 移除 PermGen 可以促进 HotSpot JVM 与 JRockit VM 的融合,因为 JRockit 没有永久代。

008i++和++i的底层区别?

i++:刚开始学java时候,i++先使用i,然后再自加1,为什么是这样呢?
底层步骤:
1.从局部变量表取出 i 并压入操作数栈。(入栈)
2.然后对局部变量表中的i自增1,将操作栈栈顶值取出使用。(自加1,出栈)
3.最后使用操作数栈的栈顶值更新局部变量表,如此线程从操作栈读到的是自增之前的值。(更新)
++i:刚开始学java时候,++i先自加1然后再使用i,为什么是这样呢?
----------------------------------------------------------
底层步骤:
1.先对局部变量表的 i 自增 1。(自加1)
2.然后取出并压入操作数栈。(入栈)
3.再将操作栈栈顶值取出使用。(出栈)
4.最后使用栈顶值更新局部变量表,线程从操作栈读到的是自增之后的值。(更新)
--------------------------------------------
之前之所以说 i++ 不是原子操作,即使使用 volatile 修饰也不是线程安全,就是因为可能 i 被从局部变量表取出,压入操作数栈,操作数栈中自增,使用栈顶值更新局部变量表(寄存器更新写入内存),其中分为 3 步,volatile 保证可见性,保证每次从局部变量表读取的都是最新的值,但可能这 3 步可能被另一个线程的 3 步打断,产生数据互相覆盖问题,从而导致 i 的值比预期的小。

009什么时候抛出StackOverflowError?

如果线程请求的栈深度大于虚拟机所允许的深度,则抛出StackOverflowError。

010什么情况下会出现堆内存溢出?

堆内存存储对象实例。我们只要不断地创建对象。并保证gc roots到对象之间有可达路径来避免垃圾回收机制清除这些对象。就会在对象数量到达最大。堆容量限制后,产生内存溢出异常。

011如何实现一个堆内存溢出?

public class Cat {
    public static void main(String[] args) {
        List list = new ArrayList();
        while (true) {
            list.add(new Cat());
        }
    }
}

012什么情况下会发生栈内存溢出?

1.栈是线程私有的,栈的生命周期和线程一样,每个方法在执行的时候就会创建一个栈帧,它包含局部变量表、操作数栈、动态链接、方法出口等信息,局部变量表又包括基本数据类型和对象的引用;
2.当线程请求的栈深度超过了虚拟机允许的最大深度时,会抛出StackOverFlowError异常,方法递归调用肯可能会出现该问题;
3.调整参数-xss去调整jvm栈的大小;

013堆是分配对象的唯一选择么?

在Java虚拟机中,对象是在Java堆中分配内存的,这是一个普遍的常识。但是,有一种特殊情况,那就是如果经过逃逸分析(Escape Analysis)后发现,一个对象并没有逃逸出方法的话,那么就可能被优化成栈上分配。这样就无需在堆上分配内存,也无须进行垃圾回收了。这也是最常见的堆外存储技术。
1)当一个对象在方法中被定义后,对象只在方法内部使用,则认为没有发生逃逸。
2)当一个对象在方法中被定义后,它被外部方法所引用,则认为发生逃逸。例如作为调用参数传递到其他地方中。

014运行时数据区,是否存在Error和GC?

运行时数据区 是否存在 是否存在
程序计数器
虚拟机栈
本地方法栈
方法区 是(OOM)

目录

返回顶部