jvm面试我只看这篇

前言

这是一篇看了能说的出来的jvm面试;个人能力有限,文中描述难免有错误,请指正;

一 JVM面试

1.1 简述Java内存模型(重点)

jvm会将运行程序所管理的空间分为若干部分,每个部分都起到至关重要的部分;jdk1.8java运行时数据区如下:

程序计数器:当前线程执行字节码的行号指示器;字节码解析器通过改变计数器的值,来选取下一条需要执行的字节码指令,分支、循环、跳转、异常处理、线程恢复等基础功能;

Java 虚拟机栈: java方法执行的内存模型,每个方法被执行的时候都会创建帧栈用于存储局部变量表、操作数栈、动态链接、方法出口等信息;

本地方法栈: 执行java的native方法服务,每个方法被执行的时候都会创建帧栈用于存储局部变量表、操作数栈、动态链接、方法出口等信息;

Java 堆: 储存对象的实例和数组;

方法区: 非堆,储存类的结构信息;例如运行时常量池的字段和方法数据,构造函数和普通方法的字节码内容等;

科普运行时常用池:运行时常用池是class文件中每一个类或者接口的常量池表的运行表现形式,包括若干种常量,如字段和方法的引用;在类加载至虚拟机的时候就会创建运行时常量池;

科普帧栈:帧栈用于储存数据和部分过程结果的数据,同时也会处理动态链接,方法返回值和分派;帧栈随着方法的创建而创建,随着方法的销毁而销毁;帧栈中维护着本地变量表,操作数栈,和指向当前方法所属类的运行常量池的引用;

注意:jdk1.8已经使用元空间替代了jdk1.7方法区中的永久代,元空间存在于native内存中,其大小根据本地内存而定,没有限制;详细看官网地址

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/considerations.html#sthref66

1.2 简述类的加载过程(重点)

类的加载过程分为 加载,链接,初始化,使用,和卸载5个阶段,其中连接阶段又分为验证 准备和解析阶段;

  1. 加载阶段:主要是获得类的二进制字节流在内存中生产Class对象;
  2. 连接阶段:验证Class的安全性,符合虚拟机规范;准备类的内存分配和初始化变量;解析常量池的符号引用(将符号引用转换为直接引用);
  3. 初始化阶段:对静态变量和静态代码赋初值;
  4. 使用阶段;
  5. 卸载阶段;

1.3 哪些地方会发生OOM?

内存溢出:申请了8个字节的空间,但是你在这个空间写入9或以上字节的数据,出现内存溢出;

在java内存模型中只有程序计数器不会发生OOM(out of memory),其余地区都会发生OOM;

1.4 什么是双亲委派模型?

当一个类收到了类加载请求时,自己不会先去加载这个类,而是将其委派给父类去加载,如果父类不能加载,反馈给子类,由子类去完成类的加载;

这边有可能会问,你都知道哪些类加载器

  1. 启动类加载器:主要加载%JAVA_HOME%\lib 目录下的类库文件或者 -Xbootclasspath 所指定的类库文件(加载核心类)
  2. 扩展类加载器:主要加载%JAVA_HOME%\lib\ext目录下的类库文件或者java.ext.dirs系统变量所指定的类库文件(加载扩展库)
  3. 程序应用类加载器:主要加载用户类路径(classpath)所指定的类库。
  4. 用户自定义类加载器:加载用户自定义的类库。

如何打破双亲委派模型

双亲委派模型都依靠loadClass(),重写loaderClass()即可;

1.5 新生代中区分Eden和Survivor的作用是什么

新生代分为 3 个分区:Eden(伊甸园)、Survivor1、Survivor2;其中Survivor1、 Survivor2 合起来成为Survivor(幸存区); 如果没有Survivor,Eden区每进行一次Minor GC,存活的对象都会被送到老年代。老年代将很快被填满,老年代每发生一次Full GC 的速度比 Minor GC慢10倍;

Survivor 的作用就是减少老年代Full GC 的次数,相当于缓冲带;

Eden和Survivor的比例分配8:1;

1.6简述分代垃圾回收器工作流程

  1. 众多的对象都分配在Eden;
  2. 一个survivor空间接收来自Eden存活的对象;
  3. 2 个survivor 空间的对象会相互复制,不管survivor 空间的对象如何复制,总有一个survivor 空间是空的用于接收来自Eden存活的对象;
  4. 当survivor 空间不足时,对象就会复制到老年代;

官网:

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/generations.html#sthref16

