NickDwade

 

2021最新面试题-jvm篇

1、jvm是什么?

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

2、JVM内存区域分类有哪些?

  JVM 内存区域主要分为 线程私有区域【程序计数器、虚拟机栈、本地方法区】、线程共享区 域【JAVA 堆、方法区】、直接内存。

3、堆和栈的区别是什么?

  完全是两个不同的东西

  栈:

    是描述java方法执行的内存模型,每个方法在执行的同时都会创建一个栈帧(Stack Frame) 用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每一个方法从调用直至执行完成 的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程。

    栈帧( Frame)是用来存储数据和部分过程结果的数据结构,同时也被用来处理动态链接 (Dynamic Linking)、 方法返回值和异常分派( Dispatch Exception)。栈帧随着方法调用而创建,随着方法结束而销毁——无论方法是正常完成还是异常完成(抛出了在方法内未被捕获的异 常)都算作方法结束。

  堆:
    是被线程共享的一块内存区域,创建的对象和数组都保存在 Java 堆内存中,也是垃圾收集器进行 垃圾收集的最重要的内存区域。由于现代 VM 采用分代收集算法, 因此 Java 堆从 GC 的角度还可以 细分为: 新生代(Eden 区、From Survivor 区和 To Survivor 区)和老年代。

4、JVM那块内存区域不会发生内存溢出?

  程序计数器不会发生,因为程序计数器中存储的数据所占用空间的大小不会随程序的执行而发生改变。

5、什么情况下会发生栈内存溢出?

  如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError 异常。

6、对象都是在堆上分配的吗?

  不一定。满足特定条件时,它们可以在(虚拟机)栈上分配内存。

  因为JVM通过逃逸分析,能够分析出一个新对象的使用范围,并以此确定是否要将这个对象分配到堆上。如果JVM发现某些对象没有逃逸出方法,就很有可能被优化成在栈上分配。

  逃逸分析:

    一种确定指针动态范围的静态分析,它可以分析在程序的哪些地方可以访问到指针。

    在JVM的即时编译语境下,逃逸分析将判断新建的对象是否逃逸。即时编译判断对象是否逃逸的依据:一种是对象是否被存入堆中(静态字段或者堆中对象的实例字段),另一种就是对象是否被传入未知代码。

7、常用的JVM参数有哪些?

  -XX:+PrintGCDetails 打印垃圾回收信息

  -Xms 为Heap区域的初始值,线上环境需要与-Xmx设置为一致,否则capacity的值会来回飘动

  -Xmx 为Heap区域的最大值

  -Xss(或-ss) 线程栈大小(指一个线程的native空间)1.5以后是1M的默认大小

  -XX:PermSize与-XX:MaxPermSize 方法区(永久代)的初始大小和最大值(但不是本地方法区)

  -XX:NewRatio 老年代与新生代比率

  -XX:SurvivorRatio Eden与Survivor的占用比例。例如8表示,一个survivor区占用 1/8 的Eden内存,即1/10的新生代内存,为什么不是1/9?因为我们的新生代有2个survivor,即S1和S22。所以survivor总共是占用新生代内存的 2/10,Eden与新生代的占比则为 8/10。

  -XX:MaxHeapFreeRatio GC后,如果发现空闲堆内存占到整个预估的比例小于这个值,则减小堆空间。

  -XX:MinHeapFreeRatio GC后,如果发现空闲堆内存占到整个预估的比例大于这个值,则增大堆空间。

  -XX:NewSize 新生代大小

8、什么是类加载器?

  Java类加载器是Java运行时环境的一部分,负责动态加载Java类到Java虚拟机的内存空间中。

  类通常是按需加载,即第一次使用该类时才加载。由于有了类加载器,Java运行时系统不需要知道文件与文件系统。

