Java面试之JVM篇(offer 拿来吧你)

现在关于Java面试的资料是层出不穷,对于选择困难症的同学来说,无疑是陷入了一次次的抉择与不安中,担心错过了关键内容,现在小曾哥秉持着"融百家之所长,汇精辟之文档"的思想,整理一下目前主流的一些八股文,以达到1+1 > 2 的效果!

文章目录

    • JVM
      • 1、Java文件是如何被运行的?
        • **1.1 加载过程**
        • **1.2 在虚拟机中解释执行**
        • **1.3 最后通过操作系统操作 CPU 执行获取结果。**
      • 2、JVM包含哪几部分?
      • 3、类加载的流程是什么?
      • 4、类加载器的加载顺序?
      • 5、什么是双亲委派机制?
      • 6、谈谈对运行时数据区的理解?(*重点)
        • 6.1、方法区(Method Area)
        • 6.2、Java堆(Heap)
        • 6.3、虚拟机栈(JVM Stacks)
        • 6.4、本地方法栈(Native Method Stacks)
        • 6.5、程序计数器(Program Counter Register)
      • 7、堆和栈的区别是什么?
      • 8、JVM堆内存
        • 8.1 堆内存结构
        • 8.2、GC类型
        • 8.3、Minor GC工作原理
        • 8.4、Full GC工作原理
        • 8.5、理解GC日志
      • 9、垃圾回收(**重点)
        • 9.1、如何确定对象已死?
        • 9.2、垃圾回收算法
          • 标记--清除算法
          • 标记-复制算法
          • 标记-整理算法
          • 分代收集算法
        • 9.3、垃圾收集器
          • Serial收集器
          • ParNew收集器
          • Parallel Scavenge收集器
          • Serial Old收集器
          • Parallel Old收集器
          • CMS收集器
          • G1 收集器
          • 垃圾收集器对比
      • 10、内存泄漏和内存溢出有什么区别,怎么解决?
      • 11、JVM的常用参数
      • 12、JVM调优
        • 12.1、选择合适的垃圾回收器
        • 12.2、调整内存大小
        • 12.3、设置符合预期的停顿时间
        • 12.4、调整内存区域大小比率
        • 12.5、调整对象升老年代的年龄
        • 12.6、调整 JVM本地内存大小

JVM

JVM 是 Java Virtual Machine 的缩写,它是一个虚构出来的计算机,一种规范。通过在实际的计算机上仿真模拟各类计算机功能实现。
Java面试之JVM篇(offer 拿来吧你)_第1张图片
JVM是运行在操作系统之上的,它与硬件没有直接的交互

1、Java文件是如何被运行的?

我们现在写了一个 HelloWorld.java 好了,那这个 HelloWorld.java 抛开所有东西不谈,那是不是就类似于一个文本文件,只是这个文本文件它写的都是英文,而且有一定的缩进而已。

那么如何让JVM识别文本文件呢?因此需要进行编译,让其成为一个它会读二进制文件的 HelloWorld.class
Java面试之JVM篇(offer 拿来吧你)_第2张图片

1.1 加载过程

如果 JVM 想要执行这个 .class 文件,我们需要将其装进一个 类加载器 中,它就像一个搬运工一样,会把所有的 .class 文件全部搬进JVM里面来。
Java面试之JVM篇(offer 拿来吧你)_第3张图片

Java文件经过编译后变成 .class 字节码文件
字节码文件通过类加载器被搬运到 JVM 虚拟机中
虚拟机主要的5大块:方法区,堆都为线程共享区域,有线程安全问题,栈和本地方法栈和计数器都是独享区域,不存在线程安全问题,而 JVM的调优主要就是围绕堆,栈两大块进行。
方法区:是用于存放类似于元数据信息方面的数据的,比如类信息,常量,静态变量,编译后代码···等(类加载器将 .class 文件搬过来就是先丢到这一块上)
:主要放了一些存储的数据,比如对象实例,数组···等,它和方法区都同属于 线程共享区域 。也就是说它们都是 线程不安全
: 这是我们的代码运行空间。我们编写的每一个方法都会放到 栈 里面运行。
程序计数器:类似于一个指针一样的,指向下一行我们需要执行的代码。

1.2 在虚拟机中解释执行

Java面试之JVM篇(offer 拿来吧你)_第4张图片

1.3 最后通过操作系统操作 CPU 执行获取结果。

