目录
1.JDK、JRE、JVM三者关系?
2.谈谈JVM的理解?
3.JVM执行字节码的过程?(执行方式)
4.JVM的组成是什么?
5.什么是类加载机制?
6.什么是双亲委派模型?
7.JVM内存模型
8.堆区的组成
9.堆空间设置内存大小
10.JVM如何判断一个对象是垃圾
11.GCROOT是什么?
12.常见的GC垃圾回收算法有哪些?
13.常见的GC垃圾回收器有哪些?
14.内存分配过程中会发生什么GC?
15.JVM内存溢出会有哪些场景?
16.JVM总是FULL GC怎么办?
17.JVM怎么排查OOM
18.JVM的优化
1.JDK、JRE、JVM三者关系?
JRK=JRE+各种工具(编译器,Jsp工具)
JRE=JRE核心类库(.lang,.IO)+JVM
JVM是程序运行的平台,基于JVM的机制可以在多个系统或硬件上运行java程序。
2.谈谈JVM的理解?
JVM是一种java程序运行的一种环境,它可以提供在不同操作系统,不同硬件中运行java程序的条件,对于不同机器,通过jvm虚拟机它会将编译好的.class文件转换成当前机器可以识别的机器码给cpu运行处理。并且它提供了对程序的垃圾回收,内存管理优化程序性能的功能。
3.JVM执行字节码的过程?(执行方式)
字节码执行的过程是,首先将.java程序加载,加载成.class字节码文件,通过jvm虚拟机的解释器可以将.class字节码文件。再通过解释器一行一行的将字节码解释成当前机器可以识别的机器码供cpu识别。如果对于一些比较执行频繁的代码,它会进行一次计算当到达一定阈值的时候,它会利用jit即时编译器将字节码文件进行jit即时编译,让后将字节码记录下来,下次使用就不需要在经过解释、编译器,可以直接使用,提供效率。
4.JVM的组成是什么?
类加载器、运行时数据区、执行引擎、本地方法区、本地方法接口
运行时数据区有->方法区、堆、本地方法栈、虚拟机栈、程序计数器
执行引擎有 解释器、jit即时编译器、GC垃圾回收器
5.什么是类加载机制?
java程序在运行中,会在虚拟机中加载.class字节码文件,并且通过加载、验证、准备、解析、初始化五步,最终形成一个虚拟机可以使用的类。
加载字节码文件,通过验证格式是否符合jvm,数据是否正确,使用的符合是否符合规范。准备将数据赋默认值,解析符合引用变为直接引用,最后初始化赋值。
6.什么是双亲委派模型?
双亲委派模型是一种类加载的特殊机制,在一个类加载的过程中它不会首先自己加载这个类,而是寻找父类加载器进行加载,不断向上寻找直到找到bootstrap classloader为止,如果还不能加载,那么此时才会直接加载此类。除了bootstrap外,每一个加载器都有父加载器。
目的是防止核心类库被自己或第三方的类进行污染,比如 当自定义一个类String,那么我首先肯定要向父加载器加载,如果没有再不断向上找,如果我直接加载当前这个类,那么我的引用类型String就会出现问题。
Bootstrap classloader是加载一些核心类库的加载器
Extension classloader是加载/lib/ext下的或者用户指定目录下的文件
Application classloader是加载用户目录下的类加载器
7.JVM内存模型
按照内存空间划分为,线程共享和线程私有。线程共享是 堆 方法区(1.8以后是元空间) 线程私有的是虚拟机栈,本地方法栈,程序计数器。
8.堆区的组成
堆区分为新生代和老生代,新生代里有两个区,一个是Eden区,占8/10,survivor区占2/10,其中survivor区有survivor0和survivor1区平分。
为什么要这样划分呢?
因为是基于内存管理和垃圾回收的原因,具体是因为在创建的新的的对象的时候它会出现在Eden区,那么这个区会有非常多新的对象出现,那么就需要频繁的进行垃圾回收,如果满了就通过minorGC回收,它会将Eden区和其中一个survivor区的存活后的对象放到另一个survivor区,等到一定次数到达阈值后会永久的存放在永久代里。这样实际上通过survivor机制起到一个整理收集的作用,防止出现垃圾碎片,并且通过这种机制,不必担心Eden区的内存经常满,导致fullGC长时间回收让程序较长时间发生等待。每次回收会给年龄+1,等到达某个阈值比如15岁的时候那么会把他放入老年代中,可以通过jvm参数进行设置。
9.堆空间设置内存大小
默认堆空间为物理内存的1/64,最大不允许超过物理内存的1/4
-Xms 设置初始内存 -Xmx 设置最大内存大小
-Xmn 设置新生代内存大小
-XX:NewRatio 2 设置新生代与老生代的比例 1:2
10.JVM如何判断一个对象是垃圾
有两种方法,一种是引用计数法,另一种是可达性分析法。
1.每个对象都会维护一个属于自身的程序计数器,如果该程序被引用了,那么该程序的计数器+1,如果引用断开计数器-1。最后通过计数器最后是否为0来判断是否是垃圾,如果没有被引用的就是垃圾也就是程序计数器为0。这种方式简单,但是会有循环引用的问题,A->引用B B->引用A,所以他们程序计数器会一直为1,所以他们永远不会被当成垃圾被清理。
2.JVM会从GC Root开始遍历,看程序直接的引用关系,只要可以访问到就标记,最后遍历完所有可访问的节点过后进行清除哪些未被标记,也就是不可到达没有被引用的对象,就讲他们清理掉。
11.GCROOT是什么?
本地方法栈引用的对象,虚拟机栈(栈帧中的局部参数)引用的对象,本地方法区的常量池中引用的对象,方法区中静态属性引用的对象。这些都算GCroot,也就是垃圾回过程中,判断是不是垃圾需不需要回收的根节点,从根节点开始遍历每个可以访问到的对象。
12.常见的GC垃圾回收算法有哪些?
标记-清除法
分为标记阶段和清除阶段,标记阶段从GCroot开始遍历,判断哪些是可以被清理的,也就是判断引用关系,最好没有被标记的直接就被清理了。坏处是这样可能会造成内存碎片,因为清理的数据不是连续的。
复制算法
将空间分为两部分,同样进行标记,如果是存活的数据连续的复制到另一部分空间,这些数据紧密排列,然后将第一部分的数据全部清除,这样避免了内存碎片。但是需要一半额外的空间,并且会有一段时间处于空闲状态。
标记-整理法
同样进行标记,将被标记过的数据移动到空间的另一侧,然后清理被标记意外的数据。不需要额外空间,但是移动过程消耗性能。
13.常见的GC垃圾回收器有哪些?
1、serial 垃圾回收器 :使用的是单线程进行垃圾回收,当垃圾进行回收的过程中会暂停所有其他线程,使用的是复制算法是JVM中在client模式下默认的新生代的默认垃圾回收器,适合单核服务器,小型应用。
2、serial old 垃圾回收器:使用的也是单线程的进行垃圾回收,使用的回收方法是标记整理法,是JVM中client模式下默认的老年代的垃圾回收器。也是CMS垃圾回收器失败的替代方案。
3、ParNew 垃圾回收器:通过多线程进行新生代垃圾回收,使用的复制算法,是jvm在server模式下的默认回收方法
4、parallel cavenge 垃圾回收器:使用多线程进行新生代垃圾回收,使用的复制算法,关注吞吐量。有自适应调整新生代的Eden区和survivor区的大小。
5、parallel old 垃圾回收器,进行多线程垃圾回收,老生代,使用的标记整理算法。
6、CMS 垃圾回收器:清理老年代的垃圾回收器。使用的标记清除算法,在标记和清除的时候使用多线程清除,在失败的时候回退为serial old 回收算法。使用分代思想进行垃圾回收,但是会有内存碎片。初始标记-并发标记-重新标记 在初始标记和重新标记仍然会暂停线程
7、G1垃圾回收器:使用的是分代思想,但是它是把内存空间分为一个一个的相同大小的内促区域,可以计算每个regin区域的优先值,根据有限顺序进行清除指定区域的内存,保证空间不会很容易的慢,避免垃圾回收器在整个区域进行回收。适合大内存,吞吐量大的程序。
14.内存分配过程中会发生什么GC?
分为两种,一种是整堆收集,还有一种是部分收集
整堆收集是将堆空间的内存整体进行回收,这时候程序会完全暂停。
部分收集是,minorGC 新生代垃圾回收 major GC 老生代垃圾回收 还有Mix GC(仅限于G1垃圾回收器) 混合垃圾回收
15.JVM内存溢出会有哪些场景?
1.创建了大量对象不释放
2.threadlocal存储的数据不释放
3.静态数组里的内容不清空
4.资源不释放
5.递归过深
16.JVM总是FULL GC怎么办?
调整Xms Xmm Xmn 堆空间 最大堆空间 新生代与老生代比例
设置合适的垃圾回收器进行回收防止Full GC
检查是否会有内存泄漏总是回收不了的对象
减少创建对象,对于资源尽量使用线程池连接池等。
17.JVM怎么排查OOM
1.代码方面检查看有没有资源没有释放,或者有没有数据没有被清空。
2.在报错提醒处查看异常。
3.使用JConsole或者VisualVM实时监控内存情况发现内存是否有泄漏。
18.JVM的优化
使用GCViewer GCEasy查看垃圾回收日志,可以调整堆空间的内存,设置最大堆空间,设置新生代空间,Eden区和survivor区的比例。防止出现FULL GC 的情况,或者调整垃圾回收器的类型,对于严格控制暂停时间的用CMS垃圾回收器,对于高吞吐量低延迟的可以用G1垃圾回收器,对于超大内存空间,大内存的程序可以用ZGC,对于多核服务器吞吐量优先的可以使用parallel 垃圾回收器。