200字范文,内容丰富有趣,生活中的好帮手!
200字范文 > 【深入理解Java虚拟机学习笔记】第三章 垃圾收集器与内存分配策略

【深入理解Java虚拟机学习笔记】第三章 垃圾收集器与内存分配策略

时间:2023-12-27 22:28:08

相关推荐

【深入理解Java虚拟机学习笔记】第三章 垃圾收集器与内存分配策略

最近想好好复习一下java虚拟机,我想通过深读【理解Java虚拟机 jvm 高级特性与最佳实践】(作者 周志明)并且通过写一些博客总结来将该书读薄读透,这里文章内容仅仅是个人阅读后简短总结,加强学习深度的同时方便进行知识的回顾之用。如涉及版权还望周大神看到后告知一下小弟,我会第一时间将文章下线,在此强烈推荐大家买纸质图书【理解Java虚拟机 jvm 高级特性与最佳实践】(作者 周志明)进行阅读,学习java虚拟机必备。努力学习只为遇到更好的你!

第三章 垃圾收集器与内存分配策略

Java 与 C++之间有一堵由内存动态分配和垃圾收集技术所围成的高墙,墙外面的人想进去,墙里面 的人却想出来

1 垃圾收集三个疑问

说起垃圾收集我们必须明确三件事情:

1 那些内存需要回收?2 如何回收?3 什么时候回收?将这三个问题搞清楚 你就会对真正的理解垃圾收集是个什么东东?我这里将围绕这三个问题进行阐述。带着疑问去学习。

1 那些内存需要回收?

关于第二个问题和第三个问题会在后面阐述到,我们来说一下 那些内存需要进行回收。

java虚拟机的程序计数器虚拟机栈本地方法栈

这三个内存区域 生命周期和线程一致,Sun HotSopt 直接将本地方法栈和Java 虚拟机栈 合二为一 我们这将虚拟机栈 和本地方法栈 统称为栈。而栈中存储的就是栈帧。我们的方法执行就是执行入栈和出栈的操作。在编译期每个栈帧占用内存大小已经是确定了,而程序计数器占用的内存是比较小的,所以我们不用太担心这三个内存区域的回收问题,因为一旦方法结束或线程结束内存就自然跟着回收了。

垃圾收集需要关注的是Java堆方法区,因为这2块内存区域内存非配和回收是不确定的,它们在运行期间才会知道要创建多少对象。需要转载多少类。还有就是每个对象存活的时间也各不一样。我们需要通过不同的策略区进行内存的分配和回收。

2 如何回收?

我们将不可能在被任何途径使用的对象称之为死去的对象,这些对象占用的内存就是我们进行垃圾回收的内存。那么如何判断对象已经死去呢?我们可以通过引用计数器算法可达性分析算法进行操作。

2.1 判断对象死去算法

2.1.1 引用计数器算法

引用计数器算法:就是给对象添加一个计数器,每当有地方引用它时,计数器加1; 当引用失效就减1。当计数器为0 就表示该对象不能在被使用。但是这种算法无法解决循环依赖的问题。

关于循环依赖具体介绍 请参考 西门吹牛 的一篇博客 /gudi/p/6414420.html?utm_source=itdadao&utm_medium=referral

2.1.2 可达性分析算法

可达性分析算法 通过一系列的GCRoots 为起始点向下进行搜索 搜索的路径就是引用链,当一个对象到GCRoots没有任何一条引用链相连(也称之为GC Roots 到这个对象不可达)。那么该对象就可以被判定为回收的对象。

如图 3-1 所示,对象 object 5、object 6、object 7 虽然互相有关联,但是它们到 GC Roots 是不可达的,所以它们将会被判定为是可回收的对象。

java 中可作为GCRoots 的对象包括下面几种:

虚拟机栈中引用的对象方法区中类静态属性引用的对象方法区中常量池中引用的对象本地方法栈引用的对象

2.3 回收方法区

方法区又称之为HotSpot 虚拟机中的永久代,它的垃圾收集包括2部分内容:废弃常量无用的类

和java堆中对象回收类似,当一个字面常量没有被任何地方引用时 在进行垃圾收集的时候该常量内存就会被进行回收。

无用的类需要满足3个条件:

改类的所有的实例都已经被回收加载该类的ClassLoader已经被回收该类对应的java.lang.Class 对象没有在任何地方被引用 并且无法通过反射访问该类的方法

2.4 垃圾收集算法

标记清除算法

