最基础的JVM知识点

JVM相关知识点

0、大纲介绍

介绍关于常见的JVM面试题目
Java源代码运行在JVM上面,为什么Java是跨平台语音,这个和JVM有很大的关系。
即Java的源代码被编译成字节码之后,运行在jvm这个平台之上,和所处的操作系统没有直接的关系。
所以才有一次编译,随意运行。Complie One, Run Away.
这边主要是介绍一下JVM的内存模型以及常见的垃圾回收算法和Java自带的垃圾回收器
还有实战之中较为常见的GC排查以及调参的一些知识点。

1、JVM内存模型

  1. JVM运行时,分为两个区域,一个是线程私有,一个是线程共享
  2. 线程私有分为三个部分组成,程序计数器、Java虚拟机栈、本地方法栈
    1. 程序计数器是唯一一个不会产生OOM的区域,通过改变计数器的值来选取下一条需要执行的字节码指令
    2. Java虚拟机栈则是为方法创建一个栈帧,用来存放局部变量表。
    3. 本地方法栈和Java虚拟机栈。虚拟机栈执行的是Java方法,本地方法栈执行的是native方法。
    4. Java虚拟机栈和本地方法栈都有可能产生OOM。
  3. 线程共享分为两个部分组成,方法区以及堆区。
    1. 堆区是JVM内存当中最大的一块区域,在虚拟机启动时候并创建,所有的对象实例都存放在堆区当中。
    2. 方法区里面存放的是一些常量、静态变量或者虚拟机加载的类信息,即编译器编译之后的代码等数据。
    3. 这两个区域也是会产生OOM问题。

2、怎么判定对象需要被回收

本质上,只要对象不再被引用,就可以认定需要被回收。
1、引用计数器
有被其他对象使用的时候则+1,当被赋予了新值或者超过生命周期,则-1.
无法避免循环引用的情况,这种对象则永远无法被回收
2、可达性分析
从GC ROOT开始查找,如果可以被遍历到,那么就是还存在使用,无法被遍历到,那么就需要被回收

3、垃圾回收算法

1、标记-复制算法
  1. 从GC ROOT开始扫描,标记处所有存活的对象,并将这些存活的对象复制到一块新的内存区域,然后将旧的内存回收掉。
  2. 这种回收算法适用于存活对象比较少的情况下比较高效,同时由于内存被进行拆分,所以可用的内存空间为原本的一半。同时需要复制移动对象。
  3. 适用于新生代,因为新生代里面的对象迭代比较频繁。
2、标记-清除算法
  1. 标记清楚算法总的分为两个步骤,一个是标记阶段,一个是清除阶段
  2. 从GC ROOT进行查找,将所有可达的对象进行打标。未被打标的对象就是要被回收的对象
  3. 保留被标记的对象,清除未被标记的对象。
  4. 这样子的问题就是容易产生碎片,比如ABC三个对象,B是被回收的,之后B区域如果不是太大,则无法分配新对象进行,就造成了浪费,同时也要进行两次空间扫描。
  5. 这个算法比较适合存活多的区域,适用于年老代。
3、标记-整理算法
  1. 标记整理算法也叫做标记压缩算法
  2. 从GC ROOT开始,对所有可达性对象进行一次标记
  3. 将被标记过的对象进行重整合并到一起,然后清空其他的不可达对象
  4. 这种方法避免了碎片的产生,同时也不需要向复制算法一样需要拆分内存区域。是标记清除算法的一种优化
4、分代算法
  1. 目前虚拟机使用回收算法就是分代算法。因为上述的三个算法各有自己的优缺点,所以需要根据不同的区域使用不同的垃圾回收算法。比如年轻代的对象活跃迭代快,适合标记复制算法。而年老代对象多存活周期较久,就适合标记清除或者标记整理算法

4、垃圾收集器

1、CMS收集器
  1. 初始标记,该阶段是单线程执行,标记可达的对象
  2. 并发标记,这时候GC线程和应用线程并发执行,通过初始标记进行查找这些对象可达的对象
  3. 重新标记,修正因为程序继续运行导致的部分对象的标记产生变动
  4. 并发清理,标记清除算法
2、G1收集器
  1. 前三个步骤和CMS是类似的
  2. CMS是并发清理,将不可达的对象都情况,这部分会有一定的耗时操作,G1的话在这部分可以根据用户设置的时间值,在这期间来指定回收计划。

5、频繁GC的原因以及审查

1、GC触发的条件
  1. GC的话,根据不同代分为Minor GC、Major GC 和 Full GC
  2. Minor GC,是由于新生代的Eden区满了,所以触发
  3. Major GC是年老代的空间满了才会触发的GC
  4. Full GC则是两个都满了之后才会触发的GC
2、如何减少GC的频率以及Full GC的次数
  1. 尽量不用分配过大的对象,使得对象无法被新生代存放而直接进入年老代,提前GC
  2. 尽量先让新生代写满,让对象在新生代多存活一段时间再进入年老代
  3. 不要手动调用System.gc(),要尽量让JVM自主调用GC
  4. 默认的JVM的新生代:年老代=1:2,同时新生代当中的Eden:SurTo:SurFrom=8:1:1
3、线上频繁GC排查
  1. 查看Full GC的时长,超过2S的话,则需要尽快排查了
  2. 将内存的使用情况进行dump下来,利用内置指令jmap即可。这个会导致Java进程中断
  3. 查看是哪个方法持有的内存最不合理
  4. 改动代码,然后本地测试看一下效果

6、类的加载过程

1、类加载器
根据指定的全限定名称将class文件加载到JVM当中,转为Class对象。
  1. 启动类加载器(Bootstrap ClassLoader):负责将存放在<JAVA_HOME>\lib目录或-Xbootclasspath参数指定的路径中的类库加载到内存中,这部分是由C++实现。
  2. 其他的类加载器,由Java实现,继承抽象类ClassLoader
    1. 扩展类加载器(Extension ClassLoader):负责加载<JAVA_HOME>\lib\ext目录或java.ext.dirs系统变量指定的路径中的所有类库。
    2. 应用程序类加载器(Application ClassLoader)。负责加载用户类路径(classpath)上的指定类库,我们可以直接使用这个类加载器。一般情况,如果我们没有自定义类加载器默认就是用这个加载器。
2、双亲委派机制
  1. 一个类加载器在收到类加载的请求时,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器完成。每个加载器都是如此,只有当父类加载器在自己的范围内找不到指定的类时,子类加载器才回去加载。
  2. 避免内存当中出现多个相同的字节码。
  3. 打破双亲委派机制,需要自己继承ClassLoader,同时重写loadClass和findClass方法即可。

7、引用的区别

1、强引用

通过New出来的对象,进行GC的时候,即可内存不够,也不回收。

2、弱引用

对象被重新赋值或者超过了生命周期,那么进行GC的时候,无论内存是否充足,这个对象都会被回收。

3、软引用

当内存足够时候,不会回收,内存不够的时候,则会回收。

4、虚引用

相当于没有引用,主要用来跟踪对象,在任何时候都可能被垃圾回收器回收

END

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值