9、类加载器的分类及作用?

  启动类加载器(Bootstrap ClassLoader):

    负责加载 JAVA_HOME\lib 目录中的,或通过-Xbootclasspath 参数指定路径中的,且被 虚拟机认可(按文件名识别,如 rt.jar)的类。

  扩展类加载器(Extension ClassLoader):

    负责加载 JAVA_HOME\lib\ext 目录中的,或通过 java.ext.dirs 系统变量指定路径中的类 库。

  应用程序类加载器(Application ClassLoader):

    负责加载用户路径(classpath)上的类库。

  JVM 通过双亲委派模型进行类的加载,当然我们也可以通过继承 java.lang.ClassLoader 实现自定义的类加载器。

 

 

   

 

 

 10、什么是双亲委派模型?

  当一个类收到了类加载请求,他首先不会尝试自己去加载这个类,而是把这个请求委派给父 类去完成,每一个层次类加载器都是如此,因此所有的加载请求都应该传送到启动类加载其中, 只有当父类加载器反馈自己无法完成这个请求的时候(在它的加载路径下没有找到所需加载的 Class),子类加载器才会尝试自己去加载。 采用双亲委派的一个好处是比如加载位于 rt.jar 包中的类 java.lang.Object,不管是哪个加载 器加载这个类,最终都是委托给顶层的启动类加载器进行加载,这样就保证了使用不同的类加载 器最终得到的都是同样一个 Object 对象。

 

 

 11、为什么要打破双亲委派模型?

  因为类加载器受到加载范围的限制,在某些情况下父类加载器无法加载到需要的文件,这时候就需要委托子类加载器去加载class文件。

12、可以自定义一个java.lang.String吗?

  不可以。因为根据双亲委派最终会由最顶层的根加载器来执行jdk自带的java.lang.String

13、什么是JVM内存模型?

  分为虚拟机栈,堆,方法区,程序计数器,本地方法栈

14、JVM内存模型和JVM内存结构有什么区别?

  JVM内存结构:

    常说的 JVM 内存结构指的就是上文提交到运行时数据区,其中方法区被线程共享,程序计数器运行时常量池被线程独享。

    它描述的是,在运行时,字节码和代码数据存储的位置。

15、什么是指令重排序?

  假设我们写了一个 Java 程序,包含一系列的语句,我们会默认期望这些语句的实际运行顺序和写的代码顺序一致。

  但实际上,编译器、JVM 或者 CPU 都有可能出于优化等目的,对于实际指令执行的顺序进行调整,这就是重排序。

16、内存屏障是什么?

  它是一条CPU指令: a)确保一些特定操作执行的顺序; b)影响一些数据的可见性(可能是某些指令执行后的结果)。

  为了保证内存的可见性,java编译器在生成指令序列的适当位置插入一个内存屏障来禁止特定类型的处理器重排序,相当于告诉CPU和编译器先于这个命令的必须先执行,后于这个命令的必须后执行。

17、什么是Happens-Before原则?

  程序次序规则:在一个线程内,按照程序代码顺序,书写在前面的操作先行发生于书写在后面的操作。(这里涉及到 CPU 指令重排,所以需要加入内存屏障保证有序性)

  管程锁定规则:对一个锁的解锁操作,先行发生于后续对这个锁的加锁操作。这里必须强调的是同一个锁。

  volatile 变量规则:对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作。

  线程启动规则:Thread 对象的 start() 方法先行发生于此线程的每一个动作。

  线程 join() 规则:被调用 join() 方法的线程的所有操作先行发生于 join() 的返回。

  传递性规则:操作 a 先发生于操作 b,操作 b 先发生于操作 c,则操作 a 先发生于操作 c。

  对象终结规则:一个对象的初始化完成(构造函数执行结束)先行发生于它的 finalize() 方法。

18、GC是什么?为什么需要GC?

  JAVA的垃圾回收器

  Java有了GC,就不需要程序员去人工释放内存空间。当Java虚拟机发觉内存资源紧张的时候,就会自动地去清理无用变量所占用的内存空间。

19、什么是Minor GC和FullGC?

   Major GC 是清理永久代。Full GC 是清理整个堆空间—包括年轻代和永久代。

20、一次完整的GC流程是怎样的?

  对象优先在新生代区中分配,若没有足够空间,Minor GC;

  大对象(需要大量连续内存空间)直接进入老年态;长期存活的对象进入老年态。如果对象在新生代出生并经过第一次MGC后仍然存活,年龄+1,若年龄超过一定限制(15),则被晋升到老年态。

21、常用的垃圾收集器有哪些?

  Serial 垃圾收集器(单线程、复制算法)

  ParNew 垃圾收集器(Serial+多线程)

  Parallel Scavenge 收集器(多线程复制算法、高效)

  Serial Old 收集器(单线程标记整理算法 )

  Parallel Old 收集器(多线程标记整理算法)

  CMS 收集器(多线程标记清除算法)

  G1 收集器