1.7 简述内存分配与回收策略(重点)

  • 大多数情况下对象都优先直接分配在Eden ,当Eden 空间不足时会发生Minor GC, 每经过一次Minor GC,年龄+1,若年龄超过15,则被进入到老年态。当老年代空间不足时就会发生 Full GC;

  • 长期存活对象将进入老年代

  • 如果是大对象(需要大量连续内存空间的对象)直接进入老年代 ;

会出现的小问题就是:Minor GC和Full GC触发条件,答默认情况下发生15次Minor GC之后就会触发一次Full GC

1.8 GC是什么? 为什么要有 GC?

GC 是垃圾收集(GabageCollection);Java 提供的 GC 功能可以自动监测对象是否超过作用域从而达到自动回收内存的目的,而不需要人为手动释放内存;主要调用的是 System.gc() 和 Runtime.getRuntime().gc();

1.9 简述CMS收集器

CMS(Concurrent Mark Sweep)收集器基于标记—清除算法实现的收集器,是一种以获取最短回收停顿时间为目标的收集器。主要优点是并发收集,低停顿,在cpu多核的情况下性能较好。在启动 JVM 的参数加上

“-XX:+UseConcMarkSweepGC”来指定使用 CMS 垃圾回收器;其使用在老年代 可以配合新生代的Serial和ParNew收集器一起使用;由于 CMS 使用 标记—清除算法 GC时会产生大量碎片,有可能提前触发Full GC;如果在老年代充满之前无法回收不可达对象,或者没有足够的空间满足分配就会导致Concurrent Mode Failure(并发模式故障);

官网:

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/cms.html

2.0 简述G1收集器

G1(Garbage-First)从整体来看是基于标记—整理算法实现的收集器,能够实现并发并行,对cpu利用率较高,减少停顿时间。目标是取代jdk1.5发布的CMS收集器。G1收集器收集范围是老年代和新生代,不需要结合其他收集器使用,G1收集器可预测垃圾回收的停顿时间,对空间进行整合;由于G1是基于复制算法实现,当没有足够的空间(region)分配存活的对象就会导致Allocation (Evacuation) Failure(分配失败)

官网:

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/g1_gc.html#garbage_first_garbage_collection

2.1 jvm调优时如何选择垃圾回收器(重点)

  • 当应用的数据集较小时,约100M时或者 单线程应用,没有严格的停顿要求选择serial 收集器; 参数选项:-XX:+UseSerialGC;
  • 如果对应用的性能要求高或者没有停顿要求,可以接受1秒或者更久的停顿选择parallel收集器; 参数选项:XX:+UseParallelGC;
  • 如果对响应时间有很高的要求相比于吞吐量,追求更小的停顿时间可以使用CMS或者G1; 参数选项:-XX:+UseConcMarkSweepGC 或者 XX:+UseG1GC

官网:

https://docs.oracle.com/javase/8/docs/technotes/guides/vm/gctuning/collectors.html#sthref27

2.2 简述GC中Stop the world(重点)

Java中Stop-The-World机制简称STW,是在执行垃圾收集算法时,Java应用程序的其他所有线程都被挂起。Stop-The-World对系统性能存在影响,因此垃圾回收的一个原则是尽量减少“Stop-The-World”的时间;

2.3 垃圾回收算法(重点)

标记-清除法:标记出没有用的对象,之后一个一个回收掉

  • 缺点:标记和清除两个过程效率不高,产生内存碎片导致需要分配较大对象时无法找到足够的连续内存而需要触发一次GC操作;
  • 优点:实现简单;

复制算法: 按照容量划分二个大小相等的内存区域,当一块用完的时候将活着的对象复制到另一块上,然后再把已使用的内存空间一次清理掉

  • 缺点:将内存缩小为原来的一半;
  • 优点:按顺序分配内存,实现简单、运行高效,不用考虑内存碎片。

标记-整理法:标记出没有用的对象,让所有存活的对象都向一端移动,然后直接清除掉端边界以外的对象

  • 优点:解决了标记- 清除算法导致的内存碎片问题。
  • 缺点:仍需要进行局部对象移动,一定程度上降低了效率。

分代回收:根据对象存活周期的不同将内存划分为几块,一般是新生代和老年代,新生代基本采用复制算法,老年代采用标记整理算法

2.4 你知道哪些垃圾收集器

收集器 收集范围 算法 执行类型
Serial 新生代 复制 单线程
ParNew 新生代 复制 多线程并行
Parallel Scavenge 新生代 复制 多线程并行
Serial Old 老年代 标记整理 单线程
Parallel Old 老年代 标记整理 多线程
CMS 老年代 标记清除 多线程并发
G1 新生代、老年代 复制,标记整理 多线程

