JVM面试题

虚拟机(JVM)

JVM面试题

JVM面试题

JVM面试题

工作原理

一个java文件通过编译成字节码(也就是类文件),通过类装载子系统到内存区中执行(运行时数据区),内存区都是通过字节码引擎执行的。其中内存区主要由堆,栈,本地方法栈,方法区,程序计数器。其中呀,堆跟方法区是共有数据,其他都是私有数据。

程序计数器的作用可以看做是当前线程所执行的字节码的行号指示器。

先说栈,栈也可以理解为是一个线程栈,它是主管java程序的运行,我们这个线程中的一些变量都是在栈内存中分配的。栈的内存结构是先进后出,在这个栈中,执行一个方法的同时都会创建一个栈帧。每个栈帧用于存储局部变量表,操作数栈,动态链接,方法出口等信息。局部变量表就是存放了编译期可知的各种基本数据类型,对象引用等等,操作数栈就是存放计算机一些临时的操作数据。(从局部变量表或对象实例的字段中复制常量或变量写入到操作数栈,再随着计算的进行将栈中元素出栈到局部变量表或者返回给方法调用者,也就是出栈/入栈操作。)

方法出口就是根据当前字节码指令,返回到方法被调用的位置,方法返回时可能需要在栈帧中保存一些信息。如果异常退出,则不会保留信息。

堆主要用于存放各种类的实例对象对应的内存地址。

方法区存储加载进来的每一个类的结构信息,可以看做是将类(Class)的模板信息,保存在方法区里

本地方法栈与栈原理差不多,栈也就是java栈,为执行java方法服务,本地方法栈是为执行本地方法服务的。对本地实现方法及数据结构没有约束。如果说这个方法是用native(内t无)修饰的,他就是本地方法栈。

垃圾回收机制

JVM面试题

JVM面试题

堆中分为年轻代(1/3)和老年代(2/3),年轻代中有包含有eden(伊甸园),survivor(读音:色外喔,翻译:幸存区)。

一般新的对象都会存储到eden中,当eden中满的话,会触发Minor GC(卖讷)。

当eden满的话,如何查看那个是垃圾,那些不是?

通过使用可达性分析算法,将“gc roots”作为对象,向下搜索,找到的对象都是非垃圾对象。都会通过复制算法复制到survivor 0区中,然后将eden中的全部销毁。

当survivor 0区也存满时候,将eden和servivor 0区中存活的对象存放到servivor 1区中,然后清空eden和survivor0区清除,此时survivor0 区是空的,然后将survivor 0区和survivor 1区进行交换,一直保持survivor 1区为空。

当survivo 1区不能存放eden和survivor 0中的存活,就将存活对象存到老年代。要是老年代也满的话就会触发full gc,也就是年轻代和老年代都进行回收。

为什么要对jvm进行优化呢,就是当新生代和老年代都满的话,会触发full gc 垃圾回收,full gc 进行垃圾回收时,会对程序进行stw(stop the world-关闭),这样就会导致程序停止,日志不输出,并且占用需要系统资源(比如,cpu),影响系统吞吐量。

内存调优

调优的目的就是为了减少gc的频率和full gc的次数,因为full gc运行时,会产生stw(stop the world),会是占用较多的系统资源(cpu),影响程序正常运行。调优说明白点就是调整堆中内存占用比例。我们可以使用jdk提供的内存查看工具,如:jconsole或者java visualVM

监控gc状态,查看日志,根据当前堆内存快照和gc日子判断是否进行优化。

如果日志正常,则不需要优化,如果频繁使用full gc 则要进行调优,调优就是调整其内存分配大小比例。

full gc 频繁使用的原因

  1. 年轻代设置过小 由于年轻代设置过小,造成频繁使用minor gc,让系统一直处于垃圾回收中无法运行其他程序,再者就是年轻代设置过小会使一个大对象直接进入老年代,就是有些对象当占有survivor 0区的50%以上,会直接进入老年代,占用老年代内存,从而频繁调用full gc
  2. 年轻代设置过大 会导致老年代设置过小,从而频繁使用full gc ,再者年轻代gc消耗能力增加。
  3. survivor设置过小 导致一些对象直接进入老年代
  4. sruvivor设置过大 导致eden设置过小,频繁使用minor gc