2、JVM包含哪几部分?

JVM 主要由四大部分组成:ClassLoader(类加载器),Runtime Data Area(运行时数据区,内存分区),Execution Engine(执行引擎),Native Interface(本地库接口),下图可以大致描述 JVM 的结构。
Java面试之JVM篇(offer 拿来吧你)_第5张图片
各组件的作用:首先通过类加载器(ClassLoader) 会把Java代码转换成字节码,运行时数据区(Runtime DataArea)再把字节码加载到内存中,而字节码文件只是JVM的一套指令集规范,并不能直接交给底层操作系统去执行,因此需要特定的命令解析器执行引擎(Execution Engine),将字节码翻译成底层系统指令,再交由CPU去执行,而这个过程中需要调用其他语言的本地库接口(Native Interface)来实现整个程序的功能。

  • ClassLoader:负责加载字节码文件即 class 文件,class 文件在文件开头有特定的文件标示,并且 ClassLoader 只负责class 文件的加载,至于它是否可以运行,则由 Execution Engine 决定。
  • Runtime Data Area:是存放数据的,分为五部分:Stack(虚拟机栈),Heap(堆),Method Area(方法区),PC Register(程序计数器),Native Method Stack(本地方法栈)。几乎所有的关于 Java 内存方面的问题,都是集中在这块。
  • Execution Engine:执行引擎,也叫 Interpreter。Class 文件被加载后,会把指令和数据信息放入内存中,Execution Engine 则负责把这些命令解释给操作系统,即将 JVM 指令集翻译为操作系统指令集。
  • Native Interface:负责调用本地接口的。他的作用是调用不同语言的接口给 JAVA 用,他会在 Native Method Stack 中记录对应的本地方法,然后调用该方法时就通过 Execution Engine 加载对应的本地 lib。

3、类加载的流程是什么?

类加载的过程包括了加载、验证、准备、解析、初始化五个阶段
Java面试之JVM篇(offer 拿来吧你)_第6张图片

  • 加载,查找并加载类的二进制数据,在Java堆中也创建一个java.lang.Class类的对象
  • 连接,连接又包含三块内容:验证、准备、初始化。1)验证,文件格式、元数据、字节码、符号引用验证;2)准备,为类的静态变量分配内存,并将其初始化为默认值;3)解析,把类中的符号引用转换为直接引用
  • 初始化,为类的静态变量赋予正确的初始值
  • 使用,new出对象程序中使用
  • 卸载,执行垃圾回收
class Test{
	public static int a = 1;
}
//我们程序中给定的是 public static int a = 1;
//但是在加载过程中的步骤如下:
1. 加载阶段
编译文件为 .class文件,然后通过类加载,加载到JVM
2. 连接阶段
第一步(验证):确保Class类文件没问题
第二步(准备):先初始化为 a=0。(因为你int类型的初始值为0)
第三步(解析):将引用转换为直接引用
3. 初始化阶段:
通过此解析阶段,把1赋值为变量a

4、类加载器的加载顺序?

加载一个Class类的顺序也是有优先级的,类加载器从最底层开始往上的顺序是这样的:

  • BootStrap ClassLoader:rt.jar
  • Extention ClassLoader: 加载扩展的jar包
  • AppClassLoader:指定的classpath下面的jar包
  • Custom ClassLoader:自定义的类加载器
    Java面试之JVM篇(offer 拿来吧你)_第7张图片

5、什么是双亲委派机制?

双亲委派机制的工作原理:一层一层的 让父类去加载,最顶层父类不能加载往下数,依次类推。
1.类加载器收到类加载的请求;
2.把这个请求委托给父加载器去完成,一直向上委托,直到启动类加载器;
3.启动器加载器检查能不能加载(使用findClass()方法), 能就加载 (结束) ;否则,抛出异常,通知子加载器进行加载。
4.重复步骤三;
Java面试之JVM篇(offer 拿来吧你)_第8张图片

大家所熟知的String 类,直接告诉大家,String 默认情况下是启动类加载器进行加载的。假设我也自定义一个String。现在你会发现自定义的String 可以正常编译,但是永远无法被加载运行。 这是因为申请自定义String加载时,总是启动类加载器,而不是自定义加载器,也不会是其他的加载器。

双亲委派机制可以确保Java核心类库所提供的类,不会被自定义的类所替代

