华为面试官问我:你真的明白Java垃圾回收器吗?我生气了!!(︶︿︶)

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注Java)
img

正文

[

]( )

  • step1: 第一次扫描,通过GC root对象判断堆内存中哪些对象可以进行垃圾回收,进行标记。

  • step2: 第二次扫描, 将那些标记的GC root对象进行垃圾回收,只需要将起始内存地址与终止内存地址放入空闲内存区就行。

1-2 标记整理算法


第一个依旧是标记,第二步会进行一个空间整理,从而不产生碎片。

优点:避免了内存碎片

缺点:对空间的整理使得效率比较低下。

1-3 复制算法


特点:

将管理的内存分为2块区域,from区域与to区域,将那些不需要回收的对象从from区域拷贝到to区域。复制的过程中完成内存区域的整理。之后交换from和to的指向。

优点:不会产生内存碎片

缺点:需要双倍的内存空间,内存利用率不高,而且拷贝也需要时间。[

]( )

1-4 三种垃圾回收算法总结


| 垃圾回收算法 | 优点 | 缺点 |

| — | — | — |

| 标记清除算法(Mark Sweep) | 速度较快 | 产生内存碎片 |

| 标记整理算法(Mark Compact) | 没有内存碎片 | 速度慢 |

| 复制算法(Copy) | 没有内存碎片 | 需要占用双倍内存空间 |

注意:实际的JVM垃圾回收算法中上面的三种算法是综合使用的。

2 JVM分代回收算法

========================================================================

2-1 概述


Garden of Eden:伊甸园 garbage:垃圾

[

]( )

新生代主要由三部分内容组成,分别是Eden区,幸存区from,幸存区to。 通常情况下只有Eden区与幸存区from会存放数目,幸存区to只有垃圾回收时,复制对象会用到。堆内存的新生代进行一次垃圾回收(Minor GC),大部分对象都会都会被回收。

老年代通常存放一些经常被使用的对象,一个对象如果经历多次垃圾回收仍然幸存,那么该对象会从新生代放入老年代。只有新生代内存不足并且老年代内存也不足的时候才会触发full GC对老年代的对象进行垃圾回收。

为什么需要进行划分?

实际环境中,对象的生命周期是不同的,老年代的对象生命周期比较长,可能很长时间才进行一次垃圾回收。新生代的对象生命周期比较短,垃圾回收比较频繁。这种分区法方便采用不同的垃圾回收算法更加有效的进行垃圾回收。

2-2 分代垃圾回收示例


step1:程序刚刚开始运行,产生的对象先放入Eden区,当Eden区放不下的时候。

step2:对Eden区进行Minor GC,并将没有被垃圾回收的对象复制的幸存区To,然后交换幸存区To和幸存区From,第一次垃圾回收的最终的效果如下图所示:

step3: 第一次Minor GC, Eden区又有空间可以分配给新的对象使用,经过一段时间Eden又不够用了,触发第二次Minor GC, 这次垃圾会检查Eden区以及幸存区From哪些对象可以存活,并将这些对象复制到幸存区To,然后交换幸存区To和幸存区From,这个时候Eden区又空了出来,可以放置新的对象。

实际垃圾回收过程中,JVM会对每个对象经过垃圾回收幸存下来的次数进行记录,比如上图中,幸存区的2个对象经过垃圾回收的次数分别是1和2。

step4: 当一些对象经过垃圾回收的次数仍然幸存的次数达到一个阈值(说明这个对象价值比较高),那么这个对象会被移动到老年代。

极端情况考虑:Eden区,from区,老年区都已经满了?

此时会触发Full GC(优先Minor GC,Minor GC依旧内存不够)

2-3 分代垃圾回收的总结


对象首先分配在伊甸园区域

  • 新生代空间不足时,触发 minor gc,伊甸园和 from 存活的对象使用 copy复制到 to 中,存活的

对象年龄加 1并且交换from to

  • minor gc 会引发stop the world,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行