22、你知道哪几种垃圾收集器,各自的优缺点,重点讲下cms和G1,包括原理,流程,优缺点。

  CMS 收集器(多线程标记清除算法)

    Concurrent mark sweep(CMS)收集器是一种年老代垃圾收集器,其最主要目标是获取最短垃圾 回收停顿时间,和其他年老代使用标记-整理算法不同,它使用多线程的标记-清除算法。 最短的垃圾收集停顿时间可以为交互比较高的程序提高用户体验。 CMS 工作机制相比其他的垃圾收集器来说更复杂,整个过程分为以下 4 个阶段:

    初始标记 只是标记一下 GC Roots 能直接关联的对象,速度很快,仍然需要暂停所有的工作线程。

    并发标记 进行 GC Roots 跟踪的过程,和用户线程一起工作,不需要暂停工作线程。

    重新标记 为了修正在并发标记期间,因用户程序继续运行而导致标记产生变动的那一部分对象的标记 记录,仍然需要暂停所有的工作线程。

    并发清除 清除 GC Roots 不可达对象,和用户线程一起工作,不需要暂停工作线程。由于耗时最长的并 发标记和并发清除过程中,垃圾收集线程可以和用户现在一起并发工作,所以总体上来看 CMS 收集器的内存回收和用户线程是一起并发地执行。

CMS 收集器工作过程:

 

  G1 收集器

    Garbage first 垃圾收集器是目前垃圾收集器理论发展的最前沿成果,相比与 CMS 收集器,G1 收 集器两个最突出的改进是:

      1、基于标记-整理算法,不产生内存碎片。

      2、可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。

      3、G1 收集器避免全区域垃圾收集,它把堆内存划分为大小固定的几个独立区域,并且跟踪这些区域 的垃圾收集进度,同时在后台维护一个优先级列表,每次根据所允许的收集时间,优先回收垃圾 最多的区域。区域划分和优先级区域回收机制,确保 G1 收集器可以在有限时间获得最高的垃圾收 集效率。

23、什么是内存泄漏?

  被分配对象可达但无用

24、为什么会发生内存泄漏?

  a)创建和应用生命周期一样的单例对象

  b)创建匿名内部类的静态对象

  c)未关闭资源

  d)长时间存在的集合容器中创建生命周期短的对象

25、如何防止内存泄漏?

  a)尽早释放无用内存

  b)处理字符串尽可能使用StringBuffer,因为每创建一个String占一个独立内存

  c)少用静态变量(JDK1.8不存在方法区,不用考虑)

  d)避免循环中创建对象

26、常用的JVM问题定位工具有哪些?

  

