jvm(n):JVM面试

Jvm内存结构,一般是面试官对Java虚拟机这块考察的第一问。

Java虚拟机的内存结构一般可以从线程共有线程私有两部分起头作答,然后再详细说明各自的部分,类似树状结构的作答,好处就是思路清晰,面试官听着也舒服。

线程共有的包括Java堆和方法区,线程私有的包括虚拟机栈、本地方法栈和程序计数器。

Java堆是用于存放Java程序运行时所需的对象等数据,Java堆又分为新生代和老年代。我们平常所说的垃圾回收,主要回收的就是堆区。更细一点划分新生代又可划分为Eden区和2个Survivor区(From Survivor和To Survivor)。

方法区中最为重要的是类的类型信息、常量池、域信息、方法信息。总之,方法区保存的信息,大部分来自于 class 文件,是 Java 应用程序运行必不可少的重要数据。

程序计数器用于存放下一条运行的指令,这里是唯一无内存溢出的区域。如果当前程序正在执行一个Java方法,则程序计数器记录正在执行的Java字节码地址,如果当前线程正在执行一个Native方法,则程序计数器为空。

虚拟机栈和本地方法栈用于存放函数调用堆栈信息。虚拟机执行java程序的时候,每个方法都会创建一个栈帧,栈帧存放在java虚拟机栈中,通过压栈出栈的方式进行方法调用。区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。

当你回答Jvm内存结构,八九不离十,下一问就问你垃圾回收算法。

垃圾回收算法可以先回答引用计数法,回答完后说明一下此算法的缺点(无法解决互相引用问题),再引入标记-清除算法,再说一下它的缺点(空间碎片问题),然后说一下复制算法、标记-压缩算法如何解决空间碎片问题,最后说一下分代。到了这个时候,你完全可以举例新生代,老年代使用的是哪种算法,进行一个补充。一般回答到这,面试官就没得问了。如果还要继续深入,你可以了解垃圾回收器,CMS、G1、并行、串行等,以备不时之需。

引用计数法:引用计数法的实现很简单,对于一个对象A,只要有任何一个对象引用了A,则A的引用计数器就加1,当引用失效时,引用计数器就减一。只要对象A的引用计数器的值为0,则对象A就不能再被使用。

标记-清除算法:标记-清除算法将垃圾回收分为两个阶段:标记阶段和清除阶段。一种可行的实现是,在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象。然后,在清除阶段,清除所有未被标记的对象。

复制算法:将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存中的角色,完成垃圾回收。

标记-压缩算法:首先从根节点开始,对所有可达的对象做一次标记,但之后,它并不是简单的清理未标记的对象,而是将所有的存活对象压缩到内存空间的一端。之后,清理边界外所有的空间。

分代:将内存区域根据对象的特点分成不同的内存区域,根据每块区域对象的特征不同使用不同的回收算法,以提高垃圾回收的效率。

补充一个冷门的考点,也是面试官不一定问的问题。哪些可以作为GC中root的对象?这个问题的标准回答如下

1.被启动类加载的类和创建的对象

2.栈内存中引用的对象

3.方法区中静态和常量引用的对象

4.本地方法中JNI引用的对象

如何选择一个合适的垃圾收集器主要参考的就是GC策略的指标。包括以下几个部分:

吞吐量:指在应用程序的生命周期内,应用程序所花费的时间和系统总运行时间的比值。 举个例子,如果系统运行了100min,GC 耗时1min,那么系统的吞吐量就是(100-1)/100=99%。

垃圾回收器负载:和吞吐量正好相反,垃圾回收器负载指垃圾回收器耗时与系统运行总时间的比值。

停顿时间:指垃圾回收器正在运行时,应用程序的暂停时间。对于独占回收器而言,停顿时间可能会比较长。使用并发的回收器时,由于垃圾回收器和应用程序交替运行,程序的停顿时间会变短,但是,由于其效率很可能不如独占垃圾回收器,故系统的吞吐量可能会较低。

垃圾回收频率:指垃圾回收器多长时间会运行一次。一般来说,对于固定的应用而言,垃圾回收器的频率应该是越低越好。

反应时间:指当一个对象被称为垃圾后多长时间内,它所占据的内存空间会被释放。

堆分配:不同的垃圾回收器对堆内存的分配方式可能是不同的。一个良好的垃圾收集器应该有一个合理的堆内存区间划分。

科普完以上知识后,我简单说一下串行、并行、CMS、G1。

