jvm面试题汇总

本文详细介绍了JVM的分区及其作用,包括方法区、堆、栈、本地方法栈和程序计数器。讨论了堆的分区、垃圾收集算法、GC根思想、Safepoint和STW,以及各种GC收集器和回收算法。此外,还涵盖了类加载过程、双亲委派机制、类加载器、内存管理以及JVM调优的相关指令。文章旨在帮助读者理解和掌握JVM的核心概念和工作原理。
摘要由CSDN通过智能技术生成

1 jvm分区及各分区放什么?

1,方法区:存放类信息,字节码文件,静态常量,很少发生垃圾回收,线程共享
2,堆:初始化对象,成员变量,集合数据等,线程共享。
3,栈:由栈帧组成,每个栈帧是一个方法,先入后出模式,一个栈帧调用另个栈帧就把新的栈帧压入到顶层。每个栈帧又分为局部变量表(存放的都是基本类型,非基本类型都通过动态链接去找),操作数栈(存放局部每次运行的中间结果和操作数(类似字节码行号)),动态链接(即指向堆的内存地址)等。线程独有。
4,本地方法栈:主要是本地native方法,用户支持操作系统语言。
5,程序计数器,记录线程当前执行的行号,这个行号就是方法区字节码文件各行的行号。内存小,线程独有。是唯一没有OOM的区,因为java没有给他做规定

2 堆的分区

1,Eden,伊甸区,
2,Survival 幸存区,又可细分为from和to区(二者1:1)。
3,老年代
总结,Eden和survival统称新生代(二者8:2),当新生代内存容量达到一定容量时,开始进行Minor GC,没有被回收的对象被放到幸存取得from区,同时对象的分代年龄也会加1,如果from区也达到一定容量就再次复制到to区。经过多次GC(通常是15此)之后,存活下来的对象被移动到老年区,通常是一些静态变量,缓存数据,全局变量。当jvm内存不够时会触发full GC,同时会报出OOM内存溢出异常。

3 GC的方式 及GC ROOT思想

引用链法:通过GC ROOT 的对象来判断,如果一条链不能够达到GC ROOT就说明可以回收。这里的GC ROOT是是一种思想,就是从一个某个节点开始向下搜索,走过的路径称为引用链,如果某个对象到开始的节点没有任何引用链相连的话,就称之为对象不可达,就可以回收了。可以作为GC ROOT开始节点的对象有所有激活的线程中引用的对象,全局变量,所有classLoader等。如果这些处于栈中的对象和全局或系统变量都没有GC ROOT该对象的话,那就真的该回收了。
还有一种引用计数法,通过给被引用的对象加+1的方式,判断GC时其值是否为0,进行回收。不过因为其在有A B 两个对象互相引用的情况中计数器都不为0,导致不能回收,也就是循环引用。所以不是主流的回收算法。

4,safepoint和stw是什么?

是在GC时,要等到所有线程进入到safepoint的时候才能开始执行垃圾回收。而stw就是stop the world的意思,也是safepoint之后的状态,这个状态下,所有业务线程暂停,jvm只做垃圾回收。
通常的safepoint点是:某次循环的末尾,方法返回前,调用方法的call之后,抛出异常的位置。

5,GC三种回收的算法,及其区别,和jvm内存块存放方式?

https://www.jianshu.com/p/5d612f36eb0b

这个引用讲的很好
介绍三个之前我们要先知道jvm内存是一小块一小块的,做存储时可能会东一小块西一小块的存储。而且一个大的对象可能会占用好几块,当然了这几块肯定还是尽可能是连续的。而所谓的碎片就是有不连续的内存块,如果这样块很多,那么岂不是会出现及时有内存但是却不能存储大对象的现象,进而导致提前做一次GC。内存模型如下图:
在这里插入图片描述
图来自上面引用的链接中的讲解。
1,标记清除,先标记,标记完之后再清除(即原地释放空间),效率不高(因为标记是GCROOT各种遍历循环操作,很慢),且会产生碎片(即内存空间不连续)
2,标记整理,标记完毕后,让所有存活的对象滑动到另一端,相对于标记清除,标记阶段还是一样的都是GCROOT,但是会多线程整理内容,把未使用的内存放到一起,存活的对象移动(注意是移动哦,不是复制,没有备份的,更形象词是滑动,很神奇,我也很想知道是怎么个把内存滑动法,哈哈!)到一起。
标记清除和标记整理整体而言效率差不大,只是后者不产生碎片。
3,复制算法,可以理解成将内存分为两大块,一大块用的差不多的时候,将存活的对象复制到另一块上将存活对象复制到新空间中,再直接将大内存一次性清理掉,这种一次性清理相比较上面两种一块块“清除”,“整理”还是快一点的。其实survive区的from到to就是这种复制算法。

