首页 > 编程知识 正文

java手动回收垃圾,java回收无用类对象

时间:2023-05-05 00:22:02 阅读:163845 作者:4803

JVA垃圾回收机制

说起垃圾回收(Garbage Collection,GC ),很多人都会自然而然地与Java联系在一起。 在Java中,程序员不需要在意内存的动态分配和垃圾回收的问题,全部交给JVM。 宽大的羽毛,垃圾回收是指释放垃圾占用的空间,Java中什么样的对象被认定为“垃圾”? 那么,部分对象被确定为垃圾后,采用什么策略进行回收(释放空间)? 当前的商用虚拟机有哪些典型的垃圾收集器? 让我们逐一探讨这些问题。 以下是本文的目录大纲。

一.如何确认某个对象是“垃圾”呢?

二.典型的垃圾收集算法

三.典型垃圾收集器

如果有不正当的地方,请谅解和批评指正。 非常感谢。

请尊重作者的劳动成果,注明原文链接:

一.如何确认某个对象是“垃圾”呢?

在本节中,首先理解最基本的问题。 如果判断对象是“垃圾”呢? 既然垃圾收集器的作用是回收垃圾对象为新对象占用的空间,垃圾收集器如何确定对象是“垃圾”呢? 这意味着确定可以用什么方法回收对象。

在java中,通过引用与对象相关联。 这意味着您必须通过引用来操作对象。 一个明显简单的方法是确定引用计数是否可以回收对象。 一般来说,如果某个对象没有关联的引用,则表示该对象在其他地方几乎不可能被使用,并且该对象将成为可重用对象。 该方式为引用计数法。

这种方式的特点是安装简单高效,但由于不能解决循环引用问题,没有被Java采用(Python采用引用计数法)。 请看以下代码:

最后两个语句将object1和object2赋值为null。 这意味着您将无法访问object1和object2指向的对象,但由于它们彼此引用了对方,因此如果引用计数不为0,垃圾收集器将永远不会回收它们。

为了解决这个问题,Java采用了可达性分析法。 该方法的基本思想是以一系列“GC Roots”对象为起点进行检索,如果在“GC Roots”和一个对象之间没有可到达的路径,则可以说该对象是不可到达的,但被判定为不可到达的对象被判断为不能到达的对象要成为可复用对象,至少需要经过两次标记过程,如果在这两次标记过程中也不能逃脱成为可复用对象的可能性,那么基本上就是真正的可复用对象。

可行性分析法具体是如何操作还不太清楚,如果有哪位朋友很熟悉的话,请告诉我。

请看以下示例:

第几行中的对象可能可重用? 在第七行代码中,对象是可重用的。 为什么要让读者自己思考呢?

再看一个例子:

这三个语句中的哪一个将String对象作为可复用对象? 第2句和第3句、第2句在内存不足的情况下判定为String对象可以再利用,第3句则判定为无论什么情况String对象都可以再利用。

最后,总结平时经常遇到的将对象判定为可复用对象的情况。

1 )将引用赋给null,或者已经将对对象的引用指定给新对象,如以下代码所示:

2 )本地引用指向的对象。 例如,以下代码:

每次执行循环时,生成的Object对象都是可重用的对象。

3 )只有弱引用及其相关对象,例如:

二.典型的垃圾收集算法

在确认了可以回收哪些垃圾之后,垃圾机要做的就是开始回收垃圾,但那里存在着如何有效地进行垃圾回收的问题。 由于javavirtualmachinespecification没有明确规定垃圾收集器的实现方式,因此各供应商的virtualmachine实现垃圾收集器的方式不同,因此这里有一些常见的垃圾收集程序

1 .无奈的高跟鞋-Sweep (标记-通关)算法

这是最基本的垃圾回收算法,最基本的是因为它最容易实现,思想也最简单。 标记-清除算法分为两个阶段:标记阶段和清除阶段。 标记阶段的任务是标记所有需要回收的对象,而清除阶段是回收已标记对象占用的空间。 过程如下图所示。

从图中可以看出,令牌/清除算法很容易实现,但存在着容易出现内存碎片的严重问题。 如果碎片过多,当后续进程需要为大型对象分配空间时,可能无法找到足够的空间,从而提前触发新的垃圾回收行为。

2 .复制算法

为了解决无奈高跟鞋-Sweep算法的缺陷,提出了一种Copying算法。 将可用内存按容量划分为大小相同的两个块,每次只使用其中一个。 如果此块的内存用完,可以将仍然存在的对象复制到另一个块中,然后一次清理已使用的内存空间,从而减少内存碎片问题。 过程如下图所示。

该算法实现简单,运行高效,不易出现内存碎片,但由于可用内存减少了一半,为执着于内存空间使用的棒棒糖付出了代价。

显然,Copying算法的效率与生存对象的数量多少有很大关系。 例如

