zoukankan      html  css  js  c++  java
  • 面经总结:JVM

    • JVM内存模型?堆和栈?
    JVM内存模型包括5个部分,分为线程共享的和线程隔离的部分。
    线程共享的数据区是堆和方法区;
    线程隔离的数据区是虚拟机栈、本地方法栈和程序计数器;
      1)程序计数器:是一个比较小的内存区域,用于指示当前线程所执行的字节码执行到了第几行,可以理解为是当前线程的行号指示器。每个程序计数器只用来记录一个线程的行号,所以是线程私有的。(行号指示、线程私有
      2)虚拟机栈:一个线程的每个方法在执行的同时,都会创建一个栈帧,栈帧中存储的有局部变量表、操作数栈、动态链接、返回地址等,当方法被调用时,栈帧在JVM栈中入栈,当方法执行完成时,栈帧出栈。每个线程对应着一个虚拟机栈,因此虚拟机栈也是线程私有的。(栈帧
      3)本地方法栈:本地方法栈在作用,运行机制,异常类型等方面都与虚拟机栈相同,唯一的区别是:虚拟机栈是执行Java方法的,而本地方法栈是用来执行native方法的
      4)方法区:方法区是各个线程共享的区域,用于存储已经被虚拟机加载的类信息(即加载类时需要加载的信息,包括版本、field、方法、接口等信息)、final常量、静态变量、编译器即时编译的代码等。
      5)堆区:堆区的存在是为了存储对象实例,原则上讲,所有的对象都在堆区上分配内存。堆区也是Java GC机制所管理的主要内存区域
    • GC分代算法?
    内存分配,主要指的是在堆上的分配。Java内存分配和回收的机制概括的说,就是:分代分配,分代回收。对象将根据存活的时间被分为:年轻代(Young Generation)、年老代(Old Generation)、永久代(Permanent Generation,也就是方法区)。
    算法的选择:在新生代中,每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用“标记-清理”或“标记-整理”算法来进行回收。方法区永久代,回收方法同老年代。

    根搜索算法、标记-清除算法、复制算法、标记-整理算法

    根搜索算法:设立若干种根对象,当任何一个根对象到某一个对象均不可达时,则认为这个对象是可以被回收的。

    可以当做GC roots的对象有以下几种:

    (1.JVM stack;  2/3.方法区;4.本地方法栈)
    1. 虚拟机栈(栈帧中的本地变量表)中的引用的对象
    2. 方法区中的类静态属性引用的对象
    3. 方法区中的常量引用的对象
    4. 本地方法栈中JNI(Native方法)的引用对象

    标记-清除算法:当堆中的有效内存空间(available memory)被耗尽的时候,就会停止整个程序(也被成为stop the world),然后进行两项工作,第一项则是标记,第二项则是清除。

    (1)标记:标记的过程其实就是,遍历所有的GC Roots,然后将所有GC Roots可达的对象标记为存活的对象。

    (2)清除:清除的过程将遍历堆中所有的对象,将没有标记的对象全部清除掉。

    复制算法:复制算法将内存划分为两个区间,在任意时间点,所有动态分配的对象都只能分配在其中一个区间(称为活动区间),而另外一个区间(称为空闲区间)则是空闲的,当有效内存空间耗尽时,JVM将暂停程序运行,开启复制算法GC线程。接下来GC线程会将活动区间内的存活对象,全部复制到空闲区间,且严格按照内存地址依次排列,与此同时,GC线程将更新存活对象的内存引用地址指向新的内存地址。此时,空闲区间已经与活动区间交换,而垃圾对象现在已经全部留在了原来的活动区间,也就是现在的空闲区间。事实上,在活动区间转换为空间区间的同时,垃圾对象已经被一次性全部回收。

    标记-整理算法:标记-整理算法与标记-清除算法非常相似,它也是分为两个阶段:标记和整理。

    (1)标记:它的第一个阶段与标记/清除算法是一模一样的,均是遍历GC Roots,然后将存活的对象标记。

    (2)整理:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。因此,第二阶段才称为整理阶段。

    标记/整理算法不仅可以弥补标记/清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价,标记/整理算法唯一的缺点就是效率也不高,不仅要标记所有存活对象,还要整理所有存活对象的引用地址。

    参考:JVM学习之GC常用算法

    • GC的过程?
    对象根据被存活的时间被分为:年轻代、年老代、永久代;
    年轻代:对象被创建时,内存分配首先发生在年轻代,年轻代分为三个区域:Eden区、S0、S1;
    创建时分配在Eden区,满的时候执行Minor GC,消亡的对象清理,剩余的复制到S0;Eden再满时,Minor GC,存活的复制到S1,将S0消亡的清除,可以晋级的到Old,存活的到S1。切换多次,仍然存活的复制到老年代。
    年老代:内存不足时执行Full GC,标记整理。
    • 垃圾收集器?

    七种垃圾收集器简单比较:

    年轻代:Serial、ParNew、Parallel Scavenge;

    年老代:Serial Old、Parallel Old、CMS(Concurrent Mark Sweep);

    G1(Garbage Firest);

    1、Serial收集器

    新生代单线程的收集器,采用复制算法,在进行收集垃圾时,必须stop the world,

    它是虚拟机运行在Client模式下的默认新生代收集器;可以和Serial Old、CMS组合使用;

    2、ParNew收集器

    Serial收集器的多线程版本,采用复制算法,回收时需要stop-the-world;

    许多运行在Server模式下的虚拟机中首选的新生代收集器;可以和Serial Old、CMS组合使用;

    除Serial外,只有它能与CMS收集器配合工作;

    3、Parallel Scavenge收集器

    新生代收集器,使用复制算法又是并行的多线程收集器,关注系统吞吐量

    CMS等收集器的关注点在于尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐量(Throughput)。所谓的吞吐量就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间/(运行用户代码时间+垃圾收集时间),虚拟机总共运行了100分钟,其中垃圾收集花费1分钟,那吞吐量就是99%。

    停顿时间越短越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。

    参数:

    -XX:MaxGCPauseMillis:最大垃圾收集停顿时间;

    -XX:GCTimeRatio:吞吐量大小;

    -XX:UseAdaptiveSizePolicy:自适应的调节策略;打开参数后就不需要手动指定新生代的大小、Eden与Survivor区的比例、晋升老年代对象年龄等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整相关参数;

    4、Serial Old收集器

    Serial收集器的老年代版本,同样是单线程收集器,使用标记整理算法

    5、Parallel Old收集器

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

    6、CMS收集器(Concurrent Mark Sweep)

    老年代收集器;是一种以获得最短回收停顿时间为目标的收集器,基于标记清除算法

    过程如下:初始标记,并发标记,重新标记,并发清除;其中初始标记和重新标记过程需要stop-the-world;初始标记仅仅只是标记一下GC Roots能直接关联到的对象,速度很快;并发标记阶段就是进行GC Roots Tracing的过程;而重新标记阶段则是为了修正并发标记期间用用户程序继续运作而导致标记产生变动的那一部分对象的标记记录。

    优点是并发收集,低停顿,缺点是对CPU资源非常敏感,无法处理浮动垃圾,收集结束会产生大量空间碎片。 

    参数:

    UserCMSCompactAtFullCollection:默认开启,FullGC时进行内存碎片整理,整理时用户进程需停止,即发生Stop The World;
    CMSFullGCsBeforeCompaction:设置执行多少次不压缩的Full GC后,执行一个带压缩的(默认为0,表示每次进入Full GC时都进行碎片整理);

    7、G1收集器

    是基于标记整理算法实现的,不会产生空间碎片,

    过程:初始标记、并发标记、最终标记、筛选回收

    优点:并行与并发、分代收集、空间整合、可预测的停顿;

    可以精确地控制停顿,将堆划分为多个大小固定的独立区域,并跟踪这些区域的垃圾堆积程度,在后台维护一个优先列表,每次根据允许的收集时间,优先回收垃圾最多的区域(Garbage First)

     
    • GC时会对程序有什么影响?频繁GC怎么办?
    "stop-the-world" 机制简称STW,即,在执行垃圾收集算法时,Java应用程序的其他所有除了垃圾收集器线程之外的线程都被挂起。
    频繁GC:需要调优,比如控制新生代老年代比例,控制进入老年代前生存次数,控制年轻代eden和survivor比例(默认8:1)等;
    • JVM判断对象是否可回收?

    通过一系列的GC Roots作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC Roots没有任何引用链相连,即从GC Root到这个对象不可达,则证明此对象可以被回收。

    哪些对象可以作为GC Roots?

    (1.JVM stack;2/3.方法区;4.本地方法栈)

    • 类加载的过程?

    类从被加载到虚拟机内存中开始,到卸载出内存,整个生命周期包括7个阶段:

    加载、验证、准备、解析、初始化、使用和卸载。

    其中类加载的过程包括了加载、验证、准备、解析、初始化五个阶段。在这五个阶段中,加载、验证、准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始。

    加载

    1.字节流;2.方法区数据结构;3.对象-访问入口;

    验证

    文件格式的验证、元数据的验证、字节码验证和符号引用验证;

    准备

    为类变量分配内存并设置类变量初始值;这里所设置的初始值通常情况下是数据类型默认的零值(如0、0L、null、false等),而不是被在Java代码中被显式地赋予的值。

    解析

    解析阶段是虚拟机将常量池中的符号引用转化为直接引用的过程;

    初始化

    初始化类变量和其他资源;

    • 类加载器?双亲委派模型?
    类加载器ClassLoader?
    (三类、加载的类库、实现)
    系统提供的类加载器包括三类:启动类加载器Bootstrap、扩展类加载器Extension、应用程序类加载器Application。
    启动类加载器Bootstrap,加载JDK/jre/lib下的类库,也就是Java的核心库;启动类加载器是用原生代码而不是java实现的,并不继承自java.lang.ClassLoader,除此之外基本上所有的类加载器都是java.lang.ClassLoader类的一个实例。启动类加载器是无法被Java程序直接引用的。
    扩展类加载器Extension,加载/lib/ext目录的扩展库;可以被开发者直接使用。
    应用程序类加载器Application,加载Java应用类路径ClassPath所指定的类。
     
    双亲委派模型?
    (流程、优点、举例)
    工作流程是:如果如果一个类加载器收到了类加载的请求,不会首先自己去加载,而是把请求委托给父加载器去完成,依次向上。只有当父加载器在搜索范围中没有找到所需的类时,即无法完成加载,子加载器才尝试加载该类。
    双亲委派模型优点是Java类随着类加载器具备了带有优先级的层次关系,对于保证Java程序稳定运作很重要。例如Object类在lib的rt.jar中,无论是哪个类加载器要加载此类,最终都会委派给启动类加载器进行加载,这便保证了Object类在程序中的各种类加载器中都是同一个类。 防止不可靠甚至恶意的代码代替由父亲装载器装载的可靠代码。
     
     
    • 什么是热部署?什么是热加载?
    热部署,简单点来说,就是我们将打包好的应用直接替换掉原有的应用。
    每一个应用程序的类都会被ClassLoader加载,所以,要实现一个支持热部署的应用,我们可以对每一个用户自定义的应用程序使用一个单独的ClassLoader进行加载。然后,当某个用户自定义的应用程序发生变化的时候,我们首先销毁原来的应用,然后使用一个新的ClassLoader来加载改变之后的应用。而所有其他的应用程序不会受到一点干扰。
     
    热部署就是在服务器运行时重新部署项目,热加载即在在运行时重新加载class,从而升级应用。

    热加载的实现原理主要依赖java的类加载机制,在实现方式可以概括为在容器启动的时候起一条后台线程,定时的检测类文件的时间戳变化,如果类的时间戳变掉了,则将类重新载入。

    对比反射机制,反射是在运行时获取类信息,通过动态的调用来改变程序行为;
    热加载则是在运行时通过重新加载改变类信息,直接改变程序行为。

    热部署原理类似,但它是直接重新加载整个应用,这种方式会释放内存,比热加载更加干净彻底,但同时也更费时间。

     
     
    • 四种引用类型?应用场景?

    强引用、软引用、弱引用、虚引用;

        a、强引用(Strong Reference)--不会被回收

              程序代码中普遍存在的,比如Object obj = new Object(),只要存在强引用,GC收集器永远不会回收被引用的对象。

        b、软引用(Soft Reference) --内存紧张的时候被回收

              非必须的对象,是否回收,要看当前内存情况,如果紧张,则进行回收,否则不回收。当程序抛出内存溢出异常时,肯定不存在这种类型的对象。

        c、弱引用(Weak Reference) --下一次GC回收,即必被回收;

              被弱引用关联的对象只能生存到下一次GC发生之前,即每次必被回收。

        d、虚引用(Phantom Reference) --GC时回收,无法通过引用取得对象值;用来检测对象是否已经从内存删除。

              幽灵引用或者幻影引用,一个对象是否有虚引用的存在不会影响到其生存时间,无法通过虚引用获取对象实例。为一个对象设置虚引用关联的唯一目的是希望能在这个对象被回收时受到一个系统通知。

  • 相关阅读:
    asp.net mvc 自定义全局过滤器 验证用户是否登录
    jQuery怎么获取到富文本ueditor编辑器里面的文字和图片内容
    SqlServer分页操作
    不同数据库的分页查询
    js实现图片预览功能
    servlet编写验证码
    关于MVC模式的登录注册
    Request.UrlReferrer 使用
    关于通过Excel批量导入数据库的分析
    auto和100%的区别
  • 原文地址:https://www.cnblogs.com/buwenyuwu/p/7222624.html
Copyright © 2011-2022 走看看