6、谈谈对运行时数据区的理解?(*重点)

Java程序在运行时,会为JVM单独划出一块内存区域,而这块内存区域又可以再次划分出一块运行时数据区,运行时数据区域大致可以分为五个部分:
Java面试之JVM篇(offer 拿来吧你)_第9张图片

6.1、方法区(Method Area)

方法区也是一块被重点关注的区域,主要特点如下:

  • 线程共享区域,因此这是线程不安全的区域。
  • 它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。
  • 当方法区无法满足内存分配需求时,将抛出OutOfMemoryError异常。

6.2、Java堆(Heap)

栈管运行,堆管存储。则虚拟机栈负责运行代码,而虚拟机堆负责存储数据。

Java堆区具有下面几个特点:

  • 存储的是我们new来的对象,不存放基本类型和对象引用。
  • 由于创建了大量的对象,垃圾回收器主要工作在这块区域
  • 线程共享区域,因此是线程不安全的。
  • 能够发生OutOfMemoryError。

其实,Java堆区还可以划分为新生代和老年代,新生代又可以进一步划分为Eden区、Survivor 1区、Survivor 2区。
Java面试之JVM篇(offer 拿来吧你)_第10张图片

6.3、虚拟机栈(JVM Stacks)

Java虚拟机栈也是一块被开发者重点关注的地方,同样,先把干货放上来:

  • Java虚拟机栈是线程私有的,每一个线程都有独享一个虚拟机栈,它的生命周期与线程相同。
  • 虚拟机栈描述的是Java方法执行的内存模型:每个方法被执行的时候都会同时创建一个栈帧(Stack Frame)用于存储局部变量表、操作栈、动态链接、方法出口等信息。每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。
  • 存放基本数据类型(boolean、byte、char、short、int、float、long、double)以及对象的引用(reference类型,它不等同于对象本身,根据不同的虚拟机实现,它可能是一个指向对象起始地址的引用指针,也可能指向一个代表对象的句柄或者其他与此对象相关的位置)和returnAddress类型(指向了一条字节码指令的地址)。
  • 这个区域可能有两种异常:如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError异常;如果虚拟机栈可以动态扩,当扩展时无法申请到足够的内存时会抛出OutOfMemoryError异常。

8种基本类型的变量+对象的引用变量+实例方法都是在栈里面分配内存

6.4、本地方法栈(Native Method Stacks)

本地方法栈与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务,而本地方法栈则是为虚拟机使用到的Native方法服务。

虚拟机规范中对本地方法栈中的方法使用的语言、使用方式与数据结构并没有强制规定,因此具体的虚拟机可以自由实现它。甚至有的虚拟机(譬如Sun HotSpot虚拟机)直接就把本地方法栈和虚拟机栈合二为一。与虚拟机栈一样,本地方法栈区域也会抛出StackOverflowError和OutOfMemoryError异常。

比如说我们现在点开Thread类的源码,会看到它的start0方法带有一个native关键字修饰,而且不存在方法体,这种用native修饰的方法就是本地方法,这是使用C来实现的,然后一般这些方法都会放到一个叫做本地方法栈的区域。

6.5、程序计数器(Program Counter Register)

它的作用就是记录当前线程所执行的位置。 这样,当线程重新获得CPU的执行权的时候,就直接从记录的位置开始执行,分支、循环、跳转、异常处理也都依赖这个程序计数器来完成。

它也是内存区域中唯一一个不会出现OutOfMemoryError的区域,而且占用内存空间小到基本可以忽略不计。

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

  • 堆和栈(虚拟机栈)是完全不同的两块内存区域,一个是线程独享的,一个是线程共享的。
  • 二者之间最大的区别就是存储的内容不同:堆中主要存放对象实例。栈(局部变量表)中主要存放各种基本数据类型、对象的引用
  • 从作用来说,栈是运行时的单位,而堆是存储的单位。栈解决程序的运行问题,即程序如何执行,或者说如何处理数据。堆解决的是数据存储的问题,即数据怎么放、放在哪儿。
  • 在Java中一个线程就会相应有一个线程栈与之对应,因为不同的线程执行逻辑有所不同,因此需要一个独立的线程栈。而堆则是所有线程共享的。栈因为是运行单位,因此里面存储的信息都是跟当前线程(或程序)相关信息的。包括局部变量、程序运行状态、方法返回值等;而堆只负责存储对象信息。

