面试问到JVM看这一篇就够了

一、什么是JVM?

二、JVM内存模型

根据线程共享和线程独占分层大的两部分,其中线程共享分为堆(new出来的对象放在堆中,对象也可能会有栈上分配-内存逃逸),方法区(也叫元空间,存放class对象,class中包含了方法和属性,还有存放静态变量、常量),线程独占分为
线程共享区

  • 堆:大部分new出来的对象是存储再堆中的,
  • 方法区:存储静态常量、静态变量、类的class对象、

线程独占区

  • 虚拟机栈:栈内部有栈帧(局部变量表,操作数栈(局部变量一开始被压入栈,计算的时候弹出栈,计算完了再进栈,一般情况下操作数栈是空的,因为它只是用来计算),动态链接,返回地址)组成,先进后出
  • 本地方法栈:和虚拟机栈作用差不多,只是虚拟机栈服务的是java方法,本地方法栈服务的是native方法(本地方法,和平台息息相关,可移植性差)
  • 程序计数器(PC寄存器):记入当前线程执行到了多少行代码

在这里插入图片描述

三、JVM常用调优参数

  • -Xmx:堆内存最大值,默认值是电脑内存的四分之一,-Xmx2g
  • -Xms:堆内存最小值,-Xms2g
  • -Xmn:设置新生代的大小,默认是堆的三分之一
  • -Xss: 线程栈的空间大小(深度),-Xss256k
  • 设置老年代和新生代的比例
  • 设置E区和S1、S2的比例

四、为什么一般情况下堆内存的最大和最小值设置一样大?

减少fullgc回收次数,当堆内存达到最小值时会进行一次fullgc,如果设置太小fullgc会很频繁,当达到最大值时还内存不够的情况下会进行扩容,对最小值扩容

五、JVM运行时数据区域有那几部分组成?各自作用?

六、GC收集器有哪些?

MinorGC:

  • Serial(s):单线程收集器,串行,复制算法,简单高效
  • ParNew(PN,推荐) :多线程收集器,并行,复制算法,常和CMS一起使用
  • ParallelScavenge(PS,默认) :多线程收集器,并行,复制算法,关注吞吐量

MaiorGC:

  • SerialOld(SO):单线程收集器,串行,标记整理算法
  • ParallelOld(PO,默认):多线程收集器,并行,标记整理算法
  • CMS(推荐):多线程收集器,并发,标记清除算法。

FullGC:
-G1:多线程收集器,并行,标记整理算法

在这里插入图片描述

七、哪些对象可以作为GC Root的对象?

1、局部变量
2、静态变量
3、本地方法栈
4、静态常量static final

八、你知道哪几种收集器,各自的优缺点?简单说下CMS

S:单线程收集,非单核服务STW比较长
PN:优点,多核执行快(一般和CMS一起使用)
PS:重点在吞吐量(用户线程执行时间/用户线程执行时间+GC执行时间)

CMS:
初始标记(STW):标记老年代直接与gc root 相关连的对象,标记可达
并发标记:并发标记是标记gc root下面的,相比第一个要时间长一点(并发,和用户线程一起执行)
重新标记:跨代引用的对象,但是这些对象是可达的。还有一些刚进来老年代的对象stw,卡表
并发清除:

九、什么是Full GC?MinorGC?MaiorGC?STW?

Full GC:MinorGC+MaiorGC
MinorGC:新生代回收的GC
MaiorGC:老年代回收GC
STW;STOP THE WORLD,停止所有用户线程

十、JVM中一次完整的GC流程(从ygc到fgc)是怎么样的?

流程:经过15次的ygc(复制算法)到老年代,大对象直接进入老年代,
非正常流程:
动态年龄:s区50%以上的对象年纪>S区的平均值,就会进入老年代(老年代不足发生fullgc)
空间担保:在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。

十一、OOM异常定位相关面试题,如何判断是否有内存泄漏?