6,各jdk版本使用的GC收集器(GMS,G1),及对应的回收算法?

  1. jdk1.7 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

  2. jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)

  3. jdk1.9 默认垃圾收集器G1

  4. jdk10 默认垃圾收集器G1

  5. jdk11 默认垃圾收集器G1
    这里的Parallel Scavenge是并行清除收集器的意思。Parallel 是并行(收集器)的意思,多个线程同时进行垃圾回收,在1.7之前都是默认的串行(收集器)回收,stw时间很长,很耗时,同时该jvm应该也不支持并行。因此总体来说jdk1.7和1.8是使用的cms收集器。cms是并发收集器(即一边做业务一边做垃圾回收),采用的是标记清除算法,包括阶段(初始标记(需stw),并发标记,重新标记(需stw,因为要标记并发期间产生的要标记对象),并发清除),同时也会产生碎片,要多次标记才能清除,但是可以不停业务,虽然垃圾回收也抢占了一些业务线程资源,可以理解成没有stw,或stw的时间很短。适合吞吐量很大的系统。
    G1收集器从整体来看用的是标记整理算法,包括(初始标记(需stw),并发标记(原理是把被标记的对象记到日志中),最终标记(需stw,原理是把并发标记阶段的日志合并),筛选回收(对日志中记录的做筛选))它相对于CMS的有点,我认为就是能够充分协调应用多核cpu等硬件优势,至于是怎么个协调法就没必要深究了。再者就是可预测stw停顿时间。总而言之,它的优势就是即简短了stw时间又避免了产生碎片。
    最后总结,无论是G1还是GMS,在初始标记阶段都会产生短暂的stw,且都是并发收集器。

7,标记清除算法要多次执行的原因?

是为了重新标记业务线程与垃圾回收线程并行期间业务线程产生的需要回收的对象。而且这个过程是要多次反复执行的,直到重新标记的个数是0,这样就达到了在重新标记阶段没有stw的目的。至于在cms中为啥只有四个阶段不用多次,是因为在重新标记阶段做了一次stw。

8,分代收集算法

就是根据新生代和老年的的作用不同灵活使用三种垃圾回收算法。
1,如新生代对象存活时间短,回收的快,那就用复制算法,只需付出少量的复制成本就好了,而不用一个个清除整理,因为要记住是回收的量跟多哦。
2,如老年代,对象存活时间长,且没有切分给更多的区,也无法使用复制算法,所以只能使用标记清除和标志整理。

9,fullGC与YGC,OOM

FULLgc 发生在老年代的内存不够时,YGC发生在新生代的内存不够时。如果老年代也满了就会OOM了,再者,由于老年区:新生代=2:1,且新生代的垃圾算法更多样化,所以fullGC的时间远大于Ygc的时间,因此要尽可能的减少fullGC,也就是减少stw.

10,什么时候做GC?

1,YGC是系统自动控制的,只要伊甸区满了一定量就会自动触发
2,至于老年代,有以下GC机制,首先是内存达到一定阈值,再者可以在代码中手动做system.gc().还有触发悲观策略时,也就是提前预知了下一波新生代到老年代内存不够了,提前做fullgc了。

11,java类加载过程

1,加载,通过全类名获取该类的二进制流,并把类中的静态存储结构转化为方法区的数据机构,并在内存中生成该类的对象。
2,验证,验证class文件信息是否正确,如文件格式,常量的是否有不被支持的类型,字节码语义是否正确。字节码调整指令是否正确等。
3,准备,就是为类中的静态变量在方法区中分配内存并做初始化为默认值(如public static int value=123,在此阶段赋默认值0,在初始化阶段才会给123),其他的对象实例暂不分配内存,而是在使用时创建。
4,解析,这个好像没干什么事
5,初始化阶段,就是真正开始执行类中定义的程序代码了。

12 双亲委派加载机制

是类加载器(classloader)的一种加载类的模型机制,比如说要new A()了,也就是类加载器收到一个类A的加载请求时,会先自下而上到自定义的类加载查找这个类是否已经加载过了,如果有就返回字节码,如果没有就到上一层父类加载器(只是这么个说法,其实不是继承关系),做同样的检测。如果到顶级加载器中都没有加载过该类,就说明这个类之前没有使用过。那么就需要根据A类的全路径从顶级加载器自上而下到该类加载负责的jar包中查看是否有A类,如果有就加载,没有就到下一层加载器加载。以此类推,如何还没有就要报我们常见的异常之一“classnotFoundException”。
总而言之,这个过程是一个凸曲线的过程,先检查再加载,加载过就不重复加载。再者就是越是定义的类加载器负责的包就越基础,类加载本身也是一个class,因此顶级类加载器是由C编写的bootstrap,这个是java的稳定性体现之一。

