java垃圾收集机制_深入Java垃圾收集机制

深入Java垃圾收集机制

作者:admin

分类:PHP, JAVA, .NET技术

时间:2016-07-14 18:37:09

点击量:251

From developer.com

垃圾收集(GC)是由JVM(Java虚拟机)运行的后台进程,当Java应用程序在前台运行,以自动清理内储。 垃圾收集器的存在缓解了程序员每次开发应用都得写一个内存释放的程序。 这样就提高了编码的利用效率。 这样程序员就可以专注于解决手头的问题,而让JVM处理内存管理的问题。 垃圾收集在自己的权利内是一个复杂的过程。 一旦你深入竞技场中探讨,你就会意识到,其还有很多的亮点。 本文将试图探讨一些垃圾收集API有关的技术。

明确相对模糊的GC

回收未使用的内存是一个复杂的过程,通过代码进行处理会比较容易出错,而且可能会导致程序的意外行为。可以出现如下的一些问题:

摇摆不定的引用

假设对象A引用对象B而B引用对象C,并在适当的时候,对象C被释放,而其他对象被新分配到该空间。 任何试图通过A引用对象C可能会导致不可预知的行为。 这是C / C ++指针的通病。

内存泄漏

当分配的内存段,虽然没有被引用,没有被释放时发生这种情况。想象删除树的根元素;叶节点,但是,占用内存会让他们达到一个无法连接的状态。 这些空间不断消耗资源既不能被使用也不能被回收,从而导致内存泄漏。

而Java的内存管理采用了一个替代方法。 与大多数现代面向对象的语言那样,它使用一个称为垃圾回收器的自动内存管理器。 垃圾收集主要有三个功能:

内存分配

确保只有被引用的对象保留在内存中

回收未被引用的内存

GC设计

Java的内存管理器的内存段分为三类: 年轻一代,老一代以及永久代。 新一代,新对象被分配在年轻一代内。 老一代包含那些留在年轻一代一段时间的对象;还有些大的对象被直接分配在老一代段。 永久代对象是那些GC发现易于管理,如描述的类和方法的对象。新一代段包含一个被称为伊甸的地区和两个较小的生存空间。伊甸园包含那些存活至少有一个垃圾收集波的对象,以及有可能会在被转移到生存空间前死去,并且最终成为老一代的状态。 特别是,当年轻一代被填满时,一个小的收集(算法)波弹出,并执行清洁或对象被移动到下一个状态。 当老一代被填满,主要收集(另一种算法)波会工作,这意味着几乎所有的代会被收集/清洗。

这是GC设计背后的一个初步想法。 引用“在Java HotSpotTM虚拟机,Sun微系统中的内存管理”进行了详细的解释。

GC算法

在Java HotSpot虚拟机内提供了四种垃圾回收(GC)算法。 让我们每个人都有一至两行的想法。

串行GC: 这个算法连续收集老少几代。 当收集出现时申请执行会被暂停。

并行GC: 这是以前算法的并行版本,对于具有大内存和多个CPU的机器上运行的应用程序非常有用。

大多并发标记和清除(CMS)GC: 此算法避免了长时间停留于老一代的项目,而使用自由列表来管理回收的空间,主要收集了标记和清除相平行,解决应用程序执行的问题。

垃圾首先GC: 此更换的CMS集合算法,给人带来可预测性的感觉和所谓的可配置性不可预知的收集时间。这意味着你不久就可以请求暂停时间,其是x的时间量,虽然没有确定该请求将被授予。

请参阅N. Salnikov-Tarnovski 和G. Smirnov的Java垃圾收集获取每个算法的细节分析。

gc()方法

JVM在系统内存不足时会尽快运行垃圾收集器。 我们可以从代码调用垃圾收集器? 是的,但也不能保证垃圾收集器会监听到。 在java.lang.Runtime中的gc()方法可以被用来“建议”JVM运行垃圾收集器,它可以完全忽略,所以没有必要为此而烦恼。 在Java API文档表述的gc()方法如下...

“运行垃圾回收器。 调用该方法表明Java虚拟机努力回收未使用的对象以使他们目前占据的内存可快速重用。 当从方法调用返回控制时,虚拟机做出了最好的努力以回收所有丢弃的对象。

这个名字gc代表了“垃圾回收器”。 虚拟机会根据需要自动执行这个循环过程,在一个单独的线程内,即使gc方法没有明确的被调用。

该方法System.gc()是常规和方便的调用此方法的手段“。

package org.mano.example;

public class Main {

public static void main(String[] args) {

Runtime runtime = Runtime.getRuntime();

Object[] obj = new Object[500];

for (int j = 0; j < 5; j++) {

System.out.println("Free Memory="

+ runtime.freeMemory());

for (int i = 0; i < 500; i++) {

obj[i] = new Object();

}

System.out.println("Iteration: "+j

+ " Free Memory is " + runtime.freeMemory());

System.gc();

System.out.println("gc called, free Memory now is "

+ runtime.freeMemory());

System.out.println("--------------------------");

}

}

}