暂停时间较短,由于新生代大部分对象都是垃圾,复制的对象很少,所以效率较高

  • 当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit,对象头存储)

  • 当老年代空间不足,会先尝试触发 minor gc,如果之后空间仍不足,那么触发full gc,STW的时

间更长

  • Full GCstop the world的时间要比MInor GC时间长,老年代存活对象较多加上空间整理时间,所以停止时间会较长。

如果Full GC后,空间仍然不足会触发内存不足的异常。

2-4 垃圾回收相关的虚拟机参数


垃圾回收器概述


| 参数含义 | 参数 | 备注 |

| — | — | — |

| 堆初始大小 | -Xms | |

| 堆最大大小 | -Xmx 或 -XX:MaxHeapSize=size | |

| 新生代大小 | -Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size ) | NewSize是初始大小,MaxNewSize是最大大小。 |

| 幸存区比例(动态) | -XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy | 幸存区的比例,默认是8,假设新生代10M内存,8M划分给Eden区,剩下的二等分,一份from,一份to。 |

| 幸存区比例 | -XX:SurvivorRatio=ratio | 动态调整幸存区比例 |

| 晋升阈值 | -XX:MaxTenuringThreshold=threshold | 用于动态调整幸存区比例 |

| 晋升详情 | -XX:+PrintTenuringDistribution | 用于动态调整幸存区比例 |

| GC详情 | -XX:+PrintGCDetails -verbose:gc | 打印详情信息 |

| FullGC 前 MinorGC | -XX:+ScavengeBeforeFullGC | 默认在Full GC 前进行一次Minor GC |

2-5 垃圾回收案例分析


情况1:什么都不放的情况

new generation:新生代 tenured generation:老年代

package cn.itcast.jvm.t2;

import java.util.ArrayList;

/**

  • 演示内存的分配策略

*/

public class Demo2_1 {

private static final int _512KB = 512 * 1024;

private static final int _1MB = 1024 * 1024;

private static final int _6MB = 6 * 1024 * 1024;

private static final int _7MB = 7 * 1024 * 1024;

private static final int _8MB = 8 * 1024 * 1024;

//加入Java开发交流君样:756584822一起吹水聊天

// -Xms20M -Xmx20M -Xmn10M : 堆初始与最大大小都是20M,新生代的大小为10M.

// -XX:+UseSerialGC : 为了学习方便,采用这个垃圾回收器,默认的垃圾回收器并不是这个。

// -XX:+PrintGCDetails -verbose:gc :打印详细信息

// -XX:-ScavengeBeforeFullGC :在Full GC 前进行 Minor GC.

public static void main(String[] args) throws InterruptedException {

new Thread(() -> {

ArrayList<byte[]> list = new ArrayList<>();

list.add(new byte[_8MB]);

list.add(new byte[_8MB]);

}).start();

System.out.println(“sleep…”);

Thread.sleep(1000L);

}

}

情况1执行结果

可以看到即使用户没有创建对象,系统对象也要占据一部分堆内存空间。

Heap

def new generation total 9216K, used 2341K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)

// 新生代的空间总的大小为9216K,这里没有把To空间给计算进去,系统任务To的空间是分配是不可用的,所以不是10M,已经使用了2341K,[]内部则是内存地址范围。

eden space 8192K, 28% used [0x00000000fec00000, 0x00000000fee49420, 0x00000000ff400000)

//

from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)

to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)

tenured generation total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)

// 老年代大小为10M,可以看到没有任何空间使用

//加入Java开发交流君样:756584822一起吹水聊天

the space 10240K, 0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)

Metaspace used 3394K, capacity 4496K, committed 4864K, reserved 1056768K

class space used 378K, capacity 388K, committed 512K, reserved 1048576K

Java的内存对象都是分配在堆上吗

情况2:新生代堆空间放满,触发GC

public static void main(String[] args) throws InterruptedException {

ArrayList<byte[]> list = new ArrayList<>();

list.add(new byte[_7MB]); // 系统类占用2341K,加上new的7MB触发垃圾回收

}

情况2执行结果