内存泄漏的特点:因为我们对不能被gc回收,就会导致我们内存泄漏。
使用jstack查看经过GC执行后的老年代对象是否被回收,比如第一次回收后200M第二次300第三次400,就可能有发生内存泄漏。
如过服务不可用,Jstack看到fgc执行次数远远大于ygc,也有可能发生内存泄漏

十二、OOM说一下?怎么排查?哪些会导致OOM?OOM出现在什么时候?

外包给的一个项目:后台没有写分页(前端做分页),数据量特别大的时候,调用接口会直接把堆内存撑爆,直接把对象全部回收

内存泄漏最终会导致内存溢出
内存泄漏产生的内存溢出:泄漏的对象不会被回收掉,内存溢出的对象会被直接回收,服务不可用,一创建对象就被回收
递归是内存溢出,超出栈的深度,直接被回收

oraclejdk才有这个工具 openjdk没有自带

十三、java中都有哪些引用类型?

强引用:Object o = new Object(); GC不会去回收强引用的对象。。。方法出异常,方法退出栈了,所有的局部变量都会被回收掉,所以局部变量中的强引用会被回收
软引用:SoftReference对象 当JVM堆内存占满的时候就会回收软引用的对象,一般用来做缓存
弱引用:一般用的比较少WearkReferenve,只能存在于下一次gc之前,每次发生年轻代gc或者老年代GC就会被回收
虚引用:Object o=new Object();O=null;提醒我们的gc来回收这个对象

十四、引用传递和值传递的区别?

基本数据类型值传递
引用类型引用传递

十五、new String()虚拟机做了些什么?

1、去找到当前对象class
2、找不到当前对象的class(因为是第一次加载),加载当前对象的class
3、为当前对象开辟空间
4、初始化属性(父类的属性也会初始化,先父类)
5、执行构造方法

十六、什么是指针碰撞?什么时候会发生指针碰撞?

  • 指针碰撞:堆中有一个指针指向下一个内存地址,当多个线程同时创建对象的时候会产生指针碰撞
  • 解决方法:调用cas方法实现同步,在cpu硬件层面加锁,不在代码层面,lock锁是在代码层面加锁,但是内部也是采用cas

十七、什么是栈上分配?

1.6.25之后jdk提供了一个栈上分配,我们的对象可以分配到栈上,局部创建的对象,只是局部引用,其他方法用不上,这个对象就不是很需要放到堆中共享,就放到栈帧中,

十八、什么是内存逃逸分析?

判断是否有其他的地方在使用这个对象,那这个对象就会放入堆中,而不是栈帧私有,或者说这个对象占的内存太大,也只能放入堆中。
简单来说正常的局部创建的对象只会在局部使用,所以正常是存放在栈帧上的,但是因为对象过大,或者别的地方引用到了这个对象,那么这个对象就会逃到堆中

十九、除了堆还有哪里可以存放new出来的对象?

栈上分配,存在栈上,不在栈帧中

二十、对象的三个组成部分?

  • 对象头:有类型指针(指向方法区的class对象)、Hash值、分代的年龄、持有的锁、当前线程id、数组长度(只有数组对象才有),相同的类创建的不同的对象这些都差不多,最大的差别在实例数据
  • 实例数据:存放对象的一些属性
  • 填充数据:java虚拟机要求对象必须8个字节的倍数导致的,比如byte类型是4字节的,会填充成8字节的。

二十一、引用对象有哪两种方案?

1、栈或方法区中的引用直接引用堆中对象
2、栈或方法区中只是句柄引用,在堆中还有一个句柄池,句柄引用通过句柄池去引用对象

二十二、使用句柄池的好处是什么?

在垃圾回收的时候,只要改变句柄池的引用的就好,不需要改变栈中的引用
坏处:效率低,需要两次引用

二十三、什么时候会使用句柄池?

不同的虚拟机引用对象的方式不同

二十四、jvm怎么判断一个对象是否要被回收(对象是否为垃圾)?

引用计数法(JDK1.7):
1、当前没有引用的对象
2、没有互相引用的对象
存在的问题:堆内存对象相互引用的时候不会被回收