Output: (may vary in your case)

Free Memory=61740128

Iteration: 0 Free Memory is 61740128

gc explicitly called free Memory now is 61790856

------------------------------------------------------

Free Memory=61790856

Iteration: 1 Free Memory is 61790856

gc  explicitly called free Memory now is 61791272

------------------------------------------------------

Free Memory=61791272

Iteration: 2 Free Memory is 61791272

gc explicitly called free Memory now is 61791272

------------------------------------------------------

Free Memory=61791272

Iteration: 3 Free Memory is 61791272

gc explicitly called free Memory now is 61791272

------------------------------------------------------

Free Memory=61791272

Iteration: 4 Free Memory is 61791272

gc explicitly called free Memory now is 61791272

------------------------------------------------------

观察在调用gc()之后内存大小的改变。

最后,敲定...

调用finalize()的方法,指出一组动作被之前的垃圾收集收回并使用的内存位置在对象上执行。方法finalize()是这些对象类的一员,这是Java中所有类的父类。 此外,它被声明为是被保护的;这意味着任何类可以重写此方法。

package org.mano.example;

public class Main {

private String

public Main(String name) {

this.name = name;

}

@Override

public void finalize() {

system.out.println("destiny calls.

You'll be wiped out, "

+ name);

}

public static void main(String[] args) {

for (int i = 0; i < 5; i++) {

new Main("A" + i);

}

System.gc();

}

}

对象,当他们超过了范围时,被标记为结束,并在实际的垃圾回收器呼叫内存之前放置在队列中。 但是,如果你想结束等待由Java运行时终止的所有对象,你可以使用runFnialization()方法声明为运行时类的成员,以及系统类。 其可以被调用或者作为:

Runtime.getRuntime().runFinalization();

or

System.runFinalization();

一个有趣的方面是,有被标记为最终确定的对象之间的可用时间,以及对象实际上正在抹去。 垃圾收集器来自终结后的下一个阶段,并再次检查,是否被抹去的对象仍然超出范围。 虽然不是个好主意,我们可以尝试复活在finalize()方法调用和实际垃圾收集之间的对象,如下所示:

package org.mano.example;

public class Main {

privateString name;

// ...same as above

@Override

public void finalize() {

name="I am back!!";

System.out.println("destiny calls.

You'll be wiped out, "

+ name);

}

// ...same  as above

}

这是一个非常糟糕的主意,以及也是一个不好的编程习惯。 在实验中没有坏处,尽管在现实生活中从来没有做到这一点。

finalize()方法并不鼓励被显式地使用,因为这种方法会自动被GC调用,在GC回收对象内存之前执行对象的清理操作。 GC不保证执行任何特定的时间。 在程序结束前可能不会执行;因此,这是非常不可能的,如果或当该方法finalize()被调用时。

敲定,终止...

如先前说的,当垃圾收集器将执行时,Java不会进行排它性控制。 每一个相关的垃圾收集方法就是JVM的建议,它现在可以回收内存。 同样地,在try... finally语句只是指出在try块中使用的资源释放。 最后的模块保证执行。 这确保了垃圾收集器可以回收由该类对象使用的内存。

try{

// resources used

}finally{

// resources released

}

参考类型

Java提供不同类型的引用,其可用于指定一个参考对象类以给出新的含义。 一个程序可以使用这些类型的一个参考对象,其引用一些其他对象,所述对象通过GC排他性收集,根据其引用类型的方式。 如果你不了解Java中的引用类型,这意味着你已经使用强引用类型。 其是普通的引用类型,如:

String greet=new String("Hello");

除了自强引用,还有其他三种类型的引用,即软,弱,和幻象,由包java.lang.ref中定义的类SoftReferenceWeakReference和PhantomReference表示。 由这些类型实现的引用对象类型是所谓的引用的抽象基类的子类。

“软引用适用于实现内存敏感的缓存。”

“弱引用被执行规范化映射以防止其键(或值)被回收。”

“幻影引用比使用Java终结机制更灵活的方式调度验前的清理行动。”

每个这些类型对应于不同水平的可达性。 据Java API文档,

对象被访问的可能性是很强的,如果它可以通过一些线程访问,而无需遍历任何引用对象。 新创建的对象是通过创建它的线程而实现。

一个对象是比较容易访问的,如果不是强可访问,但可以通过遍历软引用访问。

一个对象是弱可访问,如果它既不是强烈的也不是软访问,但通过遍历弱引用可访问。 当一个弱引用至一个弱可访问对象被清除,对象就适合终止。

对象是幻象可访问,如果它既不强烈,柔软,也不是弱可访问,它已经被终止,有些幻象引用引用它。

最后,对象是不可访问,因此有资格再利用,当它不以上述任何方式被访问时。

引用类型的完整说明需要单独专注于它。 让我们现在把它放在一边。

结论

垃圾收集是一个复杂的子系统,JVM这里最关键的是管理流程。 那可能并不完美,但仍给程序员自由的感觉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值