JVM学习—垃圾收集器与内存分配策略

在学习垃圾收集器与内存分配之前,我们要思考三个问题:

​ 1、哪些内存才需要我们去回收?我们如何判断这些内存可以回收?

​ 2、什么时候回收?

​ 3、如何回收?

下面我们将根据这三个问题去好好研究一下垃圾收集器。

1、对象已死?—判断是否需要被回收

​ 在垃圾回收之前我们要确定哪些对象时还“存活”着,哪些对象已经“死去”。下面介绍几种流行的方法:

1.1、引用计数法

引用计数法就是给对象添加一个引用计数器,每当有一个地方引用它时,计时器值加1;当失效时减1,当计数器为0时对象就不可能再被使用。此方法实现简单,效率高,但是Java中并没有引用这种方法,根本原因在于它很难解决对象之间循环依赖的问题。

1.2、根搜索法

​ 这个算法的思路就是通过一系列名为“GC Roots”的对象作为起点,从这些节点开始向下搜索,搜索所走过的路径称为引用链,当一个对象到“GC Roots”没有任何引用链相连,则证明此对象是不可用的。可以作为GC Roots的对象包括以下几种:

​ 1、虚拟机栈中的引用的对象。

​ 2、方法区中的类静态属性引用的对象。

​ 3、方法区中常量引用的对象。

​ 4、本地方法栈中JNI的引用对象。

1.3、再谈引用

​ JDK1.2之后,Java对引用概念进行了扩充,将引用分为:强引用、软引用、弱引用、虚引用四种。为什么需要这么对中引用呢?因为我们不希望对象只有引用被引用两种状态,在内存空间不足的情况下,我们可以抛弃一些对象,来缓解内存的紧张。

​ 1、强引用

​ 类似“Object obj=new Object()”,只要强引用还在,垃圾收集器将永远不会回收掉被引用的对象。

​ 2、软引用

​ 对于软引用而言,在系统将要发生内存溢出异常之前,将会把这些对象列入回收范围之中并进行第二次回收,如果这次回收还是没有足够的内存,才会抛出内存溢出异常。

​ 3、弱引用

​ 被弱引用关联的对象只能生存到下次垃圾收集之前。

​ 4、虚引用

​ 虚引用完全不会对对象的生存时间构成影响 ,它的唯一目的就是希望能在这个对象被回收的时候收到一个系统通知。

2、如何回收?—垃圾收集算法

2.1、标记清除算法

​ 标记—清除算法主要分为两个阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。它有两个主要问题:1、效率问题,标记和清除的过程效率都不高。2、空间问题,标记清除之后会产生大量的不连续的内存碎片。

2.2、复制算法

​ 复制算法就是将内存分为大小相等的两块,每次使用其中的一块。当这块内存用完了,将存活的对象复制到另外一块上面,然后再把已使用过的内存空间一次清理掉。这种方法的优点是实现简单效率高,缺点是内存缩小为原来的一半


2.3、标记—整理算法

​ 和标记清除算法类似,不同的是不是直接对可回收的对象进行清理,而是让所有存活的对象都向一端移动,然后直接清除掉端边界以外的内存

2.4、分代收集算法

​ 分代收集算法就是将对象的存活周期的不同划分为几块,一般把Java堆分为新生代和老年代。新生代大批对象死去,只有少量存活则使用复制算法。老年代因为对象存活率高,就必须使用“标记—清理”或“标记—整理”算法。

3、什么时候回收?—内存分配与回收策略

​ 首先我们要知道堆被划分成两个不同的区域:新生代(Young)和老年代(Old)。新生代又被划分为三个区域:Eden、From Survivor、To Survivor。GC的方式有几种分为:Minor GCMajor GC以及full GC

​ Minor GC:从年轻代(包括Eden和Survivor区域)回收内存称为Minor GC。

​ Major GC:清理老年代。一般一次 Major GC会伴随一次 Minor GC(但也非绝对)

​ Full GC:是清理整个堆空间,包括年轻代和老年代。

3.1、对象优先分配在Eden区

​ 大多数情况下,对象在新生代Eden区中分配。当Eden区没有足够的空间进行分配时,虚拟机发起一次Minor GC。关于内存分配策略强烈推荐https://www.jianshu.com/p/314272e6d35b

3.2、大对象直接进入老年代

​ 假设新创建的对象很大,比如为5M(这个值可以通过PretenureSizeThreshold这个参数进行设置,默认3M),那么即使Eden区有足够的空间来存放,也不会存放在Eden区,而是直接存入老年代。

3.3、长期存活的对象进入老年代

​ 此外,如果对象在Eden出生并且经过1次Minor GC后仍然存活,并且能被To区容纳,那么将被移动到To区,并且把对象的年龄设置为1,对象没"熬过"一次Minor GC(没有被回收,也没有因为To区没有空间而被移动到老年代中),年龄就增加一岁,当它的年龄增加到一定程度(默认15岁,配置参数-XX:MaxTenuringThreshold),就会被晋升到老年代中。

4、引用参考

1、https://blog.csdn.net/weixin_39788856/article/details/80388002

2、https://www.jianshu.com/p/314272e6d35b

你可能感兴趣的:(JVM学习—垃圾收集器与内存分配策略)