Java虚拟机--垃圾回收与内存分配

来源:互联网 发布:数据库删除语句 编辑:程序博客网 时间:2024/05/29 15:30

1、可回收对象的判定

用来判断哪些对象可以被回收

1)引用计数法

给对象添加一个引用计数器,每当它被一引用时,计数器加1;当引用失效时,计数器减1。计数器为0,就表示可以回收了。

缺点:难以解决两个对象之间的循环引用

2)可达性分析算法

通过一系列称为“GC Roots”的对象作为起始点,当一个对象到GC Roots没有任何引用链(Reference Chain)时,就证明此对象不可达。

如下图,虽然object5、object6、object7互相有关联,但是到GC Roots没有引用链,它们就会被判定为可回收对象

在Java中,可以作为GC Roots的对象有以下四种:
1. 虚拟机栈(栈中的本地变量表)中引用的对象
2. 方法区中类静态属性引用的对象
3. 方法区中常量引用的对象
4. 本地方法栈中的JNI引用的对象

3)Java的四种引用类型

强、软、弱、虚,强度由高到低
1. 强引用:一般默认为强引用
2. 软引用:用来描述一些还有用但非必须的对象。在系统将要发生内存溢出异常之前,才会把这些对象列入回收范围。
3. 弱引用:用来描述非必须对象。被弱引用关联的对象只能生存到下一次垃圾回收之前。也就是,每次垃圾回收时,弱引用的对象都会被回收。
4. 虚引用:一般没有用。唯一的目的就是能在这个对象被垃圾回收器回收时收到一个系统通知。

4)对象的自救



一个对象想要自救,就必须重写finalize方法,并且在finalize方法中,把自己的引用this传递出去,比如说上面的把this引用赋给类变量或对象变量,这样他就重新被链上引用链了。

但是:一个对象的finalize方法只会被系统自动调用一次,如果对象面临下次回收或者已经逃脱过一次,那么finalize自救就会失效。

:建议不使用finalize方法。它所实现的功能。try-finally或者其他方式都可以做得更好·

5)回收方法区

需要判定什么才是“无用的类”,至少满足以下三点:
1. 该类的所有实例已经被回收
2. 加载该类的ClassLoader已经被回收
3. 该类的Class对象没有在任何地方被引用,无法在任何地方通过反射访问该类方法。

2、垃圾回收算法

1)标记-清除算法

首先标记处所有需要回收的对象,在标记完成后统一回收所有被标记的对象。

缺点
1. 时间:标记和清除两个过程效率不高
2. 空间:标记清除后会产生大量不连续的内存碎片。这样会导致分配
大对象时,因为找不到足够的连续空间,而提前出发垃圾回收

2)复制算法(新生代)

将可用内存分为大小相等的两块,每次只使用其中一块。当这一块内存用完之后,就将还活着的对象复制到另一块内存中去,然后把已经使用过的内存空间一次清除掉

缺点:可用内存缩小到一半
改进
当前虚拟机采用复制算法来回收新生代,新生代中的对象98%都是“朝生夕死”,并不需要按照1:1来划分内存空间,而是把内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中的一块Survivor(From Survivor)。当回收时,将Eden和Survivor(From Survivor)中还存活着的对象一次性拷贝到另一块Survivor(To Survivor)空间上,最后清理掉Eden和刚才使用过的Survivor的空间
Eden:From Survivor:To Survivor=8:1:1,每次新生代可使用的内存空间为90%。因为没法保证每次回收内存都只有不多于10%的对象存活,所以要采用老年代内存担保机制。

3)标记-整理法(老年代)

因为复制收集算法适用于对象存活率较低的新生代。而在老年代,对象存活率比较高,采用复制收集算法效率太低,因此采用标记-整理法较为合适:让所有存活对象都向一端移动,然后直接清理掉端边界以外的内存

4)分代收集算法

当前虚拟机都采用“分代收集”(Generational Collection)算法,一般把Java堆分为新生代老年代,根据各个年代的特点采用合适的收集算法。新生代采用复制算法, 老年代采用标记清除或标记整理法。

3、内存分配与回收策略

一般来讲内存分配就是在堆上分配,对象主要分配在Eden区、若启动了本地线程分配缓冲,将按线程优先在TLAB上分配,少数情况也可能直接分配在老年代中。

1)对象优先在Eden上分配

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

  • Minor GC:新生代GC
  • Major GC/Full GC:老年代GC

2)大对象直接进入老年代

最典型的大对象就是,长字符串及数组(byte[]数组就是典型的大对象)。使用-XX:PretenureSizeThresgold参数设定限值,新生对象大小超过此值直接进入老年代

3)长期存活对象进入老年代

虚拟机为每一个对象定义一个对象年龄(Age)计数器。如果对象出生在Eden区并且第一次MinorGC后仍然存活,并且能被Survivor区容纳,将被移动到Survivor区,并将对象年龄加1。对象在Survivor区每熬过一次MinorGC,年龄就加1,当它的年龄达到一定程度(默认15),就会晋升到老年代,晋升的阈值通过-XX:MaxTenuringThreshold来设置。

4)动态对象年龄判断

5)空间分配担保


1 0