常见面试题(JVM、GC)

目录

1、JAVA运行原理

2、GC的分类

3、哪些情况会触发FULL GC?

4、JVM 垃圾回收机制,GC发生在JVM哪部分,有几种GC,他们的算法是什么

5、JVM调优

优化的步骤?

性能测试的指标?

JVM命令介绍?

JVM命令行的存哪些局限性?

JVM调优的工具有哪些?

JVM运行时参数有哪些?


1、JAVA运行原理

首先源文件(java)通过编译器编译成class文件,class文件是字节码文件,然后在通过JVM中的解释器将字节码文件生成对应的可执行文件,通过jvm交给linux或windows等系统。所以java即时编译语言也是解释性语言。
详细的解释  :首先通过编译器编译,将源程序编译形成class文件,由于不同平台JVM提供相同接口,故即便是不同平台下将java编译成class文件,但通过相同接口的JVM进行解释,均可将该环境下的字节码解释形成该平台下的可执行的java文件,同时,由于不同操作系统的JVM提供的均相同接口,不同平台的编译器则只需要面对该JVM接口进行编译,这些都决定了java语言具有良好的跨平台性、移植性。                                                                                                                                                                                                   jvm: java虚拟机 ,用于提供java解释环境,形成可执行文件;jre:java运行的环境;jdk:java核心包,提供java编译器,运行相关的环境和类库。

2、GC的分类

按照回收区域分为两种大类型:部分收集(Partial GC),一种是整堆收集(Full GC)

部分收集:不是完整收集整个Java堆的垃圾收集。其中又分为:

  • 新生代收集(Minor GC / Young GC):只是新生代(Eden\S0,S1)的垃圾收集
  • 老年代收集(Major GC/ Old GC):只是老年代的垃圾收集
  • 混合收集(Mixed GC):收集整个新生代及部分老年代的垃圾收集,目前只有G1 GC 有这种行为

整堆收集(Full GC):收集整个java堆和方法区的垃圾收集。

目前只有CMS GC 会有单独收集老年代的行为,注意很多时候 Major GC会和FullGC 混淆使用,需要具体分辨是老年代回收还是整堆回收。

3、哪些情况会触发FULL GC?

老年代空间不足;方法区空间不足;显示调用System.gc();Minor GC进入老年代的平均大小 大于老年代的可用内存;大对象直接进入老年代,而老年代的可用空间不足;

4、JVM 垃圾回收机制,GC发生在JVM哪部分,有几种GC,他们的算法是什么

JVM垃圾回收是发生在heap(堆)中

GC是什么(分代收集算法)

      次数上频繁收集Young区,Minor GC;次数上较少收集Old区 Full GC;

GC的算法主要有:

①引用计数法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时刻计数器为0的对象就是不可能再被使用的。

引用计数法的问题:引用和去引用伴随加法和减法,影响性能。  主流的java虚拟机并没有选用引用计数法来管理内存,就是因为它很难解决对象之间循环引用的问题。

②标记清除:标记清除算法是现代垃圾回收算法的思想基础,分两个阶段:标记阶段和清除阶段。在标记阶段,首先通过根节点,标记所有从根节点开始的可达对象。因此,未被标记的对象就是未被引用的垃圾对象;然后,在清除阶段,清除所有未被标记的对象。它的做法是当堆中的有效内存空间(available memory)被耗尽的时候,就会停止整个程序(也被成为stop the world),然后进行两项工作,第一项则是标记,第二项则是清除。

标记清除算法的缺点:首先,它的缺点就是效率比较低(递归与全堆对象遍历),导致停止程序的时间比较长;其次,则是这种方式清理出来的空闲内存是不连续的,这点不难理解,我们的死亡对象都是随即的出现在内存的各个角落的,现在把它们清除之后,内存的布局自然会乱七八糟。而为了应付这一点,JVM就不得不维持一个内存的空闲列表,这又是一种开销。而且在分配数组对象的时候,寻找连续的内存空间会不太好找。

通俗来讲,就是会产生内存碎片,扫描两次,第一次标记,第二次清除

③标记压缩:标记-压缩算法适合用于存活对象较多的场合,如老年代。它在标记-清除算法的基础上做了一些优化。和标记-清除算法一样,标记-压缩算法也首先需要从根节点开始,对所有可达对象做一次标记;但之后,它并不简单的清理未标记的对象,而是将所有的存活对象压缩到内存的一端;之后,清理边界外所有的空间。标记:它的第一个阶段与标记/清除算法是一模一样的,均是遍历GC Roots,然后将存活的对象标记;整理:移动所有存活的对象,且按照内存地址次序依次排列,然后将末端内存地址以后的内存全部回收。因此,第二阶段才称为整理阶段。

