java学习笔记 2021.10.24

2021.10.24

JVM

垃圾回收概述

概述

垃圾回收不只是java的特有,其他的也有体现,但现在已经成了java的招牌能力,极大的提高了开发的效率。

可以说,现在自动化的内存分配和垃圾回收的方式已经成为现代开发语言必备的标准。

关于垃圾回收的三个基本问题

image-20211024180325549

垃圾是什么:

是指在运行程序中没有任何指针指向的对象

当然,这是针对程序中的引用变量而言的,基本数据类型就不存在这样的问题。

image-20211024180602386

为什么需要垃圾回收器

image-20211024180653689

反正基本的一个理念就是合理运用空间。

JAVA垃圾回收机制

image-20211024192741400

当然这也会导致一些缺点,即会弱化JAVA开发人员在程序出现内存溢出时定位问题和解决问题的能力。

所以说,了解内存分配和垃圾回收的机制还是很重要的,有利于后面解决问题。

GC的作用区域

image-20211024192939641

这里跟前面讲运行时数据区是对应好的。

image-20211024193006676

垃圾回收相关算法

重在使用和场景的理解,而不是在算法的实现上。

垃圾标记阶段

引用计数算法

垃圾标记的意义即在于判断哪些对象需要被回收。当然这句话的前提也是建立在认为垃圾回收的主要部分还是在堆中,针对具体对象的。而不是针对GC很少的方法区。

image-20211024193530621

引用计数法:

image-20211024193632558

  • 第二部分即是引用计数法操作的原理。

image-20211024193855311

上图即体现了循环引用的缺点所在,可以看到当p的引用失效的时候,对象的具体的引用计数器的值没有为,即没法被回收,自然也就导致了内存泄漏。

视频中有对java不是使用引用计数算法的验证实例。有兴趣可以去看,反正意思就是java能够处理循环引用使得计数器不为0的对象。

小结:

image-20211024194407018

可达性分析算法

image-20211024194540420

大概就是下面这种情况:

image-20211024195330341

基本的实现原理

image-20211024194622476

因此,这里可以看出,对于GC roots的重要性,至少需要知道GC roots包括哪些类型,才能去了解到他后面能够怎么展开。

下图就是GC roots包括的各种元素:

image-20211024194844937

这里提供一个判断roots的小技巧,所以对于一个指针,当它保存了堆内存里面的对象,但是自己又不存放在堆内存里,那他就是一个root。

一些特殊情况:

image-20211024195137270

局部回收意思就是以伊甸区为目标回收的话,老年区之类的也可以作为roots。

一些注意点:

image-20211024195239482

对象的finalization机制

image-20211024195436225

简单理解就是垃圾回收之前处理好后事,把相关的一些东西都关闭了。并且也只有在垃圾回收时才会调用这个方法。

注意点:

image-20211024195556969

三种可能的状态,即是对于垃圾回收标记阶段非常重要的一个理解:

image-20211024195817118

  • 注意这里finalization只能被调用一次的意思就是就算被复活成功了,在第二次引用都被释放完之后,也是无法再调用finalization方法了。

具体的实现过程:

image-20211024200126585

  • 这里二也对应了没法主动调用方法的情况,会自动创建线程来对其进行调用。
  • 第一次标记是没有引用链的时候,第二次标记是调用该方法的时候。
  • 该方法主要用于在对象被回收的时候进行资源释放,通过视频里有的演示也可以看出前面执行的整个逻辑,有兴趣可以参考视频实例。
  • 同时也想说下代码中this、super之类的应该也是堆中的对象。

image-20211024201915136

GC Roots溯源

通常的使用场景即是判断具体对象的roots溯源,从而使在oom情况下看有没有出现内存泄露的情况。常用的工具有mat和jprofiler。后面有用到的时候再去具体了解。同样jprofiler还可以分析oom,在后面的调优的学习中也都会涉及到。

垃圾清除阶段

image-20211024205630614

标记-清除算法

具体的实现过程:

image-20211024205821551

  • 标记的意义即在于避免在垃圾回收的过程中又出现了新的垃圾。
  • 注意标记的是可达对象,即非垃圾对象。
  • 优点即是比较基础和常见

image-20211024210206549

  • 效率不算高在两次的遍历过程中也能够体会到。
  • 空闲内存的理解也在清楚的垃圾的位置的情况能够理解。并且空闲列表本身也要消耗内存。
  • 注意清除的做法也是如上图所示,没有真正的删除,通过放新的覆盖来删除。
复制算法

核心思想:

image-20211024210550532

图示:

image-20211024210614735

  • 也是要对可达对象进行遍历,然后不用标记,直接复制到另外一个区域。
  • 之后即是对两个区域的反复操作。
  • 即是前面有提到过的新生区的s0区和s1区的工作机制。

具体的优缺点:

image-20211024210802769

注意就是对比着前面的标记清楚算法。

  • 缺点的第二点其实也就说明对象的位置发生了变化,会导致局部变量表中的引用位置也要发生变化。
  • 最后一点即是说一下最好活着的对象并不是太多的情况。
  • 更是直接对应了s区的算法的选择。
标记-压缩算法

考虑到前面所说的复制算法,不适合用在老年区中,并且老年区中对内存碎片也很敏感,所以也不考虑标记清除算法。所以才考虑引入了这种算法。

执行过程:

image-20211024215009201

也叫标记整理算法,反正就是把活着的整理一下排下序。让内存不再碎片化。

image-20211024215217853

当然从上面也可以看出也存在引用指针转移的问题。

具体的优缺点:

image-20211024215259935

三种算法的总结

image-20211024215346841

综合来看,还是标记压缩算法比较理想化的。

分代收集算法

以具体问题具体分析的角度出发总结出来的算法

image-20211024215700243

现在几乎所有的垃圾回收都是基于分代收集算法来执行的,具体的对应代的算法选择如下:

image-20211024215840014

  • 通过s区缓解内存的原理即在于s区任意一个所占堆空间的比例就是很小的,有效避免了复制算法中说两个空间带来的内存占用的问题。

下面就是一个结合算法的老年区的垃圾回收器的举例,具体后面再说

image-20211024220207526

增量收集算法

image-20211024220400723

基础还是上面的基本算法,核心就是让垃圾线程和用户线程的交替进行。主要处理的也就是几个线程之间的冲突。

缺点即在于线程切换和上下文转换的消耗,会使得垃圾回收的总体成本上升,造成系统吞吐量的下降。

分区算法

image-20211024220700569

包括常用的g1垃圾回收器也是用的这种,后面学到时再详细解释。

垃圾回收相关概念

posted @ 2021-10-24 22:13  damndamit  阅读(62)  评论(0)    收藏  举报