史上最全!2020面试阿里,字节跳动90%被问到的JVM面试题附答案

前言:最近老是收到小伙伴的私信问我能不能帮忙整理出一份JVM相关的面试题出来,说自己在大厂去面试的时候这一块问的是特别多的,每次自己学的时候每次都学不到重点去。这不他来了,一份详细的JVM面试真题给大家整理在下方了!

一、什么情况下会发生栈内存溢出?

1、栈是线程私有的,栈的生命周期和线程一样,每个方法在执行的时候就会创建一个栈帧,它包含局部变量表、操作数栈、动态链接、方法出口等信息,局部变量表又包括基本数据类型和对象的引用;
2、当线程请求的栈深度超过了虚拟机允许的最大深度时,会抛出StackOverFlowError异常,方法递归调用肯可能会出现该问题;
3、调整参数-xss去调整jvm栈的大小

二、详解JVM内存模型?

jvm将虚拟机分为5大区域,程序计数器、虚拟机栈、本地方法栈、java堆、方法区;

程序计数器:线程私有的,是一块很小的内存空间,作为当前线程的行号指示器,用于记录当前虚拟机正在执行的线程指令地址;

虚拟机栈:线程私有的,每个方法执行的时候都会创建一个栈帧,用于存储局部变量表、操作数、动态链接和方法返回等信息,当线程请求的栈深度超过了虚拟机允许的最大深度时,就会抛出StackOverFlowError;

本地方法栈:线程私有的,保存的是native方法的信息,当一个jvm创建的线程调用native方法后,jvm不会在虚拟机栈中为该线程创建栈帧,而是简单的动态链接并直接调用该方法;

堆:java堆是所有线程共享的一块内存,几乎所有对象的实例和数组都要在堆上分配内存,因此该区域经常发生垃圾回收的操作;

方法区:存放已被加载的类信息、常量、静态变量、即时编译器编译后的代码数据。即永久代,在jdk1.8中不存在方法区了,被元数据区替代了,原方法区被分成两部分;1:加载的类信息,2:运行时常量池;加载的类信息被保存在元数据区中,运行时常量池保存在堆中;

三、JVM中一次完整的GC是什么样子的?对象如何晋升到老年代?

java堆 = 新生代+老年代;
新生代 = Eden + Suivivor(S0 + S1),默认分配比例是8:1:1;
当Eden区空间满了的时候,就会触发一次Minor GC,以收集新生代的垃圾,存活下来的对象会被分配到Survivor区
大对象(需要大量连续内存空间的对象)会直接被分配到老年代
如果对象在Eden中出生,并且在经历过一次Minor GC之后仍然存活,被分配到存活区的话,年龄+1,此后每经历过一次Minor GC并且存活下来,年龄就+1,当年龄达到15的时候,会被晋升到老年代;
当老年代满了,而无法容纳更多对象的话,会触发一次full gc;full gc存储的是整个内存堆(包括年轻代和老年代);;
Major GC是发生在老年代的GC,清理老年区,经常会伴随至少一次minor gc;

四、Java中的垃圾回收算法?

java中有四种垃圾回收算法,分别是标记清除法、标记整理法、复制算法、分代收集算法;
标记清除法:
第一步:利用可达性去遍历内存,把存活对象和垃圾对象进行标记;
第二步:在遍历一遍,将所有标记的对象回收掉;
特点:效率不行,标记和清除的效率都不高;标记和清除后会产生大量的不连续的空间分片,可能会导致之后程序运行的时候需分配大对象而找不到连续分片而不得不触发一次GC;

标记整理法:
第一步:利用可达性去遍历内存,把存活对象和垃圾对象进行标记;
第二步:将所有的存活的对象向一段移动,将端边界以外的对象都回收掉;
特点:适用于存活对象多,垃圾少的情况;需要整理的过程,无空间碎片产生;

复制算法:
将内存按照容量大小分为大小相等的两块,每次只使用一块,当一块使用完了,就将还存活的对象移到另一块上,然后在把使用过的内存空间移除;
特点:不会产生空间碎片;内存使用率极低;

分代收集算法:
根据内存对象的存活周期不同,将内存划分成几块,java虚拟机一般将内存分成新生代和老生代,在新生代中,有大量对象死去和少量对象存活,所以采用复制算法,只需要付出少量存活对象的复制成本就可以完成收集;老年代中因为对象的存活率极高,没有额外的空间对他进行分配担保,所以采用标记清理或者标记整理算法进行回收;

五、如何判断一个对象是否存活?

