JVM的常见问题及答案

本文详细探讨了JVM的相关问题,包括为何学习JVM、JVM产品、构成部分,类加载器的工作原理,JVM内存的划分及作用,以及字节码和垃圾回收机制。内容涵盖类加载的双亲委派模型、内存区域的用途、对象分配内存过程,还涉及了逃逸分析、内存溢出和泄漏的原因,以及如何设计缓存。同时讲解了字节码的概念、增强技术及热替换实现。最后,讨论了GC的基本概念、判定对象为垃圾的方法以及常见的JVM配置参数。
摘要由CSDN通过智能技术生成

目录

1   入门部分

1.1  为什么要学习 JVM?

1.2  你了解哪些 JVM 产品?

         1.3  JVM 的构成有哪几部分?

2 类加载部分

2.1  你知道哪些类加载器?

2.2  为什么需要多个类加载器?

2.3  什么是双亲委派类加载模型?

2.4  双亲委派方式加载类有什么优势、劣势?

2.5  描述一下类加载时候的基本步骤是怎样的?

2.6  什么情况下会触发类的加载?

2.7  类加载时静态代码块一定会执行吗?

2.8  如何理解类的主动加载和被动加载?

2.9  为什么要自己定义类加载器,如何定义?

2.10  内存中一个类的字节码对象可以有多个吗?

3   JVM 运行内存部分

3.1  JVM 运行内存是如何划分的?

3.2  JVM 中的程序计数器用于做什么?

3.3  JVM 虚拟机栈的结构是怎样的?

3.4  JVM 虚拟机栈中局部变量表的作用是什么?

3.5  JVM 虚拟机栈中操作数栈的做用是什么?

3.6  JVM 堆的构成是怎样的?

3.7  Java 对象分配内存的过程是怎样的?

3.8  JVM 年轻代幸存区设置的比较小会有什么问题?

3.9  JVM 年轻代伊甸园区设置的比例比较小会有什么问题?

3.10  JVM 堆内存为什么要分成年轻代和老年代?

3.11  项目中最大堆和初始堆的大小为什么推荐设置为一样的?

3.12  什么情况下对象会存储到老年代?

3.13  Java 中所有的对象创建都是在堆上分配内存的?

3.14  如何理解 JVM 方法区以及它的构成是怎样的?

3.15  JDK8 中 Hotsport 虚拟机的方法区内存在哪里?

3.16  什么是逃逸分析以及可以解决什么问题?

3.17  如何理解对象的标量替换,为什么要进行标量替换?

3.18  什么是内存溢出以及导致内存溢出的原因?

3.19  什么是内存泄漏以及导致内存泄漏的原因?

3.20  JAVA 中的四大引用类型有什么特点?

3.21  项目中的哪些地方用到了缓存?

3.22  假如让你设计一个缓存你会考虑哪些问题?

4 字节码增强部分

4.1  什么是字节码?

4.2  为何要学习字节码?

4.3 如何解读字节码内容?

4.4 字节码内容由哪几部分构成?

4.5 什么是字节码增强?

4.6 为什么要进行字节码增强?

4.7 你了解哪些字节码增强技术?

4.8 什么是热替换以及如何实现?

5   JVM 垃圾回收部分

5.1  何为 GC?

5.2  为什么要 GC?

5.3  如何判定对象是否为垃圾?

5.4  你知道哪些 GC 算法?

5.5  JVM 中有哪些垃圾回收器?

5.6  如何查看 JVM 默认的垃圾收集器?

5.7  说出几个常用的 JVM 配置参数?

5.8  JAVA 中的堆区为什么要分代?

5.9  服务频繁 fullgc,younggc 次数较少,可能原因?

5.10  你知道哪些 JVM 小工具?


1   入门部分

1.1  为什么要学习 JVM?

第一:为了面试。(企业招聘战略在升级,业务和技术问的越来越身)

第二:为了更好的理解 JAVA。

第三:为了更好的解决线上问题。

1) 实现线上软件升级。(热替换)

2) 更好防止内存泄漏,提高内存的有效使用率。

3) 更好提高系统的吞吐量。

1.2  你了解哪些 JVM 产品?

Oracle 公司的 HotSpot。

IBM 公司的 J9。

阿里公司的 TaobaoVM。

1.3  JVM 的构成有哪几部分?

第一:类加载子系统(负责将类读到内存,校验类的合法性,对类进行初始化)

第二:运行时数据区(负责存储类信息,对象信息,执行逻辑)

第三:执行引擎(负责从指定地址对应的内存中数据然后解释执行以及 GC 操作)

第四:本地库接口(负责实现 JAVA 语言与其它编程语言之间的协同)

2 类加载部分

2.1  你知道哪些类加载器?

第一:BootStrapClassLoader

第二:ExtClassLoader

第三:AppClassLoader

第四:自定义 ClassLoader

2.2  为什么需要多个类加载器?

       每个类加载器都有自己的加载职责,负责从不同位置加载我们所需要的类,同时可以基于需求进行懒加载(按需加载),例如:

1) 加载基础类库(核心类库)。

2) 扩展类库。

3) 三方类(MyBatis,Spring,…..)。

4) 自己的类。

2.3  什么是双亲委派类加载模型?

        所谓双亲委派模型可以简单理解为向上询问、向下委托。当我们的类在被加载时,首先会询 问类加载器对象的 parent 对象(两者之间不是继承关系),是否已经加载过此类,假如当前 parent 没有加载过此类,则会继续向上询问它的 parent,依次递归。如果当前父加载器可 以完成类加载则直接加载,假如不可以则委托给下一层类加载器去加载(可以理解为逐层分 配任务)。

