java中的垃圾处理机制

Java基础

浏览数:181

2020-6-13

1.何为垃圾
在Java中,如果对象实体没有引用指向的话,存储该实体的内存便成为垃圾。JVM会有一个系统线程专门负责回收垃圾。垃圾同时包括分配对象内存间的碎片块

2.垃圾处理包含的算法

Java语言规范没有明确地说明JVM使用哪种垃圾回收算法,但是任何一种垃圾回收算法一般要做2件基本的事情:(1)发现无用的信息对象,(2)回收无用对象占据的内存,使得该内存可以被程序再次使用。
垃圾回收一面在回收内存,一面使堆中的内存紧密排列。下面介绍几种算法:

2-1引用计数法
该算法使用引用计数器来区分存活对象和不再使用的对象。一般来说,堆中的每个对象对应一个引用计数器。当每一次创建一个对象并赋给一个变量时,引用计数器置为1。当对象被赋给任意变量时,引用计数器每次加1当对象出了作用域后(该对象丢弃不再使用)或者被置为null时,引用计数器减1,一旦引用计数器为0,对象就满足了垃圾收集的条件。
基于引用计数器的垃圾收集器运行较快,不会长时间中断程序执行,适宜必须实时运行的程序。但引用计数器增加了程序执行的开销,因为每次对象赋给新的变量,计数器加1,而每次现有对象出了作用域,计数器减1。虽然管理引用计数的开销不大,但是该开销在整个程序的生命周期

2-2tracing算法(标记-清除)
基于tracing算法的垃圾收集也称为标记和清除(mark-and-sweep)垃圾收集器,它所依据的思路是,从栈和静态存储区出发,遍历所有的引用,找到存活的对象,每当找到一个存活的对象,就给该对象设一个标记,当标记工作全部完成时,清理工作才会开始。在清理的过程中,没有标记的对象会被释放。该方式相当慢,在产生少量垃圾和几乎不产生垃圾的情况下速度就很快了。

2.3. compacting算法(标记-整理)
为了解决堆碎片问题,基于compacting的垃圾回收吸收了tracing算法的思想。在清除无用对象之后,算法将所有的对象移到堆的一端,堆的另一端就变成了一个相邻的空闲内存区,收集器会对它移动的所有对象的所有引用进行更新,使得这些引用在新的位置能识别原来的对象。解决了内存碎片的问题(不但进行了清理而且进行了对象的搬运,成本更高)。在基于Compacting算法的收集器的实现中,一般增加句柄和句柄表。

2.4. copying算法
它开始时把堆分成一个对象区和多个空闲区,程序从对象区为对象分配空间,当对象满了,基于coping算法的垃圾回收就扫描活动对象,并将每个活动对象复制到空闲区(使得活动对象所占的内存之间没有空闲间隔),这样空闲区变成了对象区,原来的对象区变成了空闲区,程序会在新的对象区中分配内存。
一种典型的基于coping算法的垃圾回收是stop-and-copy算法,它将堆分成对象区和空闲区域区,在对象区与空闲区域的切换过程中,程序暂停执行。

2.5. generation算法
stop-and-copy垃圾收集器的一个缺陷是收集器必须复制所有的活动对象,这增加了程序等待时间,这是coping算法低效的原因。分代的垃圾回收策略,是基于这样一个事实:不同的对象的生命周期是不一样的。因此,不同生命周期的对象可以采取不同的回收算法,以便提高回收效率。generation算法将堆分成两个或多个,每个子堆作为对象的一代(generation)。1.所有新生成的对象首先都是放在年轻代的。年轻代的目标就是尽可能快速的收集掉那些生命周期短的对象。2.在年轻代中经历了N次垃圾回收后仍然存活的对象,就会被放到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象。3.持久代用于存放静态文件,如Java类、方法等。持久代对垃圾回收没有显著影响,但是有些应用可能动态生成或者调用一些class,在这种时候需要设置一个比较大的持久代空间来存放这些运行过程中新增的类。

3.System.gc()方法
使用System.gc()可以不管JVM使用的是哪一种垃圾回收的算法,都可以请求Java虚拟机进行垃圾回收,值得注意的是,JVM接受这个消息后,并不是立即做垃圾回收(需要抢占CPU资源),而只是对几个垃圾回收算法做了加权,使垃圾回收操作容易发生,或提早发生,或回收较多而已。
尽量避免显示的调用gc,若不针对GC的特点进行设计和编码,就会出现内存驻留等一系列负面影响。此函数建议JVM进行主GC,虽然只是建议而非一定,但很多情况下它会触发主GC,从而增加主GC的频率,也即增加了间歇性停顿的次数。

4. finalize()方法
java垃圾处理器只能释放哪些经由New出来的对象内存,对于其它途径产生的内存,java允许在类中定义finalize()方法,在垃圾回收时候调用finalize(),处理内存,虽然不一定发生,但是当垃圾回收动作发生时,非new内存会被清理。finalize中添加一些非java能够处理的垃圾,例如类似C语言中使用的malloc()函数分配的内存,除非调用free(),否则内存得不到释放,造成泄露。所以,在finalize方法中调用free()方法,(free是C和C++的方法)。当垃圾回收发生时,finalize()函数被调用。绝对不能直接调用finalize(),因为垃圾回收只与内存有关,无论对象是如何创建的,垃圾回收器都会负责释放那些对象占有的内存。
当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。子类重写 finalize 方法,以配置系统资源或执行其他清除。
java虚拟机在未面临内存耗尽的情况下,不会浪费时间去执行垃圾回收以恢复内存的。

作者:Alias_^_^