工具名称应用场景说明备注
jps 查看系统的java进程 显示指定系统内所有HotSpot虚拟机进程。参数:-l:显示main方法的名称-v:显示虚拟机启动时的JVM参数(这个好用)-m:输出传递给main方法的参数 jps并不能显示所有的java进程。这个原因暂时未知,比如对于自定义启动脚本的进程支持不大好
jstat 从参数中也可以看到对域gc的监控比较充分。可以通过它来获取gc相关的信息 监控虚拟机的各种状态信息。参数如下:-class:统计class loader行为信息 -compile:统计编译行为信息 -gc:统计jdk gc时heap信息 -gccapacity:统计不同的generations(不知道怎么翻译好,包括新生区,老年区,permanent区)相应的heap容量情况 -gccause:统计gc的情况,(同-gcutil)和引起gc的事件 -gcnew:统计gc时,新生代的情况 -gcnewcapacity:统计gc时,新生代heap容量 -gcold:统计gc时,老年区的情况 -gcoldcapacity:统计gc时,老年区heap容量 -gcpermcapacity:统计gc时,permanent区heap容量 -gcutil:统计gc时,heap情况 使用过程中也发现和jps同样的问题。就是通过ps aux|grep java查看的进程,使用jstat执行时报:not found。好处是命令行显示。可以在远程服务器上执行。而有类似功能的如visual vm是图形界面,需要本地远程连接。
jinfo 使用jps可以查看启动时的参数列表,但是如果想看未指定参数的默认值,就需要jinfo了 通过 -flag 参数 可以打印使指定的jvm flag参数值。通过 -sysprops 可以打印出系统参数通过 -flag name=value 可以修改一部分可以在运行时改变的虚拟机参数值 在测试环境比较常用,比如想知道当前jvm进程的启动参数和系统属性时用到。第三种方法个人用的比较少,没有在生成环境上用过,比较谨慎。
jmap 一般用于内存溢出或者full fc很频繁时,需要定位jvm 的内存使用情况 生成堆转存信息(也就是heapdump)。jmap的作用不仅仅是获取dump文件,还可以查询finalize执行队列(什么东东),java堆和永久代信息,如空间使用率(貌似和jinfo重合了)和使用哪种gc。参数:-dump:[live,]format=b,file=<filename> 使用hprof二进制形式,输出jvm的heap内容到文件=. live子选项是可选的,假如指定live选项,那么只输出活的对象到文件. -heap 打印heap的概要信息,GC使用的算法,heap的配置及wise heap的使用情况. -histo[:live] 打印每个class的实例数目,内存占用,类全名信息. VM的内部类名字开头会加上前缀”*”. 如果live子参数加上后,只统计活的对象数量. -permstat 打印classload和jvm heap长久层的信息. 包含每个classloader的名字,活泼性,地址,父classloader和加载的class数量. 另外,内部String的数量和占用内存数也会打印出来. -F 强迫.在pid没有相应的时候使用-dump或者-histo参数. 在这个模式下,live子参数无效. 其他可以生成dump的方式:1、配置-XX:HeapDumpOnOutOfMemoryError在内存溢出时自动生成dump2、执行Kill -3
jhat   分析dump文件。不过一般不使用,因为功能简陋以及实际使用中并不会在服务器进行分析  
jstack 用于定位线程异常增多,死锁或者死循环导致cpu使用率暴增的情况 用于生成虚拟机当前时刻的线程快照。也就是theaddump。通过threadump可以看到各个线程的调用栈,就知道没有响应的线程在后台做什么。参数:-F:正常请求无法响应时强制输出线程栈-l:除堆栈外,显示关于锁的附加信息 jmap和jstack都会到时阻塞,所以需要摘线上流量才能执行。也可以代买中通过Thread类的getAllStackTraces方法可以获取虚拟机中的所有StackTraceElement对象,从而获取线程栈信息
visual vm   多合一工具。几乎整合了上述的所有工具,提供了完整的图形化分析功能。比如可以将服务器上dump文件,在visual vm中打开进行分析。

27、你们线上应用的JVM参数有哪些。

  -server

  -Xms6000M

  -Xmx6000M

  -Xmn500M

  -XX:PermSize=500M

  -XX:MaxPermSize=500M

  -XX:SurvivorRatio=65536

  -XX:MaxTenuringThreshold=0

  -Xnoclassgc

  -XX:+DisableExplicitGC

  -XX:+UseParNewGC

  -XX:+UseConcMarkSweepGC

  -XX:+UseCMSCompactAtFullCollection

  -XX:CMSFullGCsBeforeCompaction=0

  -XX:+CMSClassUnloadingEnabled

  -XX:-CMSParallelRemarkEnabled

  -XX:CMSInitiatingOccupancyFraction=90

  -XX:SoftRefLRUPolicyMSPerMB=0

  -XX:+PrintClassHistogram

  -XX:+PrintGCDetails

  -XX:+PrintGCTimeStamps

  -XX:+PrintHeapAtGC

  -Xloggc:log/gc.log

28、g1和cms区别,吞吐量优先和响应优先的垃圾收集器选择。

  CMS收集器:一款以获取最短回收停顿时间为目标的收集器,是基于“标记-清除”算法实现的,分为4个步骤:初始标记、并发标记、重新标记、并发清除。

  G1收集器:面向服务端应用的垃圾收集器,过程:初始标记;并发标记;最终标记;筛选回收。整体上看是“标记-整理”,局部看是“复制”,不会产生内存碎片。

  吞吐量优先的并行收集器:以到达一定的吞吐量为目标,适用于科学技术和后台处理等。

  响应时间优先的并发收集器:保证系统的响应时间,减少垃圾收集时的停顿时间。适用于应用服务器、电信领域等。

 

posted on 2021-03-02 20:36  NickDwade  阅读(167)  评论(0)    收藏  举报

导航