八股文面试---jvm(简易版)

01-jvm内存结构-代码执行流程

八股文面试---jvm(简易版)_第1张图片
  1. java源代码对应java source

  1. javap将源代码编译成java class字节码(支持跨平台,可以被虚拟机解释为使用于各个平台的机器码)

  1. 接下来图中所剩下的部分都可以称为java虚拟机的一部分。使用java命令执行字节码后,就会创建出java虚拟机。

  1. 生成main主线程执行方法,此线程需要的内存由虚拟机分配(所有创建的线程所需的内存都是来自于虚拟机栈)。

  1. 主线程碰到一个没见过的类Main,把Main这个类通过类加载子系统把类的原始信息加载到方法区(把字节码文件读取到内存里来)。

  1. 接下来开始执行main方法,又碰到一个没见过的类Student,把Student的类信息加载到方法区。(student不是成员变量,而是方法局部变量,所以上面Main类加载的时候是没有加载到方法区的)。

  1. 遇到一个new,计算对象创建所需要的内存空间,创建对象然后放到堆中的空闲处。

  1. stu这个局部变量跟方法参数args,它们都是引用地址,引用对象的内存都是放在虚拟机栈中。

  1. 方法分为两类,一类是java可以实现的,比如上面的study方法。另外一种是需要调用操作系统底层函数实现的比如hashcode方法。java方法内存用的虚拟机栈,本地方法用的本地方法栈。

  1. main线程代码执行到一半,可能要交出cpu使用权,那下次再回来时怎么知道代码从哪里继续?这就需要用到程序计数器,记录当前代码执行到哪行代码。

  1. stu=null,没有对象引用的对象可以进行回收

  1. 机器并不认识字节码,这就需要通过解释器把机器码翻译为机器码

  1. 调用比较频繁的代码称为热点代码,就不适合用解释器解释,会通过即时编译器翻译为机器码并缓存。

02-jvm内存结构-哪些区域会有内存溢出

八股文面试---jvm(简易版)_第2张图片

03-jvm内存结构-方法区-元空间

八股文面试---jvm(简易版)_第3张图片

方法区是规范。永久代和元空间是不同版本的虚拟机对方法区的实现

八股文面试---jvm(简易版)_第4张图片

类的基本信息存储在方法区(类加载时放入),同时也会在堆内存创建出一个X.Class对象。我们访问类的原始信息不能直接访问,需要通过X.Class对象间接访问。

八股文面试---jvm(简易版)_第5张图片

什么时候方法区里类的信息内存会得到清理呢?如上图,虽然Y.Class对象和c对象都没有被引用了,但是方法区中类Y不能被卸载。只有整个类加载器不再使用了,那这个类加载器所加载的类才会被卸载。

八股文面试---jvm(简易版)_第6张图片

04-jvm内存参数

堆内存参数

八股文面试---jvm(简易版)_第7张图片

-Xmx 最大堆内存

-Xms 最小堆内存

-Xmn 新生代内存

-XX:SurvivorRatio=3 eden:from=3:1

from所占内存=to所占内存

Survivor内存=from内存+to内存=2G

八股文面试---jvm(简易版)_第8张图片
八股文面试---jvm(简易版)_第9张图片
八股文面试---jvm(简易版)_第10张图片
八股文面试---jvm(简易版)_第11张图片

05-jvm垃圾回收算法

八股文面试---jvm(简易版)_第12张图片

标记不能被垃圾回收的对象,垃圾回收发生时标记的对象把它保留下来,未标记的就清理掉。

1、标记清除

八股文面试---jvm(简易版)_第13张图片

GC Root:局部变量引用的对象、静态变量引用的对象

缺点:内存碎片

2、标记整理

无内存碎片,但是效率低

3、标记复制

八股文面试---jvm(简易版)_第14张图片

效率相对于标记整理更好。因为清除较快,直接把幸存对象复制到另一片区域,原区域作废。(适合新生代,因为新生代存活对象较少,复制的过程很快,而老年代存活的对象较多,全部复制的话效率较低,老年代更适合标记整理)

缺点:内存占用较高

06-jvm垃圾回收-概述

面试题:说说GC和分代回收算法

