JVM 垃圾回收相关


JVM 垃圾回收相关

1.如何判断对象是否可回收?

1.引用计数法:

存在循环引用

2.可达性分析(GC ROOTS):

2.常见垃圾回收算法?3种

1.标记清除

标记清除:标记存活的对象,清除已经死亡的对象;
缺点:会有内存泄漏

2.复制算法:

复制算法:将内存空间分为两部分,每次只使用其中一部分,将存活的对象复制到另外内存空间,清除当前空间
缺点:每次都有内存的浪费,适合年轻代

3.标记整理:

标记整理:标记存活的对象,清除已经死亡的对象,将存活的对象重新整理在内存最开头或最末尾
缺点:多了整理步骤,相比标记清楚稍微更耗时,但解决了内存泄漏与浪费的问题。

3.常见垃圾回收器?

在这里插入图片描述

垃圾回收期搭配 4组
1.serial 与serial old
2.parNew 与cms(清除算法)
3.parallel Scanvenge 与 parallel old
G1

分类方式:

1.单线程/多线程 分类 3对+1

是否支持多线程分类:
1.1.单线程:Serial 与Serial Old 共1组:单核Cpu情况下,性能比较好
1.2.多线程:ParNew 与CMS(工作老年代,标记清除算法),Parellel Sacavenge 与Parrellel Old; G1 共3组;
G1局部采用的是复制算法,大部分还是采用的是标记整理算法

2.年轻代/老年代 分类

1.年轻代:Serial、ParNew、Parellel Scavenge、G1
2.老年代:Serial Old、CMS、Parellel Old、G1

3.CMS与G1对比

STW(stop the word):指的是垃圾回收器回收时需要暂停用户所有线程

1.CMS回收流程:4步

1 初始标记:STW,只标记与GC roots直接相关的对象
2 并发标记:和用户线程同时执行,进行可达性分析(和用户线程争抢CPU资源)
3 重新标记:STW,修正上一阶段变动的对象
4 并发清除:并发清除掉垃圾对象

特点:在CMS回收时,每次清除会涉及两次STW,分别是初始标记和重复标记

缺点:3个
1.CPU敏感:

并发标记时和用户线程抢占CPU资源,降低了系统的吞吐量。
核心数 >= 4时,回收器线程占系统线程资源25%
核心数 < 4 时,会占用系统大量的线程资源

2.大量的内存碎片:

CMS使用标记清除算法实现垃圾回收。
参数:XX:+UseCMS-CompactAtFullCollection,默认开启,在每次FullGc时,会整理内存碎片,停顿时间变长
参数:XX:CMSFullGCsBefore-Compaction,默认0,每次FullGC都会整理。在CMS执行多次回收没有进行整理时,会触发一次整理

3.浮动垃圾:

并发清除时和用户线程同时进行,此时必然会有垃圾对象产生,需要预留一部分空间存储这些对象,因此会浪费一部分空间

2.G1回收流程:4步

1.初始标记:STW,只标记GC Roots直接关联的对象,并且修改TAMS指针,这个过程耗时很短,并且是在Minor GC触发时完成的,这个阶段实际没有额外的停顿

2.并发标记:可达性分析找出需要回收的对象,浮动对象会由STAB(原始快照)
3.最终标记:STW、处理并发标记阶段遗留的STAB记录
4.筛选回收:回收掉对象(部分复制回收算法,大部分标记整理算法)

G1/ZGC特点:

不同于其他垃圾回收器,用化整为零的思想将内存划分为多个大小相等的独立区域Region,每个Region内存大小是(1-32M)是2的倍数,回收时对Region进行回收

Region的4种角色:

1.Eden 2.Survivor 3.Old 4.Humongous(超大对象)

总结:G1适合上百G的堆空间回收,CMS适合20G以内,较大内存空间回收用G1才能凸显其性能

4. GC触发条件?

Minor GC:1种

