垃圾回收机制

5 篇文章 0 订阅
本文详细介绍了Java垃圾收集器的工作原理,包括分代收集理论、复制算法、标记-清除和标记-整理算法。重点讲解了CMS并发标记清除垃圾收集器及其优缺点,以及如何应对浮动垃圾问题。此外,还提到了G1垃圾收集器,它的目标是实现可预测的停顿时间,并介绍了StopTheWorld现象。最后,文章讨论了Java中字符串常量池的运作机制和String对象的不可变性。
摘要由CSDN通过智能技术生成

一、分代收集理论

1.绝大部分的对象都是朝生夕死 —新生代
2.对象熬过多次垃圾回收,越难回收 —老年代
应该这两类对象分开
在这里插入图片描述
cms 才会单独收集老年代
其他很多时候收集老年代是Full GC(新生代、老年代、方法区)

二、复制算法(Copying)

回收新生代

特点:
1.实现简单、运行高效
2.内存复制、没有内存碎片
3.利用率只有一半
在这里插入图片描述
Eden区的来源:
1.Appel式回收
2.提高空间利用率和空间分配担保
在这里插入图片描述

三、标记-清除算法(Mark-Sweep)

特点:
1.执行效率不稳定
2.内存碎片导致提前GC
在这里插入图片描述
内存碎片:堆空间不是连续的
在jvm中对象一定要占用连续空间

90%都是要清理的话 那么每个都要标记和清除就效率低
10%要清楚那么久效率还可以
所以适合老年代

四、标记-整理算法(Mark-Compact)

整理==压缩
清除之后,还要移动
涉及到对象的移动,效率偏低
老年代大部分是存活的 大部分是需要移动的 ,直接引用,还涉及到更新
用户线程还需要暂停
整体效率偏低

特点:
1.对象移动
2.引用更新
3.用户线程暂停
4.没有内存碎片

在这里插入图片描述

五、JVM中常见的垃圾收集器

单线程垃圾收集器

多线程并行垃圾收集器

多线程并发垃圾收集器

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
新生代和老年代回收器是配对的
G1可以跨越代
涉及到单线程多线程

从历史开始讲起:
Jvm 刚诞生的时候就只有两个 (开始就是单线程 ,内存不大的)
在这里插入图片描述

内存越来越大,诞生多线程
在这里插入图片描述

简单的垃圾回收器工作示意图

单线程收集
在这里插入图片描述
多线程收集
并行收集
在这里插入图片描述
要暂停所有的用户(业务)线程
不暂停的话,永远收集不完 ,永远收集不干净

在之后—>多线程并发垃圾回收器
部分时间可以并发,但不是全程
出现的第一个并发垃圾收集器是CMS(并发的标记清除垃圾回收器)
在这里插入图片描述
并行:就是多线程的垃圾收集器,启动多个线程
并发:垃圾回收器的线程和用户的线程可以同时工作

多线程,还是要暂停,那就不太好 —卡顿
所以推出CMS(只针对老年代)

CMS垃圾回收器工作示意图

Concurrent Mark Sweep
标记清除算法

1.初始标记 --暂停
2.并发标记 --同时进行
3.重新标记 --暂停
4.并发清除 --同时进行

CMS的缺点:
1.CPU敏感
2.浮动垃圾
3.内存碎片

在这里插入图片描述
CMS只针对老年代
根可达标记,很快

把标记阶段分成三步:
第一步:初始标记(暂停所有的线程) GcROOTS直接标连的对象 (速度很快)
第二步:并发标记(用户线程继续跑)(耗时长)(主要目的让一个时间长的并发)(根下的一一标记)
第三步:重新标记(暂停所有的线程)(耗时短)

清理的时候也要走并发
最后清理完后重置线程,让用户线程跑

CMS优点事跨时代的:不用完全暂停了

最大响应时间: CMS卡顿的时间比较短
如果cpu核心数小的话,影响比较大

并发清理时会产生浮动垃圾,等下一次清理,这次已经标记完了
可以老年代达到80%就进行回收,剩下20%放浮动垃圾
浮动垃圾过多了咋办?切换成 serial old
在这里插入图片描述
CMS弊端:因为浮动垃圾切换成单线程的serial old,回收效率很低
Java后端服务器,美其名为维护,就是重启,重启之后就没有碎片了,比serial old要好得多

主流还是CMS,考虑用户的体验
Android NDK 垃圾回收器花样百多 ,核心思想掌握CMS就可以了

G1

Garbage First
1.追求停顿时间
2.Region区
3.筛选回收
4.可预测停顿
5.算法——复制和标记整理

在这里插入图片描述
在这里插入图片描述
在G1中新生代和老年代一起分堆,不再是单纯的分代划分
有筛选回收,优先回收有价值的垃圾
humongous 大对象区 ,512kb 就是大对象

Eden、Survivor是复制算法
老年代和大对象采用标记整理
不走全部回收,走筛选回收
JVM 最小暂停时间,超过1000ms 就会骂人。。。
配参数可预测停顿(并不是绝对)

根据很多经验研究
CMS和G1平衡点,6~8个G,堆空间大于8G用G1的效率高一些,堆空间小追求效率用CMS

六、Stop The World现象

什么是STW:业务线程要等GC做完之后才能跑
为什么要STW:不然清理不干净
STW的危害:卡
在这里插入图片描述

七、常量池与String

常量池(方法区):

1.静态常量池
2.运行时常量池

字符串常量池(常量池的一部分)

静态常量池:

1.字面量: String i=“puppy”(puppy就是字面量)
2.符号引用:string 这个类 java.lang.string
3.类的、方法的信息

运行时常量池:

类加载-运行时数据区-方法区(逻辑区域)
实体,符号引用—>直接饮用(hash值)

String 的创建分配内存地址

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];
    ......
}

public final class String 不能再被继承
private final char value[]; final 对象的不可变性
String只要创建就不可变

        //JVM首先会检查该对象是否在字符串常量池中,如果在,就返回该对象引用,否则新的字符串将在常量池中被创建。
        //这种方式可以减少同一个值的字符串对象的重复创建,节约内存。
        String str ="abc";

在这里插入图片描述

        //首先在编译类文件时,"abc"常量字符串将会放入到常量结构中,在类加载时,“abc"将会在常量池中创建;
        //其次,在调用 new 时,JVM 命令将会调用 String 的构造函数,同时引用常量池中的"abc” 字符串,
        // 在堆内存中创建一个 String 对象;最后,str1 将引用 String 对象。
        String str1 =new String("abc");

在这里插入图片描述

        //这里就跟第一步类似。
        Location location = new Location();
        location.setCity("深圳");
        location.setRegion("南山");

在这里插入图片描述

        //new Sting() 会在堆内存中创建一个a的String对象,
        // “puppy"将会在常量池中创建
        // 在调用intern方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回引用。
        String a =new String("puppy").intern();
        //调用 new Sting() 会在堆内存中创建一个b的String 对象,。
        //在调用 intern 方法之后,会去常量池中查找是否有等于该字符串对象的引用,有就返回引用。
        String b = new String("puppy").intern();
        //所以 a 和 b 引用的是同一个对象。
        if(a==b) {
            System.out.print("a==b");
        }else{
            System.out.print("a!=b");
        }

不加intern则不相等,创建了两个对象
加了intern会去常量池中查找是否有等于该字符串对象的引用,有就返回引用.

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值