标记压缩优点:标记/整理算法不仅可以弥补标记/清除算法当中,内存区域分散的缺点,也消除了复制算法当中,内存减半的高额代价。

标记压缩缺点:效率也不高,不仅要标记所有存活对象,还要整理所有存活对象的引用地址。从效率上来说,标记/整理算法要低于复制算法。

④复制算法:将原有的内存空间分为两块,每次只使用其中一块,在垃圾回收时,将正在使用的内存中的存活对象复制到未使用的内存块中,之后,清除正在使用的内存块中的所有对象,交换两个内存的角色,完成垃圾回收。

       复制算法缺点:空间的浪费,复制算法使得每次都只对整个半区进行内存回收,内存分配时也就不用考虑内存碎片等复杂情况,只要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效。只是这种算法的代价是将内存缩小为原来的一半,这个太要命了。所以从以上描述不难看出,复制算法要想使用,最起码对象的存活率要非常低才行,而且最重要的是,我们必须要克服50%内存的浪费。

    年轻代就使用的复制算法,回收较为频繁;老年代就使用的标记清除或者标记清除与标记压缩的混合实现。

    标记-清除-压缩,减少移动成本

详细参考资料:https://www.cnblogs.com/chinaifae/articles/10329308.html

5、JVM调优

为什么要调优?

防止出现oom(内存溢出);解决oom;减少Full GC出现的频率

监控的依据?

运行日志;异常堆栈;GC日志;线程快照;堆转储快照

调优的大方向?

合理编写代码;充分合理的使用硬件资源;合理的进行JVM调优

优化的步骤?

  • 性能监控(发现问题):GC频繁;cpu load过高;oom;内存泄露;死锁;程序相应时间过长
  • 性能分析(排查问题):打印GC日志,通过GCviewer来分析日志;使用工具jstack,jmap,jinfo等;dump堆导出文件,使用内存分析工具分析文件;使用阿里的Arthas、jconsole、JVisualVM来实时查看JVM的状态;jstack查看堆栈信息
  • 性能调优(解决问题):适当的增加内存,根据业务背景选择垃圾回收器;优化代码,控制内存使用;增加机器,分散节点压力;合理设置线程池线程数量;使用中间件提高效率,比如缓存,消息队列

性能测试的指标?

  •  相应时间:提交请求和返回请求的响应之间使用的时间,一般关注平均响应时间,  可能平时的一个查询操作,登录时长等;在垃圾回收环节,暂停时间,就是指执行垃圾回收时,程序的工作线程被暂停的时间
  • 吞吐量:单位时间内完成的工作量;在GC中 ,运行用户代码时间占总运行时间的比例(总运行时间:程序的运行时间+内存回收的时间)吞吐量为1-1/(1+n)。 --XX:GCTimeRatio=n
  • 并发数:同一时刻,对服务器有实际交互的请求书
  • 内存占用:java堆区所占内存的大小

JVM命令介绍?

  • jps(java process status):查看当前运行的java进程,可以查看到线程的ID、入口类、启动参数等;对于本地虚拟机来说,进程的本地虚拟机id与操作系统的进程id是一致的,是唯一的。
  • jstat(JVM Statistics Monitoring Tool):查看指定虚拟机进程的各种运行状态信息,包含:类装载、垃圾收集、运行期编译状况等信息
  • jinfo(Configuration Info for Java):查看虚拟机配置参数信息,也可用于调整虚拟机的配置参数 ,比如:System.getProperties()的参数,并且可以修改其值
  • jmap(JVM Memory Map):生成堆内存的dump日志
  • jstack(JVM Stack Trace):查看指定虚拟机各个线程的运行状态
  • jhat(JVM Heap Analysis Tool):针对dump文件,暴露一个http服务,供外部可以查看分析

JVM命令行的存哪些局限性?

1、无法获取方法级别的分析数据,如方法间的调用关系、各方法的调用次数和调用时间等

2、要求用户登录到目标Java 应用所在的主机上,使用起来不方便

3、分析数据通过终端输出,展示结果不够直观

JVM调优的工具有哪些?

JDK自带的工具

  • jconsole:查看Java应用程序的运行概况、监控堆信息、永久区使用情况、类加载情况等。  位置为:jdk\bin\jconsole.exe
  • Visual VM:查看Java虚拟机上运行的基于Java技术的应用程序的详细信息。 位置为:jdk\bin\jvisualvm.exe     
  • JMC:Java Mission Control,内置Java Flight Recorder 。能够极低的性能开销收集JAVA虚拟机的性能数据