八股文面试---jvm(简易版)_第15张图片

07-jvm垃圾回收-分代回收

八股文面试---jvm(简易版)_第16张图片
八股文面试---jvm(简易版)_第17张图片

八股文面试---jvm(简易版)_第18张图片

八股文面试---jvm(简易版)_第19张图片

八股文面试---jvm(简易版)_第20张图片

08-jvm垃圾回收-三色标记

八股文面试---jvm(简易版)_第21张图片

黑色:沿着根对象已经找到了你这个对象并且这个对象的引用也在处理了。

灰色:沿着根对象已经找到了你这个对象,但是这个对象的引用还没有处理完成。

八股文面试---jvm(简易版)_第22张图片
八股文面试---jvm(简易版)_第23张图片
八股文面试---jvm(简易版)_第24张图片

09-jvm垃圾回收-并发漏标

八股文面试---jvm(简易版)_第25张图片

10-jvm垃圾回收-垃圾回收器

八股文面试---jvm(简易版)_第26张图片

新生代:标记复制,老年代:标记整理

八股文面试---jvm(简易版)_第27张图片

老年代垃圾回收器。并发失败(产生对象的速度>垃圾回收的速度),会进行full gc

11-jvm垃圾回收器-G1

八股文面试---jvm(简易版)_第28张图片
八股文面试---jvm(简易版)_第29张图片
八股文面试---jvm(简易版)_第30张图片

随着这些eden区放满,会触发新生代的垃圾回收。为啥不多创建几个eden?eden大小会受新生代大小控制的。而G1新生代的内存占比是在5%~6%之间波动,所以eden就不能随便创建新的了。

八股文面试---jvm(简易版)_第31张图片
八股文面试---jvm(简易版)_第32张图片
八股文面试---jvm(简易版)_第33张图片
八股文面试---jvm(简易版)_第34张图片

当老年代的内存超过一定阈值就会触发并发标记,默认是老年代内存达到了堆内存的45%以上。

八股文面试---jvm(简易版)_第35张图片

混合收集会根据暂停时间,优先对回收价值高的区域(存活对象少的区域)进行回收

八股文面试---jvm(简易版)_第36张图片

13-内存溢出-情况1-误用固定大小线程池

八股文面试---jvm(简易版)_第37张图片
八股文面试---jvm(简易版)_第38张图片
八股文面试---jvm(简易版)_第39张图片

解决方法:不要用自带的线程池,用自己定义的线程池,设置一个有

大小限制的任务队列。

14-内存溢出-情况2-误用带缓冲线程池

救急线程没有上限,线程数耗尽了系统的线程资源

八股文面试---jvm(简易版)_第40张图片
八股文面试---jvm(简易版)_第41张图片

15-内存溢出-情况3-一次查询太多数据

16-内存溢出-情况4-类太多

八股文面试---jvm(简易版)_第42张图片
八股文面试---jvm(简易版)_第43张图片

17-类加载-三个阶段

八股文面试---jvm(简易版)_第44张图片

18-类加载-验证类加载是懒惰的

八股文面试---jvm(简易版)_第45张图片
八股文面试---jvm(简易版)_第46张图片

(调用类的静态成员变量也会触发类的初始化)

19-类加载-验证类对象位于堆

20-类加载-验证类静态变量在初始化时赋值

八股文面试---jvm(简易版)_第47张图片

21-类加载-如何找到类对象地址

22-类加载-类初始化方法原理

八股文面试---jvm(简易版)_第48张图片
八股文面试---jvm(简易版)_第49张图片

这个方法在类的初始化的时候被调用

八股文面试---jvm(简易版)_第50张图片
八股文面试---jvm(简易版)_第51张图片
八股文面试---jvm(简易版)_第52张图片

23-类加载-final修饰基本类型变量的原理

八股文面试---jvm(简易版)_第53张图片

看TestFinal的字节码

八股文面试---jvm(简易版)_第54张图片

对于final修饰的基本数据类型,用到的类就会把这个值复制到自己的类中(即把这些值复制了一份放到了TestFinal类中)

(如果数字比较小直接放在方法区,如果数字比较大直接放到常量池中)