判断一个对象是否存活,分为两种算法1:引用计数法;2:可达性分析算法;
引用计数法:
给每一个对象设置一个引用计数器,当有一个地方引用该对象的时候,引用计数器就+1,引用失效时,引用计数器就-1;当引用计数器为0的时候,就说明这个对象没有被引用,也就是垃圾对象,等待回收;
缺点:无法解决循环引用的问题,当A引用B,B也引用A的时候,此时AB对象的引用都不为0,此时也就无法垃圾回收,所以一般主流虚拟机都不采用这个方法;

可达性分析法
从一个被称为GC Roots的对象向下搜索,如果一个对象到GC Roots没有任何引用链相连接时,说明此对象不可用,在java中可以作为GC Roots的对象有以下几种:

虚拟机栈中引用的对象
方法区类静态属性引用的变量
方法区常量池引用的对象
本地方法栈JNI引用的对象
但一个对象满足上述条件的时候,不会马上被回收,还需要进行两次标记;第一次标记:判断当前对象是否有finalize()方法并且该方法没有被执行过,若不存在则标记为垃圾对象,等待回收;若有的话,则进行第二次标记;第二次标记将当前对象放入F-Queue队列,并生成一个finalize线程去执行该方法,虚拟机不保证该方法一定会被执行,这是因为如果线程执行缓慢或进入了死锁,会导致回收系统的崩溃;如果执行了finalize方法之后仍然没有与GC Roots有直接或者间接的引用,则该对象会被回收;

六、有哪几种垃圾回收器,有哪些优缺点?cms和g1的区别?

垃圾回收器主要分为以下几种:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1;
Serial:
单线程的收集器,收集垃圾时,必须stop the world,使用复制算法。

ParNew:
Serial收集器的多线程版本,也需要stop the world,复制算法.

Parallel Scavenge:
新生代收集器,复制算法的收集器,并发的多线程收集器,目标是达到一个可控的吞吐量,和ParNew的最大区别是GC自动调节策略;虚拟机会根据系统的运行状态收集性能监控信息,动态设置这些参数,以提供最优停顿时间和最高的吞吐量;

Serial Old:
Serial收集器的老年代版本,单线程收集器,使用标记整理算法。

Parallel Old:
是Parallel Scavenge收集器的老年代版本,使用多线程,标记-整理算法。

CMS:
是一种以获得最短回收停顿时间为目标的收集器,标记清除算法,运作过程:初始标记,并发标记,重新标记,并发清除,收集结束会产生大量空间碎片;

G1:
标记整理算法实现,运作流程主要包括以下:初始标记,并发标记,最终标记,筛选回收。不会产生空间碎片,可以精确地控制停顿;
G1将整个堆分为大小相等的多个Region(区域),G1跟踪每个区域的垃圾大小,在后台维护一个优先级列表,每次根据允许的收集时间,优先回收价值最大的区域,已达到在有限时间内获取尽可能高的回收效率;

七、什么是类加载?

虚拟机把描述类的数据加载到内存里面,并对数据进行校验、解析和初始化,最终变成可以被虚拟机直接使用的class对象;

八、类加载的过程?

主要分为以下几个过程:加载、验证、准备、解析、初始化;
加载:
加载分为三步:
1、通过类的全限定性类名获取该类的二进制流;
2、将该二进制流的静态存储结构转为方法区的运行时数据结构;
3、在堆中为该类生成一个class对象;

验证:
验证该class文件中的字节流信息复合虚拟机的要求,不会威胁到jvm的安全;

准备:
为class对象的静态变量分配内存,初始化其初始值;

解析:
该阶段主要完成符号引用转化成直接引用;

初始化:
到了初始化阶段,才开始执行类中定义的java代码;
初始化阶段是调用类构造器的过程;

九、什么是类加载器,常见的类加载器有哪些?

类加载器是指:通过一个类的全限定性类名获取该类的二进制字节流叫做类加载器;
类加载器分为以下四种:
启动类加载器:
用来加载java核心类库,无法被java程序直接引用;

扩展类加载器:
用来加载java的扩展库,java的虚拟机实现会提供一个扩展库目录,该类加载器在扩展库目录里面查找并加载java类;

系统类加载器:
它根据java的类路径来加载类,一般来说,java应用的类都是通过它来加载的;

自定义类加载器:
由java语言实现,继承自ClassLoader;

十、什么是双亲委派模型?

当一个类加载器收到一个类加载的请求,他首先不会尝试自己去加载,而是将这个请求委派给父类加载器去加载,只有父类加载器在自己的搜索范围类查找不到给类时,子加载器才会尝试自己去加载该类;

十一、为什么需要双亲委派模型?

为了防止内存中出现多个相同的字节码;
因为如果没有双亲委派的话,用户就可以自己定义一个java.lang.String类,那么就无法保证类的唯一性;

十二、怎么打破双亲委派模型?

