一、如何确定为垃圾
引用计数法
- 在对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加一;当引用失效时,计数器值就减一,当引用为0,则认为对象可被回收。
- 引用计数不能解决循环引用的问题
根可达算法
-
通过一系列称为"GC Roots"的根对象作为起始节点,根据引用关系向下搜索,搜索过程所走过的路径称为"引用链"(Reference Chain),如果某个对象到GC Roots间没有任何引用链相连,或者用图论的话来说就是从GC Roots到这个对象不可达时,则证明此对象是不可能再被使用的。
-
固定可作为GC Roots的对象包括以下几种:
- 在虚拟机栈(栈帧中的本地变量表)中引用的对象,譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。
- 在方法区中类静态属性引用的对象,譬如Java类的引用类型静态变量。
- 在方法区中常量引用的对象,譬如字符串常量池(String Table)里的引用。
- 在本地方法栈中JNI(即通常所说的Native方法)引用的对象。
- Java虚拟机内部的引用,如基本数据类型对应的Class对象,一些常驻的异常对象(比如NullPointExcepiton、OutOfMemoryError)等,还有系统类加载器。
- 所有被同步锁(synchronized关键字)持有的对象。
- 反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。
JAVA中四种对象的引用
- 强
Object obj = new Object();
只要引用关系在,gc 就不会回收。 - 软
SoftReference<String> softRef=new SoftReference<String>(str);
当内存不足时,会回收str对象,可用于缓存场景。 - 弱
WeakReference<String> abcWeakRef=new WeakReference<String>(str)
,当GC扫描到str时就会回收,对象只会存活到下次GC。 - 虚
PhantomReference
[ˈfæntəm] 必须关联一个引用队列,在回收之前会被加入到引用队列,用来管理堆外内存,虚引用不会对对象存活时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的只是为了能在这个对象被收集器回收时收到一个系统通知。
堆外内存的回收 使用 Unsafe 类可以操作
Unsafe unsafe = Unsafe.getUnsafe(); unsafe.freeMemory();
java
public static void main(String[] args) throws InterruptedException {
ReferenceQueue<Object> referenceQueue = new ReferenceQueue<>();
PhantomReference<Object> softRef=new PhantomReference<Object>(new M(),referenceQueue);
System.gc();
new Thread(()->{
while (true){
Reference<? extends Object> reference2 = referenceQueue.poll();
if(reference2!=null){
System.out.println("虚引用对象被JVM回收了");
}
}
}).start();
}
- 总结:JAVA中的 软、弱、虚引用都可以关联一个引用队列,保证在回收之前做必要的操作,编写和缓存相关的程序可以使用
java
/* 使用引用对象来 引用需要的缓存信息,避免强引用保证GC能够回收 */
public static void main(String[] args) throws InterruptedException {
WeakReference<String> abcWeakRef = new WeakReference<>("这个是是缓存信息");
if(abcWeakRef.get() == null){
//重新获取
}
// 根据业务情况处理
}
强引用 GC 回收的情况:1.线程运行结束,线程运行使用的相关对象。2.方法的局部变量
二、垃圾清除算法
Mark-Sweep (标记清除)
- 两遍扫描,第一次标记,第二次清除
- 容易产生碎片,对象清除后不进行内存整理,内存碎片多
- 存活对象比较多的情况下效率高(比如适合在堆分区的XX区)
Mark-Copying (标记复制)
- 内存空间一分为二,把存活有用的对象重新拷贝到另一块内存区域,然后扫描过的区域全部清除
- 空间浪费
- 需要调整对象
- 适合存活对象较少的情况,只扫描一次,没有碎片
Mark-Compact(标记压缩)
- 扫描找到需要回收的对象,然后移动到某一块区域
- 扫描两次,效率偏低
- 不会产生碎片
- 方便对象分配
- 不会产生内存减半
三、分代收集理论
收集器将Java堆划分出不同的区域,然后将回收对象依据其年龄(年龄即对象熬过垃圾收集过程的次数)分配到不同的区域之中存储,针对不同的区域安排与里面存储对象存亡特征相匹配的垃圾收集算法
- 部分收集(Partial GC):指目标不是完整收集整个Java堆的垃圾收集。
- 新生代收集(Minor GC/Young GC):指目标只是新生代的垃圾收集。
- 老年代收集(Major GC/Old GC):指目标只是老年代的垃圾收集。目前只有CMS收集器会有单独收集老年代的行为。另外请注意"Major GC"这个说法现在有点混淆,在不同资料上常有不同所指,读者需按上下文区分到底是指老年代的收集还是整堆收集。
- 混合收集(Mixed GC):指目标是收集整个新生代以及部分老年代的垃圾收集。目前只有G1收集器会有这种行为。
- 整堆收集(Full GC):收集整个Java堆和方法区的垃圾收集。