2.5 你有遇到过java内存泄漏么?(次重点)

内存泄漏:new申请了一块内存,后来很长时间都不再使用了(按理应该释放),但是因为一直被某个或某些实例所持有导致 GC 不能回收;

经典案例

void test(){
    Vector vector = new Vector();
    for (int i = 1; i<100; i++)
    {
        Object object = new Object();
        vector.add(object);
        object = null;
    }
    //...对vector的操作
    //...与vector无关的其他操作
}

手动解决赋值null即可

void test(){
    Vector vector = new Vector();
    for (int i = 1; i<100; i++)
    {
        Object object = new Object();
        vector.add(object);
        object = null;
    }
    //...对vector的操作
    vector = null;
    //...与vector无关的其他操作
}

还有各种流和socket的close方法未被调用也会发生内存泄漏问题;

2.6 对象什么时候会被GC

  • 引用计数器法:为每个对象创建一个引用计数,有对象引用时计数器 +1,引用被释放时计数 -1,当计数器为 0 时就可以被回收。它有一个缺点不能解决循环引用(A->B,B->A)的问题,那么 AB 将永远不会被回收;
  • 可达性分析算法:从 GC Roots 开始向下搜索,搜索所走过的路径称为引用链。当一个对象到 GC Roots 没有任何引用链相连时,则证明此对象是可以被回收的。

2.7 说下Java内存堆和栈区别(偶尔会问到)

  • 栈内存用来存储基本类型的变量和对象的引用变量,堆内存用来存储Java中的对象;
  • 栈内存线程私有,堆内存线程共享
  • 栈内存不足时,JVM会抛出java.lang.StackOverFlowError(一般发生在递归的时候);堆内存不足时,JVM会抛出java.lang.OutOfMemoryError
  • 栈的内存远小于堆内存,-Xss选项设置栈的大小。-Xms选项可以设置堆的开始大小;

2.8 强引用、软引用、弱引用、虚引用以及他们之间和gc的关系

  • 强引用:new出的对象之类的引用, 只要强引用还在,gc时永远不会被回收
  • 软引用:有用但非必须的对象,内存溢出异常之前,回收
  • 弱引用:有用但非必须的对象,对象能生存到下一次垃圾收集发生之前。
  • 虚引用:对生存时间无影响,在垃圾回收时得到通知。

2.9java对象创建

  • 使用new关键字创建对象; 会调用构造方法;
  • 使用Class类的newInstance方法(反射机制);会调用构造方法;
  • 使用Constructor类的newInstance方法(反射机制);会调用构造方法;
  • 使用Clone方法创建对象;不会调用构造方法;
  • 使用(反)序列化机制创建对象;不会调用构造方法;

3.0 如果对象的引用被置为 null,垃圾收集器是否会立即回收对象?

不会,在下一个垃圾回收周期中回收对象。

3.1 jvm调优工具又哪些?各自的作用又是什么(重点)

  • jps: 查看进程的参数信息;
  • jstat: 查看某个Java进程内的线程堆栈信息;
  • jinfo: 查看虚拟机参数;
  • jmap:查看堆内存使用状况,生成快照存储(dump文件);
  • jhat: 分析jmap dump生成的快照文件;
  • jconsole: 基于JMX的可视化工具,监控 cpu, 内存,线程等使用情况;
  • jvisualvm: JDK 自带分析工具,功能齐全,如查看进行信息,快照转存,监控cpu,线程,方法区,堆等;

3.2 你知道哪些JVM调优参数

  • -Xms128m JVM初始分配的堆内存
  • -Xmx512m JVM最大允许分配的堆内存,按需分配;
  • -XX:MetaspaceSize:分配给类元数据空间(以字节计)的初始大小;
  • -XX:MaxMetaspaceSize:分配给类元数据空间的最大值,超过此值就会触发Full GC
  • -XX:NewRatio:新生代和老年代的占比;
  • -XX:NewSize:新生代空间;
  • -XX:SurvivorRatio:伊甸园空间和幸存者空间的占比;
  • -XX:MaxTenuringThreshold:对象进入老年代的年龄阈值;
  • XX:+PrintGC:打印 gc 信息;
  • -XX:+PrintGCDetails:打印 gc 详细信息

如果你对文中的知识点不太理解 推荐阅读周志明先生《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》和作者给出的官网链接!!!!

posted @ 2020-07-31 09:01  知识追寻者  阅读(428)  评论(0编辑  收藏  举报