8、JVM堆内存

8.1 堆内存结构

Java堆区可以划分为新生代和老年代,新生代又可以进一步划分为Eden区、Survivor 1区、Survivor 2区。具体比例参数的话,可以看一下这张图。
Java面试之JVM篇(offer 拿来吧你)_第11张图片

8.2、GC类型

  • Minor GC/Young GC:针对新生代的垃圾收集;
  • Major GC/Old GC:针对老年代的垃圾收集。
  • Full GC:针对整个Java堆以及方法区的垃圾收集。
    Java面试之JVM篇(offer 拿来吧你)_第12张图片

8.3、Minor GC工作原理

通常情况下,初次被创建的对象存放在新生代的Eden区,当第一次触发Minor GC,Eden区存活的对象被转移到Survivor区的某一块区域。以后再次触发Minor GC的时候,Eden区的对象连同一块Survivor区的对象一起,被转移到了另一块Survivor区。可以看到,这两块Survivor区我们每一次只使用其中的一块,这样也仅仅是浪费了一块Survivor区。
Java面试之JVM篇(offer 拿来吧你)_第13张图片

8.4、Full GC工作原理

老年代是存储长期存活的对象的,占满时就会触发我们最常听说的Full GC,期间会停止所有线程等待GC的完成。所以对于响应要求高的应用应该尽量去减少发生Full GC从而避免响应超时的问题。
Java面试之JVM篇(offer 拿来吧你)_第14张图片

8.5、理解GC日志

Minor GC日志:
Java面试之JVM篇(offer 拿来吧你)_第15张图片
Full GC日志:
Java面试之JVM篇(offer 拿来吧你)_第16张图片

9、垃圾回收(**重点)

如果没有特殊说明,都是针对的是 HotSpot 虚拟机。

9.1、如何确定对象已死?

通常,判断一个对象是否被销毁有两种方法:

  • 引用计数算法:为对象添加一个引用计数器,每当对象在一个地方被引用,则该计数器加1;每当对象引用失效时,计数器减1。但计数器为0的时候,就表明该对象没有被引用。
  • 可达性分析算法:通过一系列被称之为“GC Roots”的根节点开始,沿着引用链进行搜索,凡是在引用链上的对象都不会被回收。
    Java面试之JVM篇(offer 拿来吧你)_第17张图片

9.2、垃圾回收算法

标记–清除算法

见名知义,标记–清除算法就是对无效的对象进行标记,然后清除。
Java面试之JVM篇(offer 拿来吧你)_第18张图片
这种垃圾收集算法会带来两个明显的问题:

  • 效率问题
  • 空间问题(标记清除后会产生大量不连续的碎片)
标记-复制算法

为了解决效率问题,“标记-复制”收集算法出现了。它可以将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
Java面试之JVM篇(offer 拿来吧你)_第19张图片

标记-整理算法

标记–整理算法算是一种折中的垃圾收集算法,在对象标记的过程,和前面两个执行的是一样步骤。但是,进行标记之后,存活的对象会移动到堆的一端,然后直接清理存活对象以外的区域就可以了。这样,既避免了内存碎片,也不存在堆空间浪费的说法了。但是,每次进行垃圾回收的时候,都要暂停所有的用户线程,特别是对老年代的对象回收,则需要更长的回收时间,这对用户体验是非常不好的。
Java面试之JVM篇(offer 拿来吧你)_第20张图片

分代收集算法

当前虚拟机的垃圾收集都采用分代收集算法,这种算法没有什么新的思想,只是根据对象存活周期的不同将内存分为几块。一般将 java 堆分为新生代和老年代,这样我们就可以根据各个年代的特点选择合适的垃圾收集算法。

  • 比如在新生代中,每次收集都会有大量对象死去,所以可以选择”标记-复制“算法,只需要付出少量对象的复制成本就可以完成每次垃圾收集
  • 老年代的对象存活几率是比较高的,而且没有额外的空间对它进行分配担保,所以我们必须选择“标记-清除”或“标记-整理”算法进行垃圾收集。

9.3、垃圾收集器

如果说收集算法是内存回收的方法论,那么垃圾收集器就是内存回收的具体实现。

我们能做的就是根据具体应用场景选择适合自己的垃圾收集器。试想一下:如果有一种四海之内、任何场景下都适用的完美收集器存在,那么我们的 HotSpot 虚拟机就不会实现那么多不同的垃圾收集器了。

