JVM垃圾回收(Hotspot)
垃圾判断算法
程序计数器由于不会发生oom,所以不需要垃圾回收。虚拟机栈和本地方法栈随着线程的死亡而释放内存,所以也不需要垃圾回收。只有堆和方法区中内存的生命周期具有不确定性,需要垃圾回收管理。
另外
垃圾回收之前,需要找到垃圾。所以如何判断一个对象是垃圾是第一步。
引用计数算法
在对象中添加一个引用计数器,每当有一个地方引用该对象时,计数器值就加1,当引用失效时,计数器值就减1。当一个对象的引用计数器值为0时,则可以判定该对象是垃圾。
优点:引用计数法实现简单
缺点:
- 需要耗费一点内存去记录对象被引用数
- 存在两个对象相互引用的情况,即这两个对象的引用计数器值至少为1,造成内存泄漏
可达性分析算法
GC Roots (根对象) 定义
- 虚拟机栈中引用的对象
- 方法区中静态变量引用的对象
- 方法区中常量引用的对象
- 本地方法栈中JNI(Native方法)引用的对象
- Java虚拟机内部的引用
- 同步锁持有的对象
总结一下GC Roots的特点:就是被堆外直接引用的堆内对象
跨代引用
Hotspot堆内存是分代的,所以可能出现跨代引用,被跨代引用的堆对象其实也算是一种GC Root。
比如上面A,X,Y都是堆外直接引用的堆内对象,是常规的GC Root
但是B作为新生代对象,他没有被堆外直接引用,但是却被老年代对象X引用了(跨代引用),而老年代X是GC Root,所以B不是垃圾。
但是分代垃圾回收可以只对新生代进行回收(Minor GC),而垃圾回收前,需要查找被垃圾回收内存区域的GC Root,如果只考虑堆外直接引用堆内对象为GC Root的话,那么只有A是GC Root,B是垃圾。但是实际上B不是垃圾,所以我们可以将 代外直接引用的代内对象 也当成一种“分代级别”的GC Root.
那么上面这种情况,B是否就是垃圾了?
整体来看,B确实是垃圾。但是如果只有新生代发生垃圾回收的话,B还是不会被回收,即B不会被当成垃圾。只有X被回收了,B才会被回收。所以我们可以将 代外直接引用的代内对象 也当成代GC Root.
如果存在上面这种情况,X也存在跨代引用,则X也不能被当成垃圾。
如果发生上面这种情况,整体来看B,X都是垃圾,如果只发生老年代的垃圾回收(Majar GC),则X不会被垃圾回收。这样其实就造成了空间浪费。所以在回收老年代前,通常回收一次新生代,这样B就会被回收,之后X也会被回收。
可达性分析算法
从GC Roots(根对象)开始,根据引用关系向下搜索,搜索过程所走过的路径称为”引用链“,如果某个对象到GC Roots间没有任何引用链,则该对象可以判定为垃圾。
优点:两个对象相互引用时,也能被识别为垃圾。不需要对象保存引用计数信息。
缺点:当GC Roots数量过多时,该算法耗时较长。
Java中的四种引用
强引用(Strong Reference)
当对象被一个或一个以上的引用变量引用时,它处于可达状态,不可能被垃圾回收。
软引用(Soft Reference)
软引用需要通过SoftReference类来实现,当一个对象只有软引用时,它有可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存足够时,它不会被系统回收,程序也可使用该对象;当系统内存不足时,系统可能会回收它。软引用通常用于对内存敏感的程序中。
// java -Xmx10m Main
public class Main {
public static void main(String[] args) {
soft();
}
private static void soft(){
SoftReference<byte[]> sf = new SoftReference<>(new byte[1024 * 1024 * 6]);
System.out.println("======");
SoftReference<byte[]> sf1 = new SoftReference<>(new byte[1024 * 1024 * 6]);
System.out.println("======");
System.out.println(sf.get());//null
System.out.println(sf==null);//false
System.out.println(sf1.get());//[B@1b6d3586
}
}
当一次Full GC后,堆内存任然不足时,会再次进行一次Full GC,将new byte[1024 * 1024 * 6]对象所占内存回收。
此时只剩下软引用对象new SoftReference(),因为它被sf强引用,所以无法被垃圾回收。
此时我们可以通过以下方式回收软引用对象new SoftReference()
if(sf.get()==null){
sf = null;
}
也可以借助另一种方式,SoftReference对象本身可以绑定一个引用队列ReferenceQueue对象,当软引用指向的对象被回收了,则软引用会被自动加入绑定的引用队列中。这种情况适用于要回收的软引用对象较多时。
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args){
soft();
}
private static void soft(){
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
List<SoftReference<byte[]>> list = new ArrayList<>();
list.add(new SoftReference<>(new byte[1024 * 1024],queue));
list.add(new SoftReference<>(new byte[1024 * 1024 * 2],queue));
list.add(new SoftReference<>(new byte[1024 * 1024 * 3],queue));
list.add(new SoftReference<>(new byte[1024 * 1024 * 6],queue));
Reference<? extends byte[]> poll = queue.poll();
while (poll!=null){
list.remove(poll);
poll = queue.poll();
}
System.out.println(list.size());// 1
list.forEach(t-> System.out.println(t.get()));// [B@404b9385
}
}
弱引用(Weak Reference)
弱引用需要通过WeakReference类来实现,弱引用和软引用很想,但弱引用级别更低。对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存。
弱引用具体使用和软引用差不多,只是二者引用对象的回收时机不一样,只要发生垃圾回收,无论内存是否充足,都会回收弱引用引用的对象。
虚引用(Phantom Reference)
虚引用通过PhantomReference类实现,虚引用完全类似于没有引用。虚引用对对象本身没有太大影响,对象甚至感觉不到虚引用的存在。如果一个对象只有虚引用时,那么它和没有引用的效果大致相同。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和引用队列联合使用。
虚引用引用的对象不会因为内存不足而被垃圾回收,虚引用引用的对象和没有虚引用引用的对象效果相同。即虚引用并不是为了应对内存紧张场景。
虚引用必须配合引用队列使用,因为虚引用引用的对象无法通过get()获取,所以无法判断虚引用引用的对象是否已经被回收。只能通过检查是否虚引用本身是否入队,若入队,则说明虚引用引用的对象已经被回收。
由于虚引用引用的对象被回收后,会自动入队,所以只要检查队中是否有虚引用就可以跟踪对象被垃圾回收的状态。
public class Main {
public static class Test{
@Override
protected void finalize() throws Throwable {
System.out.println("finalize方法被调用");
super.finalize();
}
}
public static void main(String[] args){
phantom();
}
private static void phantom(){
ReferenceQueue<Test> queue = new ReferenceQueue<>();
Test test = new Test();
PhantomReference<Test> pr = new PhantomReference<>(test, queue);
test = null;
while (true){
Reference<? extends Test> poll = queue.poll();
if (poll!=null){
System.out.println(poll==pr);
break;
} else {
System.out.println("=");
System.gc();
}
}
}
垃圾回收算法
标记-清除
标记-整理
标记-复制
分代垃圾回收
堆内存分代
分代垃圾回收流程
相关VM参数
垃圾回收器
串行垃圾回收器
吞吐量垃圾回收器
响应时间垃圾回收器
G1垃圾回收器
垃圾回收调优
调优目标
代码审查
新生代调优
老年代调优
相关文章
- jvm的垃圾回收机制_垃圾回收厂
- java 针对jvm的面试题_24个Jvm面试题总结及答案
- JVM内存与垃圾回收篇第8章堆
- JVM内存与垃圾回收篇第3章运行时数据区概述及线程
- Java:JVM垃圾回收机制[通俗易懂]
- JVM 学习笔记(2):垃圾回收GC
- jvm垃圾回收算法有哪些_java垃圾回收算法几种
- 【JVM内存区域】
- jvm的垃圾回收_垃圾回收机制原理
- jvm 垃圾回收器比较_jvm有哪些垃圾回收算法
- 神操!精选JVM垃圾回收机制全面分析,聊聊你眼中的JVM
- JVM又曾放过谁,垃圾终将被回收
- JVM的4种垃圾回收算法、垃圾回收机制与总结[通俗易懂]
- jvm的垃圾回收算法_jvm默认的垃圾回收器
- JVM垃圾回收器详解:不同的复制算法比较及对程序员的启迪
- JVM垃圾回收器详解:串行回收新生代内存管理垃圾回收的触发机制
- 一文学会JVM垃圾回收器详解:串行回收,新生代内存管理内存分配
- JVM – 垃圾回收篇笔记
- jvm调优、垃圾回收机制等详解编程语言
- 参数Linux下修改JVM内存参数的方法(linux修改jvm内存)
- Linux查看JVM内存使用情况指南(linux查看jvm内存)
- 参数Linux下修改JVM参数的指南(linux修改jvm)
- JDK文档Oracle全面解读JVM(jvm文档oracle)
- Oracle控制下的JVM开启新的技术时代(jvm受oracle控制)