2.4  双亲委派方式加载类有什么优势、劣势?

     通过双亲委派类加载机制,保证同一个类只能被加载一次,同时也是对类资源的一种保护。 例 如我们自己也写了 一 个 java.lang.Object 类 ,为了保证Java官方的  java.lang.Object 类加载后不再加载我们的 Object 就可以使用双亲委派机制。但是这里也有一个缺陷,例如:我们同一个 JVM 下有多个项目,但是不同项目中有包名类名都相同的类(类中的内容是不同的),此时只能有一个项目中的类会被加载,其它项目则无法加载。还有这种双亲委派模型可能会因为向上询问和向下委托,多少会影响一些性能。

2.5  描述一下类加载时候的基本步骤是怎样的?

第一:查找类(例如通过指定路径+类全名找到指定类)

第二:读取类(通过字节输入流读取类到内存,并将类信息存储到字节数组)

第三: 对字节数组中的信息流进行校验分析以及初始化并将其结构内容存储到方法区。

第四: 创建字节码对象(java.lang.Class),基于此对象封装类信息的引用,基于这些引用获取方法区类信息。

2.6  什么情况下会触发类的加载?

       我们可以将类的加载分为显式加载和隐式加载,显式加载是通过类加载器的 loadClass 方法或 Class 类的 forName 方法直接对类进行加载。隐式加载一般指构建对象、访问类中属性或方法时触发的类加载。

2.7  类加载时静态代码块一定会执行吗?

       不一定,静态代码块是否会执行,取决于类加载时,是否会执行类初始化。

2.8  如何理解类的主动加载和被动加载?

主动加载:访问本类成员或方法时触发的加载。

被动加载:访问本类(当前类)对应的父类属性时,本类(当前类)属于被动加载。被动加载不会触发当前类的初始化。 

2.9  为什么要自己定义类加载器,如何定义?

当系统提供的类加载器不能完全满足我们需求时,我们可以考虑自定义类加载器,例如:

1) 指定加载源头?(系统提供的类加载器已经确定了从哪些位置加载对应类,假如我们的类不在指      定范围内呢?)

2) 保证类安全?(可以在类编译时对字节码进行加密,类加载时对字节码进行解密)

3) 打破双亲委派模型?(同一个 JVM 下有多个项目时,假如不同项目中有相同名字的类, 这些类都需要加载。)

2.10  内存中一个类的字节码对象可以有多个吗?

     可以,即使是同一个类,但是它的类加载器不同,生成的字节码对象也不会相同。

3   JVM 运行内存部分

3.1  JVM 运行内存是如何划分的?

       JVM 运行时内存有方法区(Method Area)、堆区(Heap)、Java 方法栈(Stack)、本地方法栈、程序计数器(寄存器)。

3.2  JVM 中的程序计数器用于做什么?

Java 中每个线程都有一个程序计数器,为线程私有,用于记录程序执行时的字节码指令地址。

3.3  JVM 虚拟机栈的结构是怎样的?

Java 中每个线程有一个虚拟机栈(Java 方法栈),每个方法的执行和退出会对应着一次入栈 (Push)和出栈(Pop)操作。这个栈中的元素为一个一个的栈帧对象,这个栈帧有如下几部分构成:

1) 操作数栈(用于执行运算,例如两个变量值的加减)

2) 局部变量表(用于存储方法内的局部变量,对于实例方法,局部变量表的第 0 个位置为 this)

3) 方法返回值(记录调用方法的返回值)

4) 动态链接(方法中可以调用其它方法,如何找到要调用的方法?)

5) 其它信息

3.4  JVM 虚拟机栈中局部变量表的作用是什么?

       局部变量标底层实现是一个数组,用于存储方法内的局部变量。对于 main 方法而言,方法中的 args 这个变量会存储在局部量表下标为 0 的位置。对于实例方法,局部变量下标为 0 位置存储的是 this。

3.5  JVM 虚拟机栈中操作数栈的做用是什么?

       最核心的作用是进行计算。JVM 的执行引擎可以基于程序计数器中指令的地址找到具体指令,然后执行。在执行这些指令时,可以将指令对应的数据放到操作数栈、也可以从操作栈将数据取出存储到局部变量表,还可以将局部变量表中的数据取出,进行计算,将计算的结果再存储到操作数栈中。

3.6  JVM 堆的构成是怎样的?

       JVM 堆主要用于存储我们创建 Java 对象,从由年轻代(Young 区)和老年代(Old 区)构成,年轻代又分伊甸园区和两个幸存区。

3.7  Java 对象分配内存的过程是怎样的?

1) 编译器通过逃逸分析(JDK8 已默认开启),确定对象是在栈上分配还是在堆上分配。

2) 如果是在堆上分配,则首先检测是否可在 TLAB(Thread Local Allocation Buffer) 上直接分配。

3) 如果 TLAB 上无法直接分配则在 Eden 加锁区(CAS 算法进行加锁)进行分配(线程共享区)。

4) 如果 Eden 区无法存储对象,则执行 Yong GC(Minor Collection)。

5) 如果 Yong GC 之后 Eden 区仍然不足以存储对象,则直接分配在老年代。

3.8  JVM 年轻代幸存区设置的比较小会有什么问题?

       伊甸园区被回收时,对象要拷贝到幸存区,假如幸存区比较小,拷贝的对象比较大,对象就会直接存储到老年代,这样会增加老年代 GC 的频率。而分代回收的思想就会被弱化。

3.9  JVM 年轻代伊甸园区设置的比例比较小会有什么问题?

       我们程序中新创建的对象,大部分要存储到伊甸园区,假如伊甸园设置的比较小,会增加 GC 的频率,可能会

  • 19
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 17
    评论
评论 17
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值