可达性算法(JDK1.8,根搜索算法,gc root):
从栈、方法区、本地方法栈中去找引用,判断堆中的对象是否可达,不可达被回收,gcroot可达的都是有用对象,其他的为垃圾对象

二十五、为什么jvm不直接采用fullgc去回收整个堆?

效率不高,程序中,绝大部分的对象,存活不到3秒钟,所以就有了分代:新生代(大部分都只活到新生代,新生代分为:Eden区、s0(from)区、s1(to)区),老年代。
就是一个接口不会调用超过3秒,否则就要去优化接口了。

二十六、一个完成的gc流程是如何?

Eden区满了,根据gcroot算法回收对象,没被回收的对象年龄增加,到s0区
S0区满了,采用复制算法,把有用的复制到s1区,然后直接删除所有的S0中的对象,
S1区满了,也是复制到S0区,删除S1区的所有对象,当一个对象被复制了15次的时候进入老年代,
老年代达到一定的比例,采用标记压缩算法,标记的是可达对象,所有的没有被标记的压缩到一块,删除这一块,然后重置标记,等到下一次标记压缩算法。
标记压缩算法的缺点:速度慢,整个过程都是stw(stop the world 整快区域长时间停止)
标记清除算法:也是标记可达对象,缺点是碎片化严重,容易遗漏。

二十七、GC六种算法?

  • 分代算法:
  • 复制算法:S区满了,把可达对象复制到另一个S区,删除当前S区所有对象
  • 标记压缩:标记的是可达对象,所有的没有被标记的压缩到一块,删除这一块,然后重置标记,等到下一次标记压缩算法。缺点就是速度慢,整个过程都是stw
  • 标记清除:1、初始标记:标记老年代中直接与gcroot关联的一个对象(stw)
    2、并发标记:用户线程会和我们的gc一起执行,标记gcroot下关联的其他对象(非stw)
    3、重新标记:扫描新生代,标记老年代中有被年轻代可达的对象(stw),一般会提前进行年轻代Gc,提高重新标记的效率,减少stw的时间
    4、并发清除:清除老年代中未标记的对象(非stw),如果在清除的时候有对象进入老年代,默认不会去删除该对象(有一张card表记录),
    缺点:
    1、不能处理浮动垃圾(在并发清除的时候被标记的对象突然不可达了)
    2、占用cpu高,因为和用户线程同时执行
    优点:因为可以并发,速度会比较快
  • 引用计数:JDK1.7的判断对象是否要被回收的算法,回收没有被引用的对象,给对象添加一个引用计数器,每当有一个地方引用它时,计数器值就加1,当引用失效时,计数器值就减1,任何时刻计数器为0的对象就是不可能再被使用的。
  • 可达性分析法:JDK1.8的判断对象是否要被回收的算法,标记可达对象,回收不可达对象,如果两个对象相互引用,且gc root不可达也会被回收,解决了引用计数不可回收相互引用的对象的缺点

二十八、并行并发的区别?

  • 并发的关键是你有处理多个任务的能力,不一定要同时。用户线程和垃圾回收线程同时进行,比如吃饭中途打电话,打电话完继续吃饭
  • 并行的关键是你有同时处理多个任务的能力。多个垃圾回收线程同时进行,比如同时吃饭和打电话

二十九、为什么要有Survivor区?

如果没有Survivor,Eden区每进行一次Minor GC,存活的对象就会被送到老年代。老年代很快被填满,触发Major GC(因为Major GC一般伴随着Minor GC,也可以看做触发了Full GC)。老年代的内存空间远大于新生代,进行一次Full GC消耗的时间比Minor GC长得多。你也许会问,执行时间长有什么坏处?频发的Full GC消耗的时间是非常可观的,这一点会影响大型程序的执行和响应速度,更不要说某些连接会因为超时发生连接错误了。
Survivor的存在意义,就是减少被送到老年代的对象,进而减少Full GC的发生,

三十、什么是空闲列表?什么是线程缓冲区?