第三方的工具

  • MAT(Memory  Analyer Tool) 是基于Eclipse 的内存分析工具,是一个快速、功能丰富的Java heap 分析工具,它可以帮助我们查找内存泄露和减少内存消耗。(Eclipse插件形式)
  • JProfiler : 商业软件,需要付费,功能强大。(与Visual VM 类似)
  • Arthas:Alibaba 开源的诊断工具,深受开发者喜爱
  • Btrace:Java运行时追踪工具。可以在不停机的情况下,跟踪指定的方法调用,构造函数调用和系统内存等信息

JVM运行时参数有哪些?

-Xms<size>        设置初始 Java 堆大小,等价于 -XX:InitialHeapSize
-Xmx<size>        设置最大 Java 堆大小,等价于-XX:MaxHeapSize
-Xss<size>        设置 Java 线程堆栈大小,等价于 -XX:ThreadStackSize

参数名称

含义

默认值

备注

-Xss

 

每个线程的堆栈大小

 

 

JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K.更具应用的线程所需内存大小进行 调整.在相同物理内存下,减小这个值能生成更多的线程.但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右

一般小的应用, 如果栈不是很深, 应该是128k够用的 大的应用建议使用256k。这个选项对性能影响比较大,需要严格的测试。(校长)

和threadstacksize选项解释很类似,官方文档似乎没有解释,在论坛中有这样一句话:"”

-Xss is translated in a VM flag named ThreadStackSize”

一般设置这个值就可以了。

等价于  -XX:ThreadStackSize

-Xms

设置JVM初始堆大小

物理内存的1/64(<1GB)

等价于 -XX:InitialHeapSize。默认(MinHeapFreeRatio参数可以调整)空余堆内存小于40%时,JVM就会增大堆直到-Xmx的最大限制.

-Xmx

设置最大 Java 堆大小

物理内存的1/4(<1GB)

等价于-XX:MaxHeapSize。默认(MaxHeapFreeRatio参数可以调整)空余堆内存大于70%时,JVM会减少堆直到 -Xms的最小限制

-Xmn

设置年轻代大小

 

官方推荐配置为整个堆大小的3/8

整个堆大小=年轻代大小 + 年老代大小 + 持久代大小;

增大年轻代后,将会减小年老代大小.此值对系统性能影响较大,

-XX:NewSize=1024m

设置年轻代初始值为1024M

 

 

-XX:MaxNewSize=1024m

设置年轻代最大值为1024M

 

 

-XX:ThreadStackSize

Thread Stack Size

 

(0 means use default stack size) [Sparc: 512; Solaris x86: 320 (was 256 prior in 5.0 and earlier); Sparc 64 bit: 1024; Linux amd64: 1024 (was 0 in 5.0 and earlier); all others 0.]

-XX:NewRatio

年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)

 

-XX:NewRatio=4表示年轻代与年老代所占比值为1:4,年轻代占整个堆栈的1/5

Xms=Xmx并且设置了Xmn的情况下,该参数不需要进行设置。

-XX:SurvivorRatio

Eden区与Survivor区的大小比值

 

设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10

-XX:LargePageSizeInBytes

内存页的大小不可设置过大, 会影响Perm的大小

 

=128m

-XX:+UseFastAccessorMethods

原始类型的快速优化

 

 

-XX:+DisableExplicitGC

关闭System.gc()

 

这个参数需要严格的测试

-XX:MaxTenuringThreshold

垃圾最大年龄

 

如果设置为0的话,则年轻代对象不经过Survivor区,直接进入年老代. 对于年老代比较多的应用,可以提高效率.如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活 时间,增加在年轻代即被回收的概率

该参数只有在串行GC时才有效.

-XX:+AggressiveOpts

加快编译

 

 

-XX:+UseBiasedLocking

锁机制的性能改善

 

 

-Xnoclassgc

禁用垃圾回收

 

 

-XX:SoftRefLRUPolicyMSPerMB

每兆堆空闲空间中SoftReference的存活时间

1s

softly reachable objects will remain alive for some amount of time after the last time they were referenced. The default value is one second of lifetime per free megabyte in the heap

-XX:PretenureSizeThreshold=1024

对象超过多大是直接在老年代分配

0

单位字节 新生代采用Parallel Scavenge GC时无效

另一种直接在旧生代分配的情况是大的数组对象,且数组中无外部引用对象.

-XX:TLABWasteTargetPercent

TLAB占eden区的百分比

1%

 

-XX:+CollectGen0First

FullGC时是否先YGC

false

 

-XX:+PrintGC

开启打印 gc 信息

 

 

-XX:+PrintGCDetails

打印 gc 详细信息

 

 

-XX:+PrintFlagsFinal

输出所有参数的名称和默认值

 

默认不包括Diagnostic和Experimental的参数

 

 

 

 


 

参与评论 您还未登录,请先 登录 后发表或查看评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:编程工作室 设计师:CSDN官方博客 返回首页

打赏作者

过_往

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值