面试复习JVM虚拟机

JVM

JVM 是可运行 Java 代码的假想计算机 ,包括一套字节码指令集、一组寄存器、一个栈、一个垃圾回收,堆 和 一个存储方法域。JVM 是运行在操作系统之上的,它与硬件没有直接的交互。这是JVM能跨平台运行的原因。

Java 源文件,通过编译器,能够生产相应的.Class 字节码文件,字节码文件又通过 Java 虚拟机中的解释器,编译成特定机器上的机器码。

当一个程序从开始运行,这时虚拟机就开始实例化了,多个程序启动就会
存在多个虚拟机实例。程序退出或者关闭,则虚拟机实例消亡,多个虚拟机实例之间数据不
能共享。

线程

jvm运行一个应用并发的执行多个线程
线程:当线程本地存储,缓冲区分配、同步对象、栈、程序计数器准备好后,就会创建一个原生线程。操作系统负责分配线程到cpu中,当原生线程初始化完毕后,就会调用java.run()方法执行线程。线程结束时释放原生线程和java线程所有资源。

jvm内存区域

1.线程私有内存(跟着线程同时创建销毁):程序计数器、虚拟机栈、本地方法栈
2.线程共享内存(跟着jvm同时创建销毁):方法区/永久代、堆
在这里插入图片描述

区域详解

1.程序计数器(线程私有):

计数器是一个寄取器,包含了当前执行的指令的地址。当指令被执行引擎获取后,计数器的存储地址自动+1,指向下一个指令的地址。

2.虚拟机栈(线程私有):

描述java方法的内存模型。每个java方法在执行的时候就会创建一个栈帧,存储局部变量表、操作数栈,动态链接。方法出入口等。出栈入栈代表了调用与完成。

栈帧:
 存储数据和部分过程结果的数据结构。同时也用来处理动态链接。

3.本地方法区(线程私有):

功能和虚拟机栈类似,但是虚拟机栈执行的是java方法,本地方法区执行的是Native方法,例如用C写的方法,本地方法区就变成了C栈

4.堆(线程共享):

创建的对象和数据以及引用类型的都保存在堆head中。堆也是垃圾收集器进行垃圾收集的重要区域。现在JVM采用的是分代收集算法,堆从GC角度上分:新生代(eden,survior from , survior to)和老年代

5.方法区/永久代(线程共享):

存储被JVM加载的类信息,常量、静态变量、即时编译器编译后的结果。(永久代的内存回收主要是针对常量池的回收和类型的卸载)。

运行时常量池:
包括字符串常量池和类信息、属性信息、方法信息等等。jdk1.7时,字符串常量池物理上转移到堆中,但是逻辑上还是在方法区。1.8后,方法区的实现从永久代变成了元空间,元空间不存在jvm中,存放于本地内存。

JVM运行时内存(堆的GC)

堆的GC是分代回收,堆的三分之一内存是新生代Young(eden,survior from 和survior to),三分之二内存是老年代Old。
在这里插入图片描述

新生代:

  • eden:java新对象的的出生地。(如果新创建的对象很大,直接分配到老年代)当eden内存不足就会触发minor
    GC。对新生代进行一次垃圾回收
  • survior from:上一次回收剩余的幸存者,作为下一次回收的被扫瞄者。
  • survior to:保留这一次的幸存者。

minor GC:

采用复制算法对标记的对象回收:

  • 1.eden、survior from复制到Survior to,然后年龄+1:把eden和survior from的区域中存活的对象复制到survior to区域中给(如果年龄达到了老年代的标准就赋值到老年代(一般15岁),或者survior to位置不够也放到老年代),同时把对象的年龄+1
  • 2.清空eden和survior from
  • 3.survior to和survior from互换,使得原来的survior to变成下次GC的survior from

老年代

存放程序中周期比较长的内存对象。

major GC:

采用标记清楚算法回收

  • 当老年代空间不足才会进行major gc。major gc会扫描一次所有老年代,用标记清除算法标记需要回收的对象,然后用垃圾收集器(一般是serial垃圾收集器)进行回收被标记的对象。

full GC

major gc清理老年代,full gc清理新生代和老年代,full gc

GC中如何确认垃圾(标记回收象)

1.引用计数法:

通过引用一个对象的次数来判断对象是否可以回收。用计数器标记对象,被引用一次,+1,引用失效,—1.当计数器为0的时候,既可以标记为可回收对象。但是因为引用计数法很难解决对象之间的循环引用问题,所以当前java没有采取引用计数法。

2.可达性分析:

当前java采取可达性分析确认回收对象。1.虚拟机栈(栈帧的变量表)中引用的对象 2.方法类中的静态属性引用的对象3.方法区中常量引用的对象4.本地方法栈中引用的native对象 堆的对象通常可以被以上的对象进行引用间接访问。如果堆中的对象没有被间接引用,那么这个对象就是可以回收的。

在GC视野中,把这些对象称为GC ROOT。以GCroot作为跟起点,往下搜索,即给定一个集合的引用作为根出发,通过引用关系遍历图,如果堆中一个对象到gc root没有任何相对链路相连时,那就标记为可回收对象。
一个对象被判断两次以上为可回收对象才会面临回收。

GC如何回收对象

1.标记清除法:

最基础算法。分两个阶段:标记和清除。1.标记阶段通过可达性分析标记出所有需要回收的对象.
2.清除阶段回收被标记的对象占用的空间,collector收集器对堆内存从头到尾的进行遍历,把标记的对象进行回收。
缺点:1.清楚效率低 2.清楚后产生大量内存碎片,可能会导致大的对象找不到可用内存空间OOM。

2.复制算法:

为了解决内存随便而产生:把内存分成两块,每次存储只是用到1块内存,当这块内存满了后,把活着的对象复制到另一块,然后清空已使用的。
优点:效率高,不易产生碎片
缺点:原本内存被压缩成了原来一半,当对象产生过多的时候,算法效率大大降低

3.标记整理法:

综合标记清除法和复制法,在第一阶段标记回收对象后,将存活的对象移动到另一端,然后清除端边外的对象。

4.分代收集算法:

当前JVM才用的算法:核心思想是根据对象存货的生命周期将内存划分不同域,新生代和老年代,一般老年代特点是只有少量对象需要回收,新生代每次回收大量垃圾。
新生代使用复制算法:因为新生代每次回收大量的对象,所以即需要复制的操作比较少,且新生代划分内存不是按1:1,是按8:1:1划分eden,survior from 和survior to
老年代:使用标记复制法:在新生代存活下来一次GC后就年龄加一,当对象内存过大活着年龄到达15就会到老年代。

5.分区收集算法:

将堆空间划分为连续的不同小区,每个小区独立使用,这样的好处是可以控制一次回收多少空间。根据目标停顿时间,每次合理的回收若干个小区间而不是堆,从而减少一次GC产生的停顿。

引用类型(强度从强到弱)

  • 1.强引用:类似A a = new A(),强引用是最普遍的引用,一个对象只要有强引用,当内存不足时候JVM宁愿抛出oom也不会回收该对象。
  • 2.软引用:java提高SoftRefernce类实现软引用:只要内存足够,就不会回收软引用。内存不足则回收。
  • 3.弱引用:java提供WeakReference实现。一旦发送GC,只要是弱引用,无论内存是否足够,只要是在回收器的扫描下被扫描到了,就得回收(但是垃圾回收器是优先级很低的线程,不一定能很快发现)
  • 4.虚引用:java提供PhantomReference实现虚引用。虚引用不能单独使用,必须配合引用队列使用,只要作用是跟踪对象被垃圾回收的状态。(任何时候都可能回被会回收)

垃圾收集器

新生代

serial垃圾收集器(单线程、复制法),ParNew垃圾收集器(serial+多线程),parallel scavenge(多线程复制算法、高效)

  • serial垃圾收集器:最基本的垃圾回收器,使用复制算法。serial是单线程的回收器,只使用一个cpu或者一个线程去完成工作,因为没有与其他线程交互的开销,所以效率很高,是jvm运行在client模式下默认的新生代垃圾回收器。

  • ParNew垃圾收集器:是serial的收集的多线程版本,也使用复制法,serial基础上采取了多线程收集,跟serial一样工作时候会暂停其他的工作现场。是java虚拟机运行在server模式下新生代的默认收集器。

  • parallel scavenge收集器:也是多线程采取复制算法的收集器。突出重点是有个自适应策略:关注程序是否在一个可控的吞吐量(吞吐量=程序运行时间/(程序运行时间+垃圾收集时间))

老年代

serial old垃圾收集器(单线程标记整理法),parallel old(多线程标记整理法)CMS(多线程标记清除法)

  • serial old:serial 垃圾回收器老年代版本,单线程使用标记整理法。运行在client的默认老年代的垃圾回收器。

  • parallel old垃圾收集器:parallel scavenge的老年代版本,多线程的标记整理法

  • CMS收集器:采用多线程标记清除法:主要目标是获取最短停顿时间的回收垃圾。

  • G1收集器:Garbage first收集器,jvm最新版本有:1.基于标记整理法,不产生内存碎片2.精准控制停顿时间,在不牺牲吞吐量的情况下,实现低停顿回收。3.避免全区域回收,把堆内存分成几个大小固定区域,跟踪这些区域的垃圾收集进度,同时后台维护一个优先级表。,根据允许的最大收集时间,优先回收垃圾最多的区域。区域划分和优先等级回收能很好的提高G1收集器的收集效率。

当前JVM收集器:

查看命令:
java -XX:+PrintCommandLineFlags -version

java -XX:+PrintGCDetails -version

显示:

  • UseSerialGC,虚拟机运行在Client模式下的默认值,Serial+Serial Old。
    UseParNewGC,ParNew+Serial Old,在JDK1.8被废弃,在JDK1.7还可以使用。
    UseConcMarkSweepGC,ParNew+CMS+Serial Old。
    UseParallelGC,虚拟机运行在Server模式下的默认值,Parallel Scavenge+Serial Old(PS
    Mark Sweep)。 UseParallelOldGC,Parallel Scavenge+Parallel Old。
    UseG1GC,G1+G1。

书上默认的jdk1.8jvm收集器是UseParallelGC,即parallel scavenge + serial old,实际现实中上是:parallel scavenge+parallege old(即使现在jdk 查询命令查到的是UseParallelGC)
jdk1.9的是G1+G1
在这里插入图片描述

Guess you like

Origin blog.csdn.net/weixin_43859562/article/details/121379976