Eden区满时,触发Minor GC

Full GC:5种

1.java代码调用System.gc时(java代码建议Full GC),什么时候执行,由JVM决定
2.老年代空间不足
3.方法区空间不足
4.Minor GC后,需要分配到老年代的对象大小大于老年代剩余空间时
5.由Eden区和From Space区向To Space区复制时,复制的对象大小大于To Space区大小时,把该部分对象转存到老年代时,并且老年代的空间小于这些对象的大小时。

5.类的加载过程?大概5步

大致分为5个步骤:

1.加载(类加载器):

过程:
1.1、通过类的全限定名加载该类对应的二级制字节流
1.2、将字节流所代表的静态存储结构转换为方法区运行时的数据结构
1.3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区各个类访问该对象的接口

2.连接(细分为3步:验证、准备、解析):

2.1 验证:验证哪些内容
2.1.1:文件格式验证:魔数版本号和常量等
2.1.2:元数据验证:类的语义信息是否符合java规范
2.1.3:字节码验证:验证程序的语义是否合法规范
2.1.4:符号引用验证:JVM会将符号引用转为直接应用,通过符号引用是否可以定位到具体类或方法,或权限等

2.2 准备:哪些准备?
为final static修饰的变量进行内存分配并对基本类型的变量指定初始值(非new的基本类型)

2.3 解析:解析哪些内容?
2.3.1 类或接口解析
2.3.2 字段解析
2.3.3 类方法解析
2.3.4 接口方法解析

3.初始化

3.1初始化哪些内容?
初始化static的成员变量并赋值,若有static代码块,则按顺序进行初始化。
若当前类具有父类,JVM会保证子类的()执行前,父类的()已经执行完毕。

3.2 初始化时机:

// 在连接阶段的准备环节赋值
public static final int INT_CONSTANT = 10;  
 // 在初始化阶段clinit>()中赋值
public static final int NUM1 = new Random().nextInt(10);   
// 在初始化阶段<clinit>()中赋值
public static int a = 1;                                                  
// 在初始化阶段<clinit>()中赋值
public static final Integer INTEGER_CONSTANT1 = Integer.valueOf(100);     
// 在初始化阶段<clinit>()中概值
public static Integer INTEGER_CONSTANT2 = Integer.valueOf(100);     
 // 在连接阶段的准备环节赋值
public static final String s0 = "helloworld0";           
 // 在初始化阶段<clinit>()中赋值
public static final String s1 = new String("helloworld1");      
 // 在初始化阶段<clinit>()中赋值
public static String s2 = "hellowrold2";                                 

3.3 初始化的方式
主动初始化:
1.实例化:使用new关键字,或者通过克隆、反序列化创建一个实例
2.静态方法:调用类的静态方法。
3.静态字段:当使用类、接口的静态字段时(final修饰特殊考虑)
4.反射:当使用java.lang.reflect包中的反射创建类实例

Class.forName("com.lr.gc.Order");

5.继承:当初始化子类时,如果发现其父类还没有进行初始化,则需要先触发其父类的初始化,但是这条规则并不适用于接口
6.default方法:如果一个接口定义了default方法,那么直接或间接实现该接口的类在进行初始化前,该接口要在其之前被初始化

被动初始化: 被动使用不会引起类的初始化
1.静态字段:通过子类引用父类的静态变量,不会导致子类初始化。
2.数组定义:通过数组定义类引用,不会触发此类的初始化
3.引用常量:引用常量不会触发类或接口的初始化,因为常量在连接阶段就已经被显式赋值了。
4.loadClass方法:调用ClassLoader类的loadClass()方法加载一个类,并不是对类的主动使用,不会导致类的初始化。

4.使用

5.卸载

6.双亲委派

1.根类加载器
2.扩展类加载器
3.应用加载器
4.自定义加载器

打破双亲委派:重新ClassLoad接口的classLoad方法
不打破双亲委派:重写findclass方法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值