Serial收集器

Serial收集器是最基础、历史最悠久的收集器,是一个单线程工作的收集器,使用 Serial收集器,无论是进行 Minor gc 还是 Full GC ,清理堆空间时,所有的应用线程都会被暂停。
Java面试之JVM篇(offer 拿来吧你)_第21张图片
优点:简单而高效(与其他收集器的单线程相比)

ParNew收集器

ParNew 收集器实质上是 Serial 收集器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,其余的行为包括 Serial 收集器可用的所有控制参数、收集算法、Stop The World、对象分配规则、回收策略等都与 Serial 收集器完全一致。
Java面试之JVM篇(offer 拿来吧你)_第22张图片

Parallel Scavenge收集器

Parallel Scavenge收集器也是一款新生代收集器,基于标记——复制算法实现,能够并行收集的多线程收集器和 ParNew 非常相似。

Parallel Scavenge 收集器的目标则是达到一个可控制的吞吐量(Throughput)。所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值。如果虚拟机完成某个任务,用户代码加上垃圾收集总共耗费了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。

Serial Old收集器

Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。
Java面试之JVM篇(offer 拿来吧你)_第23张图片

Parallel Old收集器

Parallel Old是Parallel Scavenge收集器的老年代版本,支持多线程并发收集,基于标记-整理算法实现。
Java面试之JVM篇(offer 拿来吧你)_第24张图片

CMS收集器

CMS 收集器设计的初衷是为了消除 Parallel 收集器和 Serial 收集器 Full gc 周期中的长时间停顿。CMS收集器在 Minor gc 时会暂停所有的应用线程,并以多线程的方式进行垃圾回收。
Java面试之JVM篇(offer 拿来吧你)_第25张图片

  • 初始标记: 暂停所有的其他线程,并记录下直接与 root 相连的对象,速度很快 ;
  • 并发标记: 同时开启 GC 和用户线程,用一个闭包结构去记录可达对象;
  • 重新标记: 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短
  • 并发清除: 开启用户线程,同时 GC 线程开始对未标记的区域做清扫。

优点:并发收集、低停顿
缺点:对 CPU 资源敏感;无法处理浮动垃圾;它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生

G1 收集器

G1 (Garbage-First) 是一款面向服务器的垃圾收集器,主要针对配备多颗处理器及大容量内存的机器. 以极高概率满足 GC 停顿时间要求的同时,还具备高吞吐量性能特征.

G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region(这也就是它的名字 Garbage-First 的由来) 。这种使用 Region 划分内存空间以及有优先级的区域回收方式,保证了 G1 收集器在有限时间内可以尽可能高的收集效率(把内存化整为零)

垃圾收集器对比

Java面试之JVM篇(offer 拿来吧你)_第26张图片

10、内存泄漏和内存溢出有什么区别,怎么解决?

内存泄漏(memory leak):内存泄漏指程序运行过程中分配内存给临时变量,用完之后却没有被GC回收,始终占用着内存,既不能被使用也不能分配给其他程序,于是就发生了内存泄漏。

以发生的方式来分类,内存泄漏可以分为4类:常发性内存泄漏、偶发性内存泄漏、一次性内存泄漏、隐式内存泄漏

解决方案:1、尽早释放无用对象的引用。2、避免在循环中创建对象。3、使用字符串处理时避免使用String,应使用StringBuffer。4、尽量少使用静态变量,因为静态变量存放在永久代,基本不参与垃圾回收。

内存溢出(out of memory):简单地说内存溢出就是指程序运行过程中申请的内存大于系统能够提供的内存,导致无法申请到足够的内存,于是就发生了内存溢出。

引起内存溢出的原因有很多种,常见的有以下几种:

  • 内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
  • 集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
  • 代码中存在死循环或循环产生过多重复的对象实体;
  • 使用的第三方软件中的BUG;
  • 启动参数内存值设定的过小。

内存溢出的解决方案:

  • 第一步,修改JVM启动参数,直接增加内存。
  • 第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。
  • 第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。
  • 第四步,使用内存查看工具动态查看内存使用情况。

11、JVM的常用参数

Java面试之JVM篇(offer 拿来吧你)_第27张图片

12、JVM调优