自定义类加载器,继承ClassLoader类,重写loadClass方法和findClass方法;

十三、强引用、软应用、弱引用、虚引用的区别?

强引用:强引用是我们使用最广泛的引用,如果一个对象具有强引用,那么垃圾回收期绝对不会回收它,当内存空间不足时,垃圾回收器宁愿抛出OutOfMemoryError,也不会回收具有强引用的对象;我们可以通过显示的将强引用对象置为null,让gc认为该对象不存在引用,从而来回收它;

软引用:软应用是用来描述一些有用但不是必须的对象,在java中用SoftReference来表示,当一个对象只有软应用时,只有当内存不足时,才会回收它;
软引用可以和引用队列联合使用,如果软引用所引用的对象被垃圾回收器所回收了,虚拟机会把这个软引用加入到与之对应的引用队列中;

弱引用:弱引用是用来描述一些可有可无的对象,在java中用WeakReference来表示,在垃圾回收时,一旦发现一个对象只具有软引用的时候,无论当前内存空间是否充足,都会回收掉该对象;
弱引用可以和引用队列联合使用,如果弱引用所引用的对象被垃圾回收了,虚拟机会将该对象的引用加入到与之关联的引用队列中;

虚引用:虚引用就是一种可有可无的引用,无法用来表示对象的生命周期,任何时候都可能被回收,虚引用主要使用来跟踪对象被垃圾回收的活动,虚引用和软引用与弱引用的区别在于:虚引用必须和引用队列联合使用;在进行垃圾回收的时候,如果发现一个对象只有虚引用,那么就会将这个对象的引用加入到与之关联的引用队列中,程序可以通过发现一个引用队列中是否已经加入了虚引用,来了解被引用的对象是否需要被进行垃圾回收;

小结:今天的JVM面试题分享就到这里了,大家一次性记不住或者有什么看不懂的话可以收藏起来慢慢看,在看的途中有什么不懂的也可以私信问我,我看到都会回的。文章的最后祝大家在工作的工作顺利,在找工作的都能拿到自己满意的offer!

原文地址:https://www.cnblogs.com/lwh1019/p/13124635.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