24-类加载-将符号引用变为直接引用

研究第二阶段---解析

八股文面试---jvm(简易版)_第55张图片

第一个read时三个类都还没被加载

八股文面试---jvm(简易版)_第56张图片

第二个read时

八股文面试---jvm(简易版)_第57张图片

25-类加载-双亲委派

八股文面试---jvm(简易版)_第58张图片

jdk8中类加载器

八股文面试---jvm(简易版)_第59张图片

比如要加载String.class这个类,先问Application ClassLoader有没有这个类,发现没有,然后问Extension ClassLoader,也没有,最后问Bootstrap ClassLoader,把String.class加载到内存中。上级加载的类,所有的下级也可见。

再比如加载自己写的Student.class类,虽然Application ClassLoader中有这个类,但是也得先问上级Extension ClassLoader----->Bootstrap ClassLoader,上级都没有自己才可以加载这个类,并且加载的这个类上级不可见。

26-类加载-能假冒一个System类吗

27-四种引用-概述

八股文面试---jvm(简易版)_第60张图片

28-四种引用-虚引用

八股文面试---jvm(简易版)_第61张图片
八股文面试---jvm(简易版)_第62张图片
八股文面试---jvm(简易版)_第63张图片

如果改成"b",则不会被垃圾回收,因为这样的话是在字符串常量池中的引用,这个引用是强引用。

29-四种引用-弱引用

ThreadLocalMap中Entry的key是弱引用,而key不是。如果垃圾回收会把key回收,value不会回收。如果使用不当会造成内存泄漏。

八股文面试---jvm(简易版)_第64张图片

八股文面试---jvm(简易版)_第65张图片
八股文面试---jvm(简易版)_第66张图片

jdk中没有使用这种方法,因为这样成本有点高。

30-四种引用-Cleaner

八股文面试---jvm(简易版)_第67张图片

为什么最后要System.in.read()?因为后台这个Cleaner-0这个线程是一个守护线程,如果主线程停止了,守护线程也会直接停止,可能来不及做清理操作。

31-finalize概述

八股文面试---jvm(简易版)_第68张图片

第一,从表面上我们能看出来finalize方法调用次序并不能保证(与垃圾回收的顺序有关,因为是按照入队的顺序来调用)

第二,日志中的Finalizer表示输出日志的线程名称,从这我们看出这个叫做Finalizer的线程调用的finalize方法

第三,你不能注释掉System.out.read(),否则会发现(绝大概率)并不会有任何输出结果了,从这我们看出finalize中的代码并不能保证被执行。

第四,如果将finalize中的代码出现异常,会发现根本没有异常输出

八股文面试---jvm(简易版)_第69张图片

第五,垃圾回收时不会立刻调用finalize方法,会先把对象加到referqueue,referqueue里有一个线程,起一个调一次。

32-finalize-unfinalized链表

八股文面试---jvm(简易版)_第70张图片

先调父类构造

把传入的狗对象包装成一个Finalizer对象,这个对象和虚引用弱引用对象类似,通过一个引用队列,就可以跟踪狗对象的垃圾回收过程。

八股文面试---jvm(简易版)_第71张图片
八股文面试---jvm(简易版)_第72张图片

unfinalized是一个双向链表,里面是Finalizer对象,这些Finalizer对象又真正引用了狗对象。作用是所有实现了finalize方法的对象都会放到这个链表中。

八股文面试---jvm(简易版)_第73张图片

33-finalize-调用原理

第一次垃圾回收时实现了finalize方法的对象无法被回收,只有执行完finalize方法后,第二次垃圾回收后该对象才会被回收。

八股文面试---jvm(简易版)_第74张图片
八股文面试---jvm(简易版)_第75张图片
八股文面试---jvm(简易版)_第76张图片
八股文面试---jvm(简易版)_第77张图片
八股文面试---jvm(简易版)_第78张图片

ps:finalize线程优先级并不低,普通线程优先级为5,它为8

八股文面试---jvm(简易版)_第79张图片
八股文面试---jvm(简易版)_第80张图片

(串行加锁一个个执行,非常慢)

你可能感兴趣的:(面试,职场和发展)