太顶了!阿里架构师深入讲解垃圾回收算法,强烈建议做开发的都看看。

〇、补充-数组长度

补讲数组长度:

1、如果不压缩,则在arrayOopDesc中声明的非静态字段之后分配。

此时存klass指针 + 数组长度要用 12字节
eg: 11111 11111

2、如果压缩,它将占用oopDesc中_klass字段的后半部分

此时存klass指针 + 数组长度要用 8字节。因为压缩后klass指针只占用4个自己,还剩4个空的字节,用来存储数组长度,而不是去再申请一个8个字节的长度,然后将压缩至4字节。
eg:1111 [多出来的四个字节存数组长度:1010]

这里是如何生省出来4个字节的呢?
因为存储类型和数组长度的用的是联合体,联合体大小就是8个字节,如果压缩开启后只用4个字节,剩下的4个字节就浪费了,所以用来存储数组长度。因为数组长度刚好是int,占用四个字节。

我们来看存储数组长度的联合体:

union_metadata {
    Klass*      _klass;  // 占用8B
    narrowKlass _compressed_klass; // 占用4B
} _metadata;

整个联合体占用8字节

  • JVM 四大模块: 1、类加载 2、内存模型 3、执行引擎 4、垃圾回收器
  • 其中,垃圾回收器是基于垃圾回收算法实现的。
  • 垃圾判断算法
  • 垃圾回收算法

标记清楚算法:标记的是存活对象还是等待回收的对象?
个人整理了一些资料,有需要的朋友可以直接点击领取。

Java基础知识合集

百本Java架构师核心书籍

对标阿里P7的java架构师学习路线图

结合金三银四整理的2021年最新面试题

垃圾判断算法

引用计数法

和可重入锁有点像,进入锁之后这个数+1,释放锁之后-1.

在对象中添加一个属性用于标记对象被引用的次数,每多一个其他对象引用,计数+1,当引用失效时,计数-1,如果计数=0,表示没有其他对象引用,就可以被回收。
这个算法无法解决循环依赖的问题。


可达性分析

通过一系列被称为“GC Roots”的根对象作为起始节点集,从这些节点开始,根据引用关系链向下搜索,如果某个对象无法被搜索到,则说明该对象无引用执行,可回收。相反,则对象处于存活状态,不可回收。
JVM中的实现是找到存活对象,未打标记的就是无用对象,GC时会回收。


哪些对象可以作为GC Root呢:
• 所有Java线程当前活跃的栈帧里指向GC堆里的对象的引用;换句话说,当前所有正在被调用的方法的引用类型的参数/局部变量/临时值。
• VM的一些静态数据结构里指向GC堆里的对象的引用,例如说HotSpot VM里的Universe里有很多这样的引用。
• JNI handles,包括global handles和local handles
• (看情况)所有当前被加载的Java类
• (看情况)Java类的引用类型静态变量
• (看情况)Java类的运行时常量池里的引用类型常量(String或Class类型)
• (看情况)String常量池(StringTable)里的引用

复制算法:给存活对象打标记,回收没有打标记的对象;

内存分配算法

虚拟机遇到一条new指令时,首先将去检查这个指令的参数是否能在常量池中定位到一个类的符号引用,并且检查这个符号引用代表的类是否已被加载、解析和初始化过。如果没有,那必须先执行相应的类加载过程。

在类加载检查通过后,接下来虚拟机将为新生对象分配内存。对象所需内存的大小在类加载完成后便可完全确定,为对象分配空间的任务等同于把一块确定大小的内存从Java堆中划分出来。假设Java堆中内存是绝对规整的,所有用过的内存都放在一边,空闲的内存放在另一边,中间放着一个指针作为分界点的指示器,那所分配内存就仅仅是把那个指针向空闲空间那边挪动一段与对象大小相等的距离,这种分配方式称为“指针碰撞”(Bump thePointer)。如果Java堆中的内存并不是规整的,已使用的内存和空闲的内存相互交错,那就没有办法简单地进行指针碰撞了,虚拟机就必须维护一个列表,记录上哪些内存块是可用的,在分配的时候从列表中找到一块足够大的空间划分给对象实例,并更新列表上的记录,这种分配方式称为“空闲列表”(FreeList)。选择哪种分配方式由Java堆是否规整决定,而Java堆是否规整又由所采用的垃圾收集器是否带有压缩整理功能决定。因此,在使用Serial、ParNew等带Compact过程的收集器时,系统采用的分配算法是指针碰撞,而使用CMS这种基于Mark-Sweep算法的收集器时,通常采用空闲列表。

指针碰撞(open jdk基于此实现,无限CAS,自旋锁)

取到指针地址,然后加上对象的大小。
然后判断相加后的值是否小于总的内存大小。
因为分配内存是并发的,所以使用了CAS去比较,如果失败,goto retry,继续分配。
CAS失败了,就代表该块内存已经被使用了。


空闲列表

0~10