如果堆中的内存分配是绝对规整的,指针指向下一块内存的起始地址
如果堆中的内存分配不是规整的,那么就会有一个空闲列表来记录哪块空间没有被使用。
堆先为每个线程创建一个规整的线程缓冲区,把不规整的对象存放到线程缓冲区中,满了之后也是采用cas机制和其他线程去争夺更大的线程缓冲区
不规整和规整使用哪种看gc回收机制的算法

三十一、什么样的对象会进入老年代

1、大对象(年轻代位置不够了)
2、存活时间长的对象
3、空间担保:太大超过新生代分区内存
4、动态年龄:当前S区对应的对象年纪如果一半以上都大于S区平均年纪,则大于的对象都会被放入老年代

三十二、什么G1收集器?

G1收集器:没有很明确的分代,它把堆区间分成很多个区域(region区默认大小大概是1M~32M,最大2048块区域)
一部分为Eden区、一部分为S0,S1(简称S)区,一部分为Old区三部分、还有H区,也是老年代的一部分,用来存储大对象(大于0.5个regin,合并小regin,更大的对象喝更多的regin),其他都是空白未分配区

1、进行一次类似年轻代GC会把E区和S区中留下的对象放在空白区域,变成一个S区,原来的区域变成空白区域,类似复制算法
跨代引用:region区域中会分出一小块,表示当前区域是否引入其他区域
rset关系:region之间存在引用关系

三十三、各个区域的比例

老年代:新生代=2:1
eden区:Survivor区(S0):Survivor区(S1)=8:1:1

三十四、什么时候会发生FullGC?

空间担保:在发生Minor GC之前,虚拟机会先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果这个条件成立,那么Minor GC可以确保是安全的。如果不成立,则虚拟机会查看HandlePromotionFailure设置值是否允许担保失败。如果允许,那么会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次Minor GC,尽管这次Minor GC是有风险的;如果小于,或者HandlePromotionFailure设置不允许冒险,那这时也要改为进行一次Full GC。

1、老年代空间不足
2、方法区不足
3、主动调用System.gc()
4、空间担保失败

三十服务器cpu100%如何定位?

1、定位到哪一个程序cpu占用高,top
2、定位到当前程序,是哪一个线程占用率高
ps -mp pid -o THREAD,tid,time
ps -mp 5118 -o THREAD,tid,time
3、jstack 对应到线程以及他在执行的方法
jstack 5118 >a.txt打印线程快照
把tid转换16进制5140->1414
搜索找到nid=0x1414,从而定位到方法

三十五、内存溢出如何去定位?

1、程序里面有大对象,dump文件中可以查看大对象
2、大对象被谁引用 dump快照中可以找到线程快照,dump文件太大可以用jvm监控工具打开

三十六、OOM异常分两种情况:

1、内存溢出 OOM 满了之后gc回收对象
2、内存泄漏 GCC gc无法回收对象,一直往list中添加东西,这个对象就不会被回收

三十七、什么是Dump快照?

dump快照:堆有多大,快照就有多大,会stw
1、内存溢出时候打印堆内存快照
-XX:+HeapDumpOnOutOfMemoryError
该配置会把快照保存在用户目录或者tomcat目录下,也可以通过 -XX:HeapDumpPath=/tmp/heapdump.dump来显示指定路径
2、jatat -gcutil pid 打印gc执行次数和堆信息

三十八、JVM调优案例

1、年轻代GC和老年代GC频繁
原因:1、发生空间担保 2、动态年龄
解决方案:扩容年轻代
2、CMS重新标记耗时长
原因:1、年轻代对象太多
解决方案:1、在重新标记之前使用一次Y gC
2、扩大年轻代,让大多数对象在年轻代被回收,从而减少O gc的次数
主要调优是减少fullgc 和magior gc

三十九、其他

c.intern()//把字符串移动到常量池中
常量池可以看成一个hashset
javap -c xx.class 反汇编

发布了53 篇原创文章 · 获赞 2 · 访问量 1870

猜你喜欢

转载自blog.csdn.net/qq_42972645/article/details/104626619