Java中关于虚拟机的面试题

267 阅读10分钟

以下面试题是基于网络整理的一些高频的关于java虚拟机面试题目,值得大家去观看。

1、什么是java虚拟机

java虚拟机,是一个可以执行java字节码的虚拟机程序。java源文件会被编译成能够被java虚拟机执行的字节码文件。(.class),也是因为有java虚拟机的存在,所以java程序才能跨平台运行,因为java虚拟机屏蔽了程序与各计算机软硬件的差异,由java虚拟机来进行调配。

2、JVM由哪些部分组成。

JVM的结构基本上由4个部分组成:

类加载器,在JVM启动时或者类运行时将需要的class文件加载到JVM中

运行时数据区域,将内存划分为若干个以模拟实际机器上的存储、记录和调度功能模块,如实际机器上的各种功能的寄存器或者PC指针的记录器等。

执行引擎,执行引擎的任务是负责执行class文件中所包含的字节码指令,相当于实际机器上的CPU

本地方法调用,调用C或C++实现的本地方法的代码返回执行结果。

3、JVM运行时数据区域的分类

程序计数器:java线程私有,类似于操作系统的PC计数器,它可以看做是当前线程所执行的字节码的行号指示器。如果线程正在执行的是一个java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果正在执行的是Native方法,这个计数器值就为空。

虚拟机栈(栈内存):java线程私有,虚拟机栈描述的是java方法执行的内存模型。每个方法在执行的时候,都会创建一个栈帧用于存储局部变量、操作数、动态链接、方法出口等信息。每个方法调用都意味着一个栈帧在虚拟机栈中入栈到出栈的过程。

本地方法栈:和java虚拟机栈的作用类似,区别是该区域为JVM提供使用Native方法的服务。

堆内存:所有线程共享的一块区域,垃圾收集器管理的主要区域。目前主要的垃圾回收算法都是分代收集算法,所以java堆中还可以细分为:新生代和老年代;再细致一点的有Eden空间、From Survivor空间、To Survivor空间,默认情况新生代按照8:1:1的比例来分配。

方法区:也是各个线程共享的一个区域,用于存储虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

4、直接内存是不是虚拟机运行时数据区的一部分?

直接内存并不是虚拟机运行时数据区的一部分,在JDK1.4中新加入了NIO类,引用了一种基于通道(Channel)和缓冲区(Buffer)的I/O模式,它可以用native函数库中介分配堆外内存,然后通脱一个存储在java堆中的DirectByteBuffer对象作为这块内存的引用进行操作。这样能在一些场景中显著提高性能,因为避免了在Java堆和Native堆中来回复制数据。

5、直接内存与堆内存的比较?

1、直接内存申请空间耗费更高的性能,当频繁申请到一定量时尤为明显。 2、直接内存IO读写的性能要优于普通的堆内存,在多次读写操作的情况下差异明显。

6、垃圾收集主要针对哪些模块

垃圾收集主要针对堆和方法区进行的。程序计数器、虚拟机栈、本地方法栈这三个区域属于线程私有的,只存在于线程的生命周期内,线程结束之后就会消失,因此不需要对这三个区域进行垃圾回收。

7、判断一个对象是否可被回收

1、引用计数算法

为对象添加一个引用计数器,当对象增加一个引用时计数器加1,引用失效时计数器减1.引用计数为0的对象可被回收。但是当两个对象循环引用的情况下,此时引用计数器永远不为0,意味此对象着永远不可能被回收,因此java虚拟机不使用引用计数算法。

2、可达性分析算法

为了解决引用计数法的循环引用问题,java使用了可达性分析的方法。通过一系列的“GC roots”对象作为起点搜索。如果在“GC roots” 和一个对象之间没有可达路径,则称该对象不可达的。要注意的是,不可达对象不等价于可回收对象,不可达对象变为可回收对象至少要经过两次标记过程。两次标记后仍然是可回收对象,则将面临回收。

8、垃圾回收的各种算法

1、标记清除算法

最基础的垃圾回收算法,分为两个阶段,标注和清除。标记阶段标记出所有需要回收的对象,清除阶段回收被标记的对象所占用的空间。此算法最大的问题是内存碎片化严重,后续可能会发生大对象不能找到可利用空间的问题。