0-5:    m_available_table   当前可以用内存
6-10:   m_idle_table 空闲列表

list<Memorycell> m_available_table; // 所有可使用的内存
list<Memorycell> m_used_table;      // 所有被使用的内存
list<Memorycell> m_idle_table;      // 空闲内存
list<Memorycell> m_transer_table;   // 待交换内存

内存池及相关算法

先搞明白角色

  • OS 堆
  • Memory Pool
  • Memory Chunk
  • Memory Cell

Memory Poo l内存池

理解成一个管理员,管理的是内存块,不直接持有内存

内存池中使用一个列表维护着内存块:


能做的事情:
向OS申请内存:malloc、calloc
释放内存:没有垃圾回收器,需要手动释放
其他:打印chunk信息

Memory Chunk 内存块

直接持有内存
管理内存块

将分配到的内存分块处理(Memory Cell, 内存细胞)
每个Cell占8字节

Memory Cell

真实在使用的内存
每个cell占的字节大小,由开发者决定。JVM中是8字节

每一个角色的分析

OS堆:操作系统也是有内存模型的
堆(OS堆)

静态区域
代码区域 可读可写可执行

Memory Pool

MemoryChunk *new_chunk(uint_size);  // 像操作系统要内存
    1、根据你要的内存以及对齐(8B)计算出向OS要多大的内存;
        如果要了79B,那么根据8字节对齐计算后,会向OS申请80B内存。
    
    2、向OS申请内存   
    
    3、根据不同的垃圾回收算法,填充不同的list
        标记清除、标记整理,这两种算法回收的是整个chunk. 这两种算法只需要两个list就能玩转
            list<Memorycell> m_available_table; // 所有可使用的内存
            list<Memorycell> m_used_table;      // 所有被使用的内存
        
        分代+复制算法:五五分,一半在用,一般空闲。四个list都要用]
            From 区 => TO 区
            0-10    正在用(gc) -》 我这边没有被回收的对象放在哪里 (From)-<(To)
            11-20   空闲 (To)->(From)

            list<Memorycell> m_available_table; // 所有可使用的内存    0-10 cell
            list<Memorycell> m_used_table;      // 所有被使用的内存   
            list<Memorycell> m_idle_table;      // 空闲内存         11-20 cell  
            list<Memorycell> m_transer_table;   // 待交换内存
        
void print_chunks();                // 调试用的

void free_chunks();                 // 释放内存,用C或者C++申请的内存需要手动释放

内存算法怎么写,取决于你要支持的垃圾回收算法

垃圾回收算法

1、将内存55分;

标记-清除算法

遍历所有的GC Roots,然后将所有GC Roots可达的对象标记为存活的对象。

面向整个堆,会产生碎片

如果需要分配大对象,需要连续的内存
而你的内存是碎片化的,分配不到内存。所以出现了标记整理算法,但是标记整理算法比较耗费CPU。

我们知道,Eden区的对象通常都是招生夕灭,对象通常在第一次gc就会被回收,生命周期很短,会产生大量的内存碎片。

这个时候不是没有内存,而是内存不是连续的,导致无法分配对象。

清除的过程将遍历堆中所有的对象,将没有标记的对象全部清除掉。

标记-整理算法

内存碎片合并算法
老年代基本都是基于这个算法实现的,因为老年代本身的回收对象的概率比较低。

能整理内存
合并碎片的时候需要STW

合并碎片的时候,面临两种空间:
1、这个空间的对象已经被释放
2、这个空间的对象未被释放(复杂):合并内存、数据移动、指针移动

分代+复制算法

存在的目的:解决标记-整理算法,合并碎片消耗性能过高的问题、以及gc停止用户线程过长的问题
1、将内存五五分(JVM中是8:1:1),一半用(0-10, From,),一半空闲(11-20, To);
2、发生gc的时候,切换角色:
原先的From区的内存处理:

标记的对象清理
存活的对象需要移动到新的内存区域(11-20)
数据整理
指针整理

总结:

不管是现在的9种垃圾回收器还会未来新的垃圾回收器,他们的底层都是这三种回收算法。

指针移动后,老的指针怎么办?

移动以后,老的地址不变,非常复杂。需要借助另外的数据结构,指针是动态计算出来的。下面是计算公式:


chunk->get_data()获取到chunk的首地址,

总结

内存池
    JVM不需要我们手动释放内存,有垃圾回收器自动回收;

垃圾回收算法
    标记-清除算法
        标记阶段:遍历所有的GC Roots,然后将所有GC Roots可达的对象标记为存活的对象
        清除阶段:将没有标记的对象全部清除掉。
    
    标记-整理算法
        标记存活的对象
        清除没有被标记的对象
        
        标记和清理这两个阶段基本和标记-清理算法是一样的

        内存整理:
    
    分代-复制算法
        因为整理算法性能太低,而且新生代的特点是招生夕灭
            


最后

各位大佬都看到这里了给个赞呗

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容