12.1、选择合适的垃圾回收器

  • CPU单核,那么毫无疑问Serial 垃圾收集器是你唯一的选择。
  • CPU多核,关注吞吐量 ,那么选择PS+PO组合。
  • CPU多核,关注用户停顿时间,JDK版本1.6或者1.7,那么选择CMS。
  • CPU多核,关注用户停顿时间,JDK1.8及以上,JVM可用内存6G以上,那么选择G1。

12.2、调整内存大小

现象:垃圾收集频率非常频繁。

原因:如果内存太小,就会导致频繁的需要进行垃圾收集才能释放出足够的空间来创建新的对象,所以增加堆内存大小的效果是非常显而易见的。

注意:如果垃圾收集次数非常频繁,但是每次能回收的对象非常少,那么这个时候并非内存太小,而可能是内存泄露导致对象无法回收,从而造成频繁GC。

 //设置堆初始值
 指令1-Xms2g
 指令2-XX:InitialHeapSize=2048m
 
 //设置堆区最大值
 指令1:`-Xmx2g` 
 指令2-XX:MaxHeapSize=2048m
 
 //新生代内存配置
 指令1-Xmn512m
 指令2-XX:MaxNewSize=512m

12.3、设置符合预期的停顿时间

现象:程序间接性的卡顿

原因:如果没有确切的停顿时间设定,垃圾收集器以吞吐量为主,那么垃圾收集时间就会不稳定。

注意:不要设置不切实际的停顿时间,单次时间越短也意味着需要更多的GC次数才能回收完原有数量的垃圾.

 //GC停顿时间,垃圾收集器会尝试用各种手段达到这个时间
 -XX:MaxGCPauseMillis 

12.4、调整内存区域大小比率

现象:某一个区域的GC频繁,其他都正常。

原因:如果对应区域空间不足,导致需要频繁GC来释放空间,在JVM堆内存无法增加的情况下,可以调整对应区域的大小比率。

注意:也许并非空间不足,而是因为内存泄造成内存无法回收。从而导致GC频繁。

 //survivor区和Eden区大小比率
 指令:-XX:SurvivorRatio=6  //S区和Eden区占新生代比率为1:6,两个S区2:6
 
 //新生代和老年代的占比
 -XX:NewRatio=4  //表示新生代:老年代 = 1:4 即老年代占整个堆的4/5;默认值=2

12.5、调整对象升老年代的年龄

现象:老年代频繁GC,每次回收的对象很多。

原因:如果升代年龄小,新生代的对象很快就进入老年代了,导致老年代对象变多,而这些对象其实在随后的很短时间内就可以回收,这时候可以调整对象的升级代年龄,让对象不那么容易进入老年代解决老年代空间不足频繁GC问题。

注意:增加了年龄之后,这些对象在新生代的时间会变长可能导致新生代的GC频率增加,并且频繁复制这些对象新生的GC时间也可能变长。

//进入老年代最小的GC年龄,年轻代对象转换为老年代对象最小年龄值,默认值7
 -XX:InitialTenuringThreshol=7 

12.6、调整 JVM本地内存大小

现象:GC的次数、时间和回收的对象都正常,堆内存空间充足,但是报OOM

原因:JVM除了堆内存之外还有一块堆外内存,这片内存也叫本地内存,可是这块内存区域不足了并不会主动触发GC,只有在堆内存区域触发的时候顺带会把本地内存回收了,而一旦本地内存分配不足就会直接报OOM异常。

注意:本地内存异常的时候除了上面的现象之外,异常信息可能是OutOfMemoryError:Direct buffer memory。解决方式除了调整本地内存大小之外,也可以在出现此异常时进行捕获,手动触发GC(System.gc())。

XX:MaxDirectMemorySize

JVM内容还有待完善,但是已经实现了从0到1的过程,后续会根据真实面试情况持续更新相关内容,实现从1到100的飞跃,我会继续更新面试板块内容,还请小伙伴们持续关注!

欢迎各位小伙伴们阅读以下内容,定能收获满满!
参考文档:
《深入理解 Java 虚拟机:JVM 高级特性与最佳实践(第二版》
楼仔:如何啃下JVM这座大山,完结撒花!
guide哥:https://javaguide.cn/database/mysql/mysql-questions-01.html
牛客网:https://www.nowcoder.com/tutorial/94/e07fdcfc369c49e8a95ea23de51d58b5
狂神说:JVM快速入门
帅地玩编程-- Java面试必知必会

你可能感兴趣的:(面试训练营)