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

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

  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内存结构-哪些区域会有内存溢出

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

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

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

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

04-jvm内存参数

堆内存参数

-Xmx 最大堆内存

-Xms 最小堆内存

-Xmn 新生代内存

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

from所占内存=to所占内存

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

05-jvm垃圾回收算法

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

1、标记清除

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

缺点:内存碎片

2、标记整理

无内存碎片,但是效率低

3、标记复制

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

缺点:内存占用较高

06-jvm垃圾回收-概述

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

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

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

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

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

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

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

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

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

11-jvm垃圾回收器-G1

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

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

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

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

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

大小限制的任务队列。

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

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

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

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

17-类加载-三个阶段

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

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

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

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

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

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

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

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

看TestFinal的字节码

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

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

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

研究第二阶段---解析

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

第二个read时

25-类加载-双亲委派

jdk8中类加载器

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

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

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

27-四种引用-概述

28-四种引用-虚引用

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

29-四种引用-弱引用

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

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

30-四种引用-Cleaner

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

31-finalize概述

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

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

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

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

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

32-finalize-unfinalized链表

先调父类构造

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

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

33-finalize-调用原理

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

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

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值