jinfo 命令可以用来查看 Java 进程运行的 JVM 参数,命令如下:[root@admin ~]# jinfo --helpUsage: jinfo [option] <pid> (to connect to running process) jinfo [option] <executable <core> (to connect to a core file) jinfo [option] [serve
原文链接:https://www.cnblogs.com/niejunlei/p/5987611.htmlJava Virtual Machine Stacks,线程私有,生命周期与线程相同,描述的是Java方法执行的内存模型:每一个方法执行的同时都会创建一个栈帧(Stack Frame),由于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法的执行就对应着栈帧在虚拟机栈中的入栈,出栈...
java 语言, 开发者不能直接控制程序运行内存, 对象的创建都是由类加载器一步步解析, 执行与生成与内存区域中的; 并且jvm有自己的垃圾回收器对内存区域管理, 回收; 但是我们已经可以通过一些工具来在程序运行时查看对应的jvm内存使用情况, 帮助更好的分析与优化我们的代码;jps查看系统中有哪些java进程jps 命令类似与 linux 的 ps 命令,但是它只列出系统中所有的 Java 应用程序。 通过 jps 命令可以方便地查看 Java 进程的启动类、传入参数和 Java 虚拟机参数等信息
1.jvm的简单抽象模型:  2.类加载机制     双亲委派模型是为了防止jdk核心类库被篡改,如果需要打破可以重写Classloader.loadClass方法。r 双亲委派模型:一个类加载器收到一个类的加载请求,他会先判断自身是否已存在该类,如果不存在上抛给上一级类加载器ClassLoad
堆外内存JVM启动时分配的内存,称为堆内存,与之相对的,在代码中还可以使用堆外内存,比如Netty,广泛使用了堆外内存,但是这部分的内存并不归JVM管理,GC算法并不会对它们进行回收,所以在使用堆外内存时,要格外小心,防止内存一直得不到释放,造成线上故障。堆外内存的申请和释放JDK的ByteBuffe
1.springboot和tomcat2.springcloud的请求如何通过网关鉴权?3.springmvc启动时组件的加载顺序?4.mybatis如何同时更新三条记录5.hibernate实现级联更新6.一个web程序应用程序启动时的加载流程7.如何向www.baidu.com地址发出请求时,并获取相应?8.???9.谈谈你对tcp/iptelnetudp协
堆设置-Xms256M:初始堆大小256M,默认为物理内存的1/64-Xmx1024M:最大堆大小1024M,默认为物理内存的1/4,等于与-XX:MaxHeapSize=64M-Xmn64M:年轻代大小为64M(JDK1.4后支持),相当于同时设置NewSize和MaxNewSize为64M-XX:NewSize=64M:初始年轻代大小-XX:MaxNewSize=256M:最大年轻代大小(默认
一.概述收集算法(JVM之垃圾回收-垃圾收集算法)是内存回收的抽象策略,垃圾收集器就是内存回收的具体实现。JVM规范对于垃圾收集器的应该如何实现没有任何规定,因此不同的厂商、不同版本的虚拟机所提供的垃圾收集器差别较大,这里只看HotSpot虚拟机。就像没有最好的算法一样,垃圾收集器
Java中的堆是JVM所管理的最大的一块内存空间,主要用于存放各种类的实例对象,如下图所示: 在Java中,堆被划分成两个不同的区域:新生代(Young)、老年代(Old)。新生代(Young)又被划分为三个区域:Eden、S0、S1。 这样划分的目的是为了使JVM能够更好的管理堆内存中的对象,包
JVM深入理解JVM(4)——如何优化JavaGC「译」 PostedbyCrowonAugust21,2017本文翻译自SangminLee发表在Cubrid上的”BecomeaJavaGCExpert”系列文章的第三篇《HowtoTuneJavaGarbageCollection》,本文的作者是韩国人,写在JDK1.8发布之前,虽然有些地
 JVM深入理解JVM(2)——GC算法与内存分配策略 PostedbyCrowonAugust10,2017说起垃圾收集(GarbageCollection,GC),想必大家都不陌生,它是JVM实现里非常重要的一环,JVM成熟的内存动态分配与回收技术使Java(当然还有其他运行在JVM上的语言,如Scala等)程序员在提升开
运行时数据区  线程独有本地方法栈、虚拟机栈、程序计数器这些与线程对应的数据区会随着线程开始和结束创建和销毁  整体公有元数据区(又称方法区)、堆区会随着虚拟机启动而创建,随着虚拟机退出而销毁 
java整个堆大小设置:Xmx和Xms设置为老年代存活对象的3-4倍,即FullGC之后的老年代内存占用的3-4倍。永久代PermSize和MaxPermSize设置为老年代存活对象的1.2-1.5倍年轻代Xmx的设置为老年代存活对象的1-1.5倍老年代的内存大小设置为老年代存活对象的2-3倍BTW: Sun官方建议年轻代
栈顶缓存(Top-of-StackCashing)技术基于栈式架构得虚拟机所使用的零地址指令更加紧凑,但完成一项操作的时候必然使用更多的入栈和出栈指令,这同时也就意味着将需要更多的指令分派次数和内存读写次数 由于操作数是存储在内存重的,因此频繁地执行内存读/写操作必然影响速度。 综上
自用。同样的代码在不同的平台生成的机器码是不一样的,为什么java代码生成的字节码文件,能在不同的平台运行?因为不同版本的jdk里面的虚拟机会屏蔽不同操作系统在底层硬件与指令上的区别。栈:线程栈,局部变量存放栈内存区域。线程(分配一个栈)运行分配栈将局部变量放入内存。怎么放:栈
jconsole监控:1.java启动命令加上参数java-Djava.rmi.server.hostname=172.16.17.247-Dcom.sun.management.jmxremote-Dcom.sun.management.jmxremote.port=2099-Dcom.sun.management.jmxremote.authenticate=false-Dcom.sun.management.jmxremote.ssl=false -XX:+Unlock
类加载器分类publicclassStackStruTest{publicstaticvoidmain(String[]args){//对用户自定义个类来说:默认使用系统类加载器进行加载-----AppClassLoaderClassLoaderclassLoader=StackStruTest.class.getClassLoader();System.out.p
堆体系结构一个JVM实例只存在一个堆内存,堆内存的大小是可调节的。类加载器读取类文件后,需要把类、方法、常量、变量放在堆内存中,保存所有引用类型的真实信息,以方便执行器指向,堆内存分为三个部分:年轻代、老年代、永久代。Java7之前,堆内存在逻辑上分为:年轻代、老年代、永久代。物
JVM深入理解JVM(5)——虚拟机类加载机制 PostedbyCrowonAugust21,2017在Class文件中描述的各种信息,最终都需要加载到虚拟机中之后才能运行和使用。而虚拟机中,而虚拟机如何加载这些Class文件?Class文件中的信息进入到虚拟机中会发生什么变化?本文将逐步解答这
保存(持久化)对象及其状态到内存或者磁盘Java平台允许我们在内存中创建可复用的Java对象,但一般情况下,只有当JVM处于运行时,这些对象才可能存在,即,这些对象的生命周期不会比JVM的生命周期更长。但在现实应用中,就可能要求在JVM停止运行之后能够保存(持久化)指定的对象,并在