标记清除算法 就是先标记要回收的对象,标记完成后回收所有被标记的对象。

它的缺点有2个:

1 效率低 标记和清除的过程效率都不高。

2 会产生大量的内存碎片。

内存碎片太多会导致无法分配比较大的对象,而频繁的执行垃圾收集的操作。

复制算法

复制算法会将内存分为相等2块, 然后只是使用其中一块 。当使用的一块内存用完时,将存活的对象复制另一块内存中。然后在将已经使用过的内存清理掉。

这种算法优点是效率高。

缺点是要牺牲一半的内存空间。

在我们的java虚拟机中 新生代的对象都是朝生夕死 的,其内存并没有按照 1:1 进行划分 而是将内存分为 一个较大的Eden 和 2块较小的Survivor 默认情况下 Eden和Survivor 大小比例是 8:1

标记整理算法

标记整理算法和标记清除算法标记过程一样,区别是在进行清理的时候会将存活的对象整理到一起 然后清理掉存活对象以外的内存。

这样做的好处是减少内存碎片的产生。

分代收集算法

分代收集算法是根据对象存活周期的不同将内存划分为几块。这样做的好处是我们可以根据不同代的特点采用不同的垃圾收集算法。java堆分为新生代老年代

新生代:新生代中会有大量的对象死去只有少量存活 所以采用复制算法

老年代:对象存活率高,所以采用标记清除标记整理算法。

2.5 HotSpot的算法实现

2.6 垃圾收器

我们上面讲了很多垃圾收集算法 而垃圾收集器就是我们这些算法的具体实现。在java虚拟机中我们通过各种垃圾收集器来完成内存的回收。

Serial 收集器

单线程作用于新生代的收集器 ,版本比较老的收集器。

ParNew 收集器

并行(多线程版)的作用于 新生代的收集器,它是Serial 收集器多线程版

Parallel Scavenge 收集器

并行的作用于新生代的收集器。 采用复制算法 可以设置吞吐量

Serial Old 收集器

单线程的作用于老年代收集器, 是Serial 收集器老年代版本收集器。

Parallel Old 收集器

并行作用于老年代收集器 ,是Parallel Scavenge 收集器的老年代版本。

CMS 收集器

并行并发的作用于新生代收集器,采用标记清除算法实现。

G1收集器

并行并发的作用于不同代的收集器,采用标记整理算法实现

3 什么时候回收

关于这个问题 书中通过内存分配和回收策略中进行详细的介绍。因为说到回收不得不说内存如何去进行分配。其实java 虚拟机就是帮我们处理了2件事情 给对象分配内存 和回收非配给对象的内存。

内存非配策略有以下几个

3.1 内存分配策略

在进行介绍之前我们要先说一下Minor GC和 Full GC 是什么? Minor GC 是新生代GC

就是发生在新生代垃圾收集操作。这个操作比较频繁回收速度也快。 Full GC/Major GC 发生在老年代的GC

一般最少执行一次Minor GC 该操作数据比较慢。一般会比Minor GC慢10倍

3.1.2 对象优先在Eden分配

大多数情况下对象在Eden区中分配,当Eden区没有足够的空间进行分配时 虚拟机将发动一次 Minor GC

3.1.2 大对象直接进入老年代

很长的字符串以及数组 我们称之为大对象,如过是大对象我们直接将其分配到老年代中。

3.1.3 长期存活的对象将进入老年代

每个对象都有一个年龄计数器 当年龄增加到一定程度后将会被晋升到老年代中。(默认是15岁)而年龄的判断是对象在Edne出生并经过一次Minor GC 后仍然存活并被Survivor 接受对象的的年龄为1 每经历一次MinorGC 年龄就增加1。

3.1.4 动态对象年龄判定

对象到达指定的年龄就进入老年代这个也不是绝对的。如果Survivor空间相同年龄对象大小总和大于Survivor空间的一半,那么年龄大于或等于该年龄的对象就可以直接进入老年代。

3.1.5 空间分配担保

在进行 Minor GC 前先判断老年代空间是否大于新生代所有对象的空间 如果大于则可以进行 MinorGC 如果不成立在检查 老年代空间是否大于晋升到老年代对象大小的空间。如果大于,如果大于就进行一次MinorGC 如果小于就进行一次Full GC。

我们的老年代就相当于新生代空间的担保人 。就想我们生活中的贷款一样,我们在贷款是一般需要有一个担保人,当你没有偿还能力是就让担保人替你偿还。我们通过这种方式来判断是否要执行Full GC

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。