JVM面试都被问烂了,你还不懂吗?

一、JVM体系结构

在学习JVM之前,我们需要知道JVM虚拟机有哪些种类?目前比较主流的有HotSpot VM、J9 VM、Zing VM、JRockit VM等等,今天我们要讲的也就是大家普遍用的HotSpot VM。大家可以通过命令去查看我们使用的是什么种类的虚拟机。
在这里插入图片描述

首先我们先从整体上来看一下JVM主要的组成成分。我整理了一张比较适合记忆的JVM架构图,这张图简单易画,并且囊括了JVM重要的部分,几乎能解决80%关于JVM分析的问题。
在这里插入图片描述
通过这张图我们可以发现JVM被分为三大核心模块:

  1. 类加载器(Class Loader)
  2. 运行时数据区(Runtime Data Area)
  3. 执行引擎(Execution Engine)

接下来我就详细点出比较重要且面试必问的几个点。

二、深入探讨JVM核心模块

1、类加载器

  首先我们要知道类加载器的作用:其实类加载器就是将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然 后在堆中生成一个代表这个类的java.lang.Class对象,作为方法区中类数据的访问入口。说白了就是加载类,将Java类加载到Java虚拟机上去并执行。
  其次我们还需要认识一个名词叫类缓存:就是在标准的JavaSE类加载器可以按要求查找类,但一旦某个类被加载到类加载器中,它将维持加载(缓存)一段时间。不过JVM垃圾回收机制可以回收这些Class对象。换句话说就是类在被收回之前,只会被加载一次,这样就不需要反复加载同一个类了。
  类加载的过程主要分为加载、连接、初始化三大步,连接又可细分为验证、准备和解析三步,如下图。
在这里插入图片描述
每一步主要的任务:

  • 加载:通过路径名找到这个要加载的类,将它加载到JVM中,这是类加载的入口。
  • 验证:对这个类的字节码格式进行验证,验证是否符合被加载的要求
  • 准备:为类加载创造条件,比如为类的静态变量分配内存等等
  • 解析:将符号引用替换成直接引用
  • 初始化:对静态变量,静态代码块执行初始化工作

在Java中一共有四种类加载器:
1、引导类加载器(bootstrap class loader)
JVM自带的类加载器,负责java平台的核心库,并且这个类加载器使用C++实现的,不能直接获取。
2、扩展类加载器(extensions class loader)
它用来加载 Java 的扩展库。Java 虚拟机的实现会提供一个扩展库目录。该类加载器在此目录里面查找并加载 Java 类。
3、应用程序类加载器(application class loader)
根据Java应用的类路径来加载Java类的。一般来说Java应用的类都是它加载的。
4、用户自定义类加载器
程序员可以根据自己的需求继承java.lang.ClassLoader类的方式实现自己的类加载器。
  这么多类加载器那JVM怎么知道选择哪一个类加载器来加载我们的类呢?这就涉及到类加载过程一个重要的机制双亲委派机制,说白了就是往上找爹。当一个类加载器收到类加载的请求时,这个类加载器会将这个请求委托给父加载器去完成,一直向上委托,直到引导类加载器;引导类加载器会去判断,我是否能加载这个类,如果能,就加载这个类结束,否则就通知子加载器去加载,直至加载成功结束,如下图。在这里插入图片描述
面试题

  • 双亲委派机制有什么作用呢?
  1. 主要是为了安全性,防止用户自己写的Java类替换了Java核心类,如java.lang.String等等
  2. 也能防止同一个类被多个加载器加载,一旦父加载器加载,子加载器就没必要再次加载了。
  • 双亲委派机制能被打破吗?,如果能,如何打破?

  答:可以被打破,要想打破双亲委派机制首先需要自己写一个类继承ClassLoader类,然后重写里面的loadClass和findClass方法。

2、运行时数据区

  运行时数据区如文章开头那张图上画的一样,主要有五部分:

  1. Java堆:堆是一个不连续的内存空间,分配灵活,速度慢!因此堆主要存储Java的所有对象及其对应的实例变量和数组(其实数组也是对象)。另外每个JVM只有一个堆区域。
  2. 方法区:其实方法区也是特殊的堆,每个JVM也只有一个方法区,该区域主要存储静态变量、类和常量相关的信息等等。

因为Java堆和方法区都只有一个,因此这两部分都是被所有线程共享的,所存储的数据不是线程安全的。

  1. Java栈:由系统自动分配的,速度快,是一个连续的内存空间。主要存储8大基本类型、对象的引用和实例的方法。对于每个线程,将会创建单独的运行时堆栈存放该线程执行方法的信息(实际参数、局部变量等);对于每个方法被调用都会创建一个栈帧(存储局部变量、操作数、方法出口等),因此栈里面几乎不会存在垃圾。
  • 在面试里,栈帧也是一个经常被问的一个点!!!我在下图的栈中简单画了两个栈帧,帮助大家理解一下。

在这里插入图片描述

  1. PC程序计数器:它和栈相似每个线程都有单独的PC计数器。用于存储指向下一条指令的地址,也就是即将要执行的指令代码。
  2. 本地方法栈:本地方法栈用于管理本地方法的调用

对于以上三个部分,每个线程都将创建一个单独的组件。因此这三部分都是线程独占的,不共享资源,所存储的数据是线程安全的。

  现在我们通过一个简单的例子具体去看一下JVM内存主要存储的数据有哪些

package com.clazz;

/**
 * @author xsl
 */
public class Person
{
    static {
        System.out.println("Person类的静态代码块");
    }
    private String name;
    private int age;
    private String sex="男生";
    public void study(){
        String k = "局部变量";
        System.out.println("我在学习。。。");
    }

    public static void main(String[] args)
    {
        Person p1 = new Person();
        Person p2 = new Person();
    }
}

![在这里插入图片描述](https://img-blog.csdnimg.cn/20210504110458173.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L01vbmtleV9LaW5nX0dM,size_16,color_FFFFFF,t_70

3、执行引擎

  Java之所以可以跨平台,和JVM的执行引擎有密不可分的关系。执行引擎的任务主要就是把字节码指令解释/编译为对应平台上的本地机器指令。说白了,执行引擎就是充当了一个翻译官。
  它主要包含解释器、JIT编译器两部分。

  • 解释器(Interpreter):当Java虚拟机启动时会根据预定义的规范对字节码采用逐行解释的方式执行,将每条字节码文件中的内容“翻译”为对应平台的本地机器指令执行。
  • JIT编译器(JIT Compiler):就是把源代码编译成机器语言,相信学过编译原理的小伙伴都知道编译的六大步骤。

三、详细结构图

最后附上JVM详细的结构图,相信有了上述的知识储备,再来理解官方给的图就很容易了。
在这里插入图片描述

四、未来工作

  通过上面的介绍,想必许多小伙伴已经对JVM结构模型有一定的了解了。但是对于JVM的所有内容,上述知识只是冰山一角。比如我们在存储数据时,堆栈肯定不是无限大的,那肯定需要对栈里面的垃圾进行回收,这就涉及到JVM的垃圾回收GC;当有些垃圾没有及时被处理就会导致栈内存溢出,所以什么时候会发生栈溢出?栈溢出怎么办?既然JVM存在一些不足,我们是否需要对JVM进行优化呢?这就涉及到JVM的调优,那如何进行调优呢?等等这一系列问题都是需要我们去深入研究的,对于这些问题,我后面也会写一些文章详细去剖析解决这些问题的,希望大家多多支持。最后如果有不足或错误的地方,欢迎大家指正,您的点赞和评论是我前进的动力,谢谢大家。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值