[GC (Allocation Failure) [DefNew: 2342K->696K(9216K), 0.0029193 secs] 2342K->696K(19456K), 0.0029867 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]

// GC:minor GC(新生代垃圾回收) FUll GC (老年代垃圾回收)

// [Times: user=0.00 sys=0.00, real=0.00 secs] 垃圾回收执行时间

Heap

def new generation total 9216K, used 8110K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)

eden space 8192K, 90% used [0x00000000fec00000, 0x00000000ff33d8c0, 0x00000000ff400000)

from space 1024K, 67% used [0x00000000ff500000, 0x00000000ff5ae100, 0x00000000ff600000)

to space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)

tenured generation total 10240K, used 0K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)

the space 10240K, 0% used [0x00000000ff600000, 0x00000000ff600000, 0x00000000ff600200, 0x0000000100000000)

Metaspace used 3539K, capacity 4536K, committed 4864K, reserved 1056768K

//加入Java开发交流君样:756584822一起吹水聊天

class space used 395K, capacity 428K, committed 512K, reserved 1048576K

情况三: 新生代内存随着对象的增多放不下了

public static void main(String[] args) throws InterruptedException {

ArrayList<byte[]> list = new ArrayList<>();

list.add(new byte[_7MB]);

list.add(new byte[_512KB]);

list.add(new byte[_512KB]);

}

执行结果

新生代放不下,将新生代的对象放置到老年代。

[GC (Allocation Failure) [DefNew: 2342K->670K(9216K), 0.0022591 secs] 2342K->670K(19456K), 0.0023131 secs] [Times: user=0.02 sys=0.00, real=0.00 secs]

[GC (Allocation Failure) [DefNew: 8678K->538K(9216K), 0.0061246 secs] 8678K->8354K(19456K), 0.0061637 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]

Heap

def new generation total 9216K, used 1132K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)

eden space 8192K, 7% used [0x00000000fec00000, 0x00000000fec94930, 0x00000000ff400000)

from space 1024K, 52% used [0x00000000ff400000, 0x00000000ff486a00, 0x00000000ff500000)

to space 1024K, 0% used [0x00000000ff500000, 0x00000000ff500000, 0x00000000ff600000)

tenured generation total 10240K, used 7815K [0x00000000ff600000, 0x0000000100000000, 0x0000000100000000)

the space 10240K, 76% used [0x00000000ff600000, 0x00000000ffda1f80, 0x00000000ffda2000, 0x0000000100000000)

Metaspace used 3539K, capacity 4536K, committed 4864K, reserved 1056768K

//加入Java开发交流君样:756584822一起吹水聊天

class space used 395K, capacity 428K, committed 512K, reserveed 1048576K

情况四:一开始直接分配大于新生代的内存,如果老年代放的下,则直接放到老年代

public static void main(String[] args) throws InterruptedException {

ArrayList<byte[]> list = new ArrayList<>();

list.add(new byte[_8MB]);

}

执行结果

Heap

//加入Java开发交流君样:756584822一起吹水聊天

def new generation total 9216K, used 2507K [0x00000000fec00000, 0x00000000ff600000, 0x00000000ff600000)

eden space 8192K, 30% used [0x00000000fec00000, 0x00000000fee72ca8, 0x00000000ff400000)

from space 1024K, 0% used [0x00000000ff400000, 0x00000000ff400000, 0x00000000ff500000)

最后

针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。

下面的面试题答案都整理成文档笔记。也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)

在这里插入图片描述

最新整理电子书

在这里插入图片描述

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
, 0x00000000ff400000, 0x00000000ff500000)

最后

针对最近很多人都在面试,我这边也整理了相当多的面试专题资料,也有其他大厂的面经。希望可以帮助到大家。

下面的面试题答案都整理成文档笔记。也还整理了一些面试资料&最新2021收集的一些大厂的面试真题(都整理成文档,小部分截图)

[外链图片转存中…(img-kK99fG7p-1713455974064)]

最新整理电子书

[外链图片转存中…(img-g2F677uR-1713455974064)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Java)
[外链图片转存中…(img-3dHTruoT-1713455974065)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值