果存活对象很多,那么Copying算法的效率将会大大降低。

3.无奈的高跟鞋-Compact(标记-整理)算法

为了解决Copying算法的缺陷,充分利用内存空间,提出了无奈的高跟鞋-Compact算法。该算法标记阶段和无奈的高跟鞋-Sweep一样,但是在完成标记之后,它不是直接清理可回收对象,而是将存活对象都向一端移动,然后清理掉端边界以外的内存。具体过程如下图所示:

4.Generational Collection(分代收集)算法

分代收集算法是目前大部分JVM的垃圾收集器采用的算法。它的核心思想是根据对象存活的生命周期将内存划分为若干个不同的区域。一般情况下将堆区划分为老年代(Tenured Generation)和ssdzh代(Young Generation),老年代的特点是每次垃圾收集时只有少量对象需要被回收,而ssdzh代的特点是每次垃圾回收时都有大量的对象需要被回收,那么就可以根据不同代的特点采取最适合的收集算法。

目前大部分垃圾收集器对于ssdzh代都采取Copying算法,因为ssdzh代中每次垃圾回收都要回收大部分对象,也就是说需要复制的操作次数较少,但是实际中并不是按照1:1的比例来划分ssdzh代的空间的,一般来说是将ssdzh代划分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden空间和其中的一块Survivor空间,当进行回收时,将Eden和Survivor中还存活的对象复制到另一块Survivor空间中,然后清理掉Eden和刚才使用过的Survivor空间。

而由于老年代的特点是每次回收都只回收少量对象,一般使用的是无奈的高跟鞋-Compact算法。

注意,在堆区之外还有一个代就是永久代(Permanet Generation),它用来存储class类、常量、方法描述等。对永久代的回收主要回收两部分内容:废弃常量和无用的类。

三.典型的垃圾收集器

垃圾收集算法是 内存回收的理论基础,而垃圾收集器就是内存回收的具体实现。下面介绍一下HotSpot(JDK 7)虚拟机提供的几种垃圾收集器,用户可以根据自己的需求组合出各个年代使用的收集器。

1.Serial/Serial Old

Serial/Serial Old收集器是最基本最古老的收集器,它是一个单线程收集器,并且在它进行垃圾收集时,必须暂停所有用户线程。Serial收集器是针对ssdzh代的收集器,采用的是Copying算法,Serial Old收集器是针对老年代的收集器,采用的是无奈的高跟鞋-Compact算法。它的优点是实现简单高效,但是缺点是会给用户带来停顿。

2.ParNew

ParNew收集器是Serial收集器的多线程版本,使用多个线程进行垃圾收集。

3.Parallel Scavenge

Parallel Scavenge收集器是一个ssdzh代的多线程收集器(并行收集器),它在回收期间不需要暂停其他用户线程,其采用的是Copying算法,该收集器与前两个收集器有所不同,它主要是为了达到一个可控的吞吐量。

4.Parallel Old

Parallel Old是Parallel Scavenge收集器的老年代版本(并行收集器),使用多线程和无奈的高跟鞋-Compact算法。

5.CMS

CMS(Current 无奈的高跟鞋 Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,它是一种并发收集器,采用的是无奈的高跟鞋-Sweep算法。

6.G1

G1收集器是当今收集器技术发展最前沿的成果,它是一款面向服务端应用的收集器,它能充分利用多CPU、多核环境。因此它是一款并行与并发收集器,并且它能建立可预测的停顿时间模型。

下面补充一下关于内存分配方面的东西:

对象的内存分配,往大方向上讲就是在堆上分配,对象主要分配在ssdzh代的Eden Space和From Space,少数情况下会直接分配在老年代。如果ssdzh代的Eden Space和From Space的空间不足,则会发起一次GC,如果进行了GC之后,Eden Space和From Space能够容纳该对象就放在Eden Space和From Space。在GC的过程中,会将Eden Space和From  Space中的存活对象移动到To Space,然后将Eden Space和From Space进行清理。如果在清理的过程中,To Space无法足够来存储某个对象,就会将该对象移动到老年代中。在进行了GC之后,使用的便是Eden space和To Space了,下次GC时会将存活对象复制到From Space,如此反复循环。当对象在Survivor区躲过一次GC的话,其对象年龄便会加1,默认情况下,如果对象年龄达到15岁,就会移动到老年代中。

一般来说,大对象会被直接分配到老年代,所谓的大对象是指需要大量连续存储空间的对象,最常见的一种大对象就是大数组,比如:

byte[] data = new byte[4*1024*1024]

这种一般会直接在老年代分配存储空间。

当然分配的规则并不是百分之百固定的,这要取决于当前使用的是哪种垃圾收集器组合和JVM的相关参数。

参考资料:

《深入理解Java虚拟机》

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。