2、复制算法

为了解决标记清除算法内存碎片化的缺陷而被提出的算法。按内存容量将内存划分为两个内存区域,当一个内存区域用满时,将该区域还存活的对象复制到另一个内存去,然后把该内存清除。这种算法虽然实现简单,内存效率高,不易产生碎片,但是最大的问题是内存的使用容量减少了一半,且如果存活对象过多,复制会耗费大量资源,使其效率大大降低。

3、标记整理算法

结合了以上两种算法,结合了其优点,也是分为两个阶段,标记阶段与标记清除算法相同,标记后不是删除对象,而是将存货对象移到内存的一端,然后清除边界外的对象。

4、分代收集算法

分代收集算法是目前大部分jvm所财通的方法,其核心思想是根据对象存活的不同生命周期将内存划分为不同的域,一般情况下将GC堆划分为老生代和新生代。老生代的特点是每次垃圾回收时只有少量对象需要被回收,新生代的特点是每次垃圾回收都有大量垃圾需要被回收,因此可以根据不同区域选择不同的算法。

4.1 、新生代与复制算法

目前大部分jvm的GC对于新生代都采用复制算法,因为新生代中每次垃圾回收都要回收大部分对象,即要复制的操作比较少,但通常并不是按照1:1来划分新生代。一般将新生代划分为一块较大的Eden空间和两个较小的Survivor空间(From Space,To Space),每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将该两块空间中还存活的对象复制到另一块Survivor空间中。

4.2、老年代与标记复制算法

老年代因为每次只回收少量对象,因而采用标记复制算法

1.java虚拟机提到过的处于方法区的永生代,它用来存储class类,常量,方法描述等。对永生带的回收主要包括废弃常量和无用的类。
2.对象的内存分配只要在新生代的Eden Space 和Survivor Space的From Space中,少数情况会直接分配到老生代。
3.当新生代的Eden Space 和From Space空间不足时就会发生一次GC,进行GC后,Eden Space和From Space区的存货对象会被挪到To Space,然后将Eden Space 和 From Space进行清理。
4.如果To Space无法足够存储某个对象时,则将这个对象存储到老生代。
5.在进行GC后,使用的便是Eden Space 和To Space了,如此反复循环。
6.当对象在Survivor区躲过一次GC后,其年龄就会+1。默认情况下年龄到达15的对象会移到老生代中。

9、java4种引用类型

1.强引用

在java中最常见的就是强引用,把一个对象赋值给一个引用变量,这个引用变量就是一个强引用,当一个对象被强引用变量引用时,它处于可达状态,它是不可能被垃圾回收机制回收的,即使该对象以后都不会被用到jvm也不会回收。因此强引用是造成java内存泄漏的主要原因之一。

2.软引用

软引用需要用SoftReference类来实现,对于只有软引用的对象来说,当系统内存足够时它不会被回收,当系统内存空间不足时它会被回收。软引用通常用在对内存敏感的程序中。

3.弱引用

弱引用需要用WeakReference类来实现,它比软引用的生存期更短,对于只有软引用的对象来说,只要垃圾回收机制一运行,不管jvm的内存空间是否足够,总会回收该对象占用的内存。

4.虚引用

许引用需要PhantomReference类来实现,它不能单独使用,必须和引用队列联合使用。许引用的主要作用是跟踪对象被垃圾回收的状态。

10、JVM类加载机制

JVM类加载主要分为五个阶段:加载、验证、准备、解析、初始化

  • 加载是类加载过程中的第一个阶段,这个阶段会在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据入口。
  • 验证这个阶段主要目的是确保Class文件的字节流包含的信息是否符合虚拟机的要求,以及不会危害到虚拟机的安全。
  • 准备阶段是正式为类变量分配内存并设置类变量的初始值阶段,即在方法区分配这些类变量所用的内存空间。
  • 解析阶段是指虚拟机将常量池中的符号引用替换成直接引用的过程。
  • 初始化阶段是类加载的最后一个阶段,是执行类构造器< client>方法的过程。< client>方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证子方法执行之前,父类的< client>方法已经执行完毕,如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器不会为这个类生成< client >()方法。