13 几种常见类加载器

由12知,自上而下有,启动类加载器,扩展类加载器,系统类加载器(一般我们写的代码都是由这个加载的),自定义类加载器

14 ,如果对象的引用被置为null,垃圾回收器是否会立即释放对象占用的内存?

不会,在下一个垃圾回收周期中,这个对象将是可被回收的。

15 串行收集器和吞吐量收集器的区别是什么?

说的很高大上,其实就是一个单线程做垃圾回收,比较慢。一个是多线程并行回收,快。

16 垃圾回收会发生在永久代吗?

不会,但如果其满了,还是会触发fullGC的。

17 jvm调优的指令

指令有很多,也很难记,这里就写两个常见的吧!

java -Xms3072m -Xmx 3072m -Xmn2048m -Xss1m -xx:metaspaceSize=256m

xms是最大堆内存,xmx是最小堆内存,二者通常设置成一样,避免gc后jvm重新分配内存,消耗性能。目前tomcat容器可以设置的最大内存为3G,也就是3072m.
xmn是年轻带大小,虽然说年轻代只占1/3堆大小。但是可以根据实际情况调整。调大年轻内存,增多minorGC次数,减少fullGC。
xss是单个线程的内存大小。-xx:metaspaceSize 是方法区大小。虽说方法区的压力相对较小,但是如果其满了还是会触发fullgc的。要是程序包特别大的话还是要注意的。

18 jvm生命周期

这种问题就以一个main方法的运行过程说一遍吧。
首先,当一个程序启动之前,它的class会被类装载器装入方法区(不好听,其实这个区我喜欢叫做Permanent区),执行引擎读取方法区的字节码自适应解析,边解析就边运行(其中一种方式),然后pc寄存器指向了main函数所在位置,虚拟机开始为main函数在java栈中预留一个栈帧(每个方法都对应一个栈帧),然后开始跑main函数,main函数里的代码被执行引擎映射成本地操作系统里相应的实现,然后调用本地方法接口,本地方法运行的时候,操纵系统会为本地方法分配本地方法栈,用来储存一些临时变量,然后运行本地方法,调用操作系统APIi等等。

19 对象在堆中的存活过程及相应的垃圾回收

创建的对象首先被放在年轻带的伊甸区,伊甸区快满了,就做minorGC,存活的对象放到年轻代的一个survivor区,当这个survivor区快满了,也会做minorGC,仍然存活的对象会被统一复制到另一个survivor区;这样连个survivor区的数据来回做复制清除操作,直到某些对象经过n(一般是15)次回收后仍然生存或者survivor即将满了,survivor区的存活对象将被放到老年代。当老年代也快满了,就会做fullGC,知道fullgc也无法解决老年代的内存满的问题,就会出现内存溢出。

20 jcnosole 监视jvm工具使用

jconsole是jdk自带的可视化监视工具,
使用过程是要在被监控的服务中加上对应配置,主要是服务ip,端口,用户名密码等。
我这里使用的是tomcat服务。所以要在bin/catalina.sh脚本中加入以下配置:

#---------Execute The Requested Command-------------
CATALINA_OPTS="$CATALINA_OPTS -Djava.rmi.server.hostname=168.7.2.111
-Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=12345
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false"

注意,上面的配置一定要加载#---------Execute The Requested Command-------------已有的这行配置下。
上面的hostname要改成主机名。这里的配置也比较简单没有设置用户名和密码。

21 java 锁有哪些

这个看怎么区分了:
公平锁和非公平锁
共享锁和独享锁
乐观锁和悲观锁
分段锁
自旋锁

22 Stack Overflow栈溢出的解决办法

代码没有逻辑的问题的话就调整jvm中JVM的栈内存把,默认是1m,修改属性是-Xss1m。调大些把。
代码方面,减少方法调用层级,进而减少压入栈中的栈帧数目。还有就是极端情况了,给静态变量设置大空间数组,都知道静态变量\变量都是放在方法区的,但是在调用的时候还是要压入到栈中的,如果静态方法中有个大的静态数组,比如说长这样:

static char chdata[2*1024*1024];

这个数组会被都加载到栈中。那就溢出了。不过这太极端了,别考虑了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

老马识途2.0

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值