串行回收器主要有两个特点:第一:使用单线程进行垃圾回收;第二:独占式垃圾回收。并行回收器只是简单的把使用单线程进行垃圾回收改为多线程进行垃圾回收,它依旧是独占式垃圾回收。

CMS是Concurrent Mark Sweep的缩写,意为并发标记清除,从名称上可以得知,它使用的是标记-清除算法,同时它又是一个使用多线程并发回收的垃圾收集器。与CMS收集器相比,G1收集器是基于标记-压缩算法的。

那为什么CMS和G1不是独占式的垃圾回收器?

CMS工作时,主要步骤有:初始标记、并发标记、重新标记、并发清除和并发重置。其中初始标记和重新标记是独占系统资源的,而并发标记、并发清除和并发重置是可以和用户线程一起执行的。因此,从整体上来说,CMS收集不是独占式的,它可以在应用程序运行过程中进行垃圾回收。

以上来自公众号:乔戈里

1、JVM管理的内存结构是怎样的?

查看JVM运行时数据区

2、不同的虚拟机在实现运行时内存的时候有什么区别?

JVM运行时所需的内存区域,不同的虚拟机实现上有所不同,规范对于方法区的管理是最宽松的:

方法区在虚拟机启动的时候创建,虽然方法区是堆的逻辑组成部分,但是简单的虚拟机实现可以选择在这个区域不实现垃圾收集与压缩。本版本的规范也不限定实现方法区的内存位置和代码编译的管理策略。方法区的容量可以是固定的,也可以随着程序执行的需求动态扩展,并在不需要过多的空间时自行收缩。方法区在实际内存空间站可以是不连续的。

虚拟机规范对方法区实现的位置并没有明确要求,在最著名的HotSopt虚拟机实现中(在Java 8 之前),方法区仅是逻辑上的独立区域,在物理上并没有独立于堆而存在,而是位于永久代中。这时候方法区也是可以被垃圾回收的。

在Java 8中 ,HotSpot虚拟机移除了永久代,使用本地内存来存储类元数据信息并称之为:元空间(Metaspace)

3、运行时数据区中哪些区域是线程共享的?哪些是独享的?

在JVM运行时内存区域中,PC寄存器、虚拟机栈和本地方法栈是线程独享的。

而Java堆、方法区是线程共享的。

Java堆其实还为每一个线程单独分配了一块TLAB空间,这部分空间在分配时是线程独享的,在使用时是线程共享的。(TLAB介绍)

4、除了JVM运行时内存以外,还有什么区域可以用吗?

直接内存。

在JDK 1.4中引入的NIO中,引入了一种基于Channel和Buffer的I/O方式,他可以使用Native函数直接分配堆外内存,然后通过一个存储在Java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。

5、堆和栈的区别是什么?

一个是线程独享的,一个是线程共享的,二者之间最大的区别就是存储的内容不同:

堆中主要存放对象实例。 栈(局部变量表)中主要存放各种基本数据类型、对象的引用。

6、Java中的数组是存储在堆上还是栈上的?

数组的实例是保存在堆中,而数组的引用是保存在栈上的。

7、Java中的对象创建有多少种方式?

Java中共有5种方式可以创建一个对象。

  1. 使用new关键字:User user = new User();

  2. 使用反射机制创建对象:User user = User.class.newInstance();

  3. 使用Constructor类的newInstance:Constructor<User> constructor = User.class.getConstructor(); User user= constructor.newInstance();

  4. 使用clone方法

  5. 反序列化的方式

8、Java中对象创建的过程是怎么样的?
  1. 虚拟机遇到new指令,到常量池定位到这个类的符号引用。

  2. 检查符号引用代表的类是否被加载、解析、初始化过。

  3. 虚拟机为对象分配内存。

  4. 虚拟机将分配到的内存空间都初始化为零值。

  5. 虚拟机对对象进行必要的设置。

  6. 执行方法,成员变量进行初始化。

9、Java中的对象一定在堆上分配内存吗?

逃逸分析的基本行为就是分析对象动态作用域:当一个对象在方法中被定义后,它可能被外部方法所引用,例如作为调用参数传递到其他地方中,称为方法逃逸。

如果JIT经过逃逸分析,发现有些对象没有逃逸出方法,那么有可能堆内存分配会被优化成栈内存分配。

10、如何获取堆和栈的dump文件?

Java Dump,Java虚拟机的运行时快照。将Java虚拟机运行时的状态和信息保存到文件。

可以使用在服务器上使用jmap命令来获取堆dump,使用jstack命令来获取线程的调用栈dump。

原文地址:https://www.cnblogs.com/angelica-duhurica/p/11380638.html