Salmon的全栈知识 Salmon的全栈知识
首页
  • JavaSE
  • JavaWeb
  • Spring生态
  • JUC
  • JVM
  • Netty
  • Java各版本特性
  • 23种设计模式
  • Maven
  • Java常用框架
  • Dubbo
  • OpenFeign
  • Nacos
  • Zookeeper
  • Sentinel
  • Seata
  • Gateway
  • Go基础
  • Gin
  • SQL数据库

    • MySQL
    • Oracle
  • NoSQL数据库

    • Redis
    • MongoDB
    • ElasticSearch
  • 消息中间件

    • RabbitMQ
    • RocketMQ
    • Kafka
    • ActiveMQ
    • MQTT
    • NATS
  • 网关中间件

    • Nginx
  • Linux
  • Docker
  • Git
  • K8s
  • Solidity
  • Java
  • 计算机网络
  • 操作系统
GitHub (opens new window)
首页
  • JavaSE
  • JavaWeb
  • Spring生态
  • JUC
  • JVM
  • Netty
  • Java各版本特性
  • 23种设计模式
  • Maven
  • Java常用框架
  • Dubbo
  • OpenFeign
  • Nacos
  • Zookeeper
  • Sentinel
  • Seata
  • Gateway
  • Go基础
  • Gin
  • SQL数据库

    • MySQL
    • Oracle
  • NoSQL数据库

    • Redis
    • MongoDB
    • ElasticSearch
  • 消息中间件

    • RabbitMQ
    • RocketMQ
    • Kafka
    • ActiveMQ
    • MQTT
    • NATS
  • 网关中间件

    • Nginx
  • Linux
  • Docker
  • Git
  • K8s
  • Solidity
  • Java
  • 计算机网络
  • 操作系统
GitHub (opens new window)
npm

(进入注册为作者充电)

  • JVM基础

    • 初识JVM
    • 字节码文件详解
    • 运行时数据区
    • 垃圾回收
  • JVM实战

    • 内存调优
    • GC调优
    • 性能调优
  • JVM高级

    • GraalVM
    • 新一代的GC
    • 揭秘Java工具
  • JVM原理

    • 栈上的数据存储
    • 对象在堆上是如何存储的?
    • 方法调用的原理
    • 异常捕获的原理
    • JIT即时编译器
    • 垃圾回收器原理
  • JVM面试

    • 什么是JVM?
    • 了解过字节码文件的组成吗?
    • 说一下运行时数据区
    • 哪些区域会出现内存溢出,会有什么现象?
    • JVM在JDK6-8之间在内存区域上有什么不同
    • 类的生命周期
    • 什么是类加载器?
    • 什么是双亲委派机制
    • 如何打破双亲委派机制
    • Tomcat的自定义类加载器
    • 如何判断堆上的对象没有被引用??
    • JVM 中都有哪些引用类型?
    • ThreadLocal中为什么要使用弱引用?
    • 有哪些常见的垃圾回收算法?
    • 有哪些常用的垃圾回收器?
    • 如何解决内存泄漏问题?
    • 常见的JVM参数?
  • 《JVM》笔记
  • JVM面试
Salmon
2024-03-12

JVM 中都有哪些引用类型?

  • 关联课程内容

    • 基础篇-软引用
    • 基础篇-弱虚终结器引用
  • 回答路径

    • 强引用

    • 软引用

    • 弱引用

    • 虚引用

    • 终结器引用

强引用,JVM中默认引用关系就是强引用,即是对象被局部变量、静态变量等GC Root关联的对象引用,只要这层关系存在,普通对象就不会被回收。

package q5reference;

import java.util.ArrayList;
import java.util.List;

//-Xmx10m -verbose:gc
public class StrongReferenceDemo {
    private static int _1MB = 1024 * 1024 * 1;
    public static void main(String[] args) {
        List<Object> objects = new ArrayList<>();

        while (true){
            byte[] bytes = new byte[_1MB];
            //强引用
            objects.add(bytes);
        }
    
    }
}

强引用的对象不会被回收掉,所以会出现内存溢出:

img

软引用,软引用相对于强引用是一种比较弱的引用关系,如果一个对象只有软引用关联到它,当程序内存不足时,就会将软引用中的数据进行回收。软引用主要在缓存框架中使用。

  package q5reference;

import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.List;

//-Xmx10m -verbose:gc
public class SoftReferenceDemo {
    private static int _1MB = 1024 * 1024 * 1;
    public static void main(String[] args) {
        List<SoftReference> objects = new ArrayList<>();

        for (int i = 0; i < 10; i++) {
            byte[] bytes = new byte[_1MB];
            //软引用
            SoftReference<byte[]> softReferences = new SoftReference<byte[]>(bytes);
            //软引用对象放入集合中
            objects.add(softReferences);
    
            System.out.println(i);
        }
    
        //有一部分对象因为内存不足,已经被回收了
        for (SoftReference softReference : objects) {
            System.out.println(softReference.get());
        }
    
    }
}

内存不足时,触发了垃圾回收。

img

所以前几个对象已经被回收了,但是后边几个会保留下来:

img

弱引用,弱引用的整体机制和软引用基本一致,区别在于弱引用包含的对象在垃圾回收时,不管内存够不够都会直接被回收,弱引用主要在ThreadLocal中使用。

package q5reference;

import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

//-Xmx100m -verbose:gc
public class WeakReferenceDemo {
    private static int _1MB = 1024 * 1024 * 1;
    public static void main(String[] args) {
        List<WeakReference<byte[]>> objects = new ArrayList<>();
        System.out.println("-------------------");
        for (int i = 0; i < 10; i++) {
            byte[] bytes = new byte[_1MB];
            //弱引用
            WeakReference<byte[]> weakReference = new WeakReference<byte[]>(bytes);
            //弱引用对象放入集合中
            objects.add(weakReference);
        }

        //设置一个强引用
        byte[] last = objects.get(9).get();
    
        //手动执行一次垃圾回收,弱引用对象只要没有强引用,就会被直接回收
        System.gc();
        System.out.println("-------------------");
        for (WeakReference softReference : objects) {
            System.out.println(softReference.get());
        }
    }
}

手动触发垃圾回收之后,前9个都被回收了,最后一个由于存在强引用会保留下来:

img

虚引用(幽灵引用/幻影引用),不能通过虚引用对象获取到包含的对象。虚引用唯一的用途是当对象被垃圾回收器回收时可以接收到对应的通知。直接内存中为了及时知道直接内存对象不再使用,从而回收内存,使用了虚引用来实现。

package q5reference;

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

//-Xmx10m -verbose:gc
public class PhantomReferenceDemo {
    private static int _1MB = 1024 * 1024 * 1;
    public static void main(String[] args) {
        ReferenceQueue<byte[]> queue = new ReferenceQueue();
        byte[] bytes = new byte[_1MB];
        MyPhantomReference phantomReference = new MyPhantomReference(bytes, queue);

        //去除强引用
        bytes = null;
        //执行垃圾回收
        System.gc();
    
        //查看队列
        MyPhantomReference ref = (MyPhantomReference) queue.poll();
        //清理
        ref.clean();
    
    }
}

class MyPhantomReference extends PhantomReference<byte[]>{
    public void clean(){
        System.out.println("清理...");
    }

    public MyPhantomReference(byte[] referent, ReferenceQueue<byte[]> q) {
        super(referent, q);
    }
}

对象回收之后虚引用对象会进入队列,这样就可以获取对象执行指定的方法了。

img

终结器引用,终结器引用指的是在对象需要被回收时,终结器引用会关联对象并放置在Finalizer类中的引用队列中,在稍后由一条由FinalizerThread线程从队列中获取对象,然后执行对象的finalize方法,在对象第二次被回收时,该对象才真正的被回收。

package q5reference;

import java.io.IOException;

//-verbose:gc
public class FinalizeReferenceDemo {
    public static void main(String[] args) throws IOException {
        Demo demo = new Demo();
        demo = null;

        System.gc();
    
        System.in.read();
    }
}


class Demo{
    @Override
    protected void finalize() throws Throwable {
        System.out.println("触发finalize");
        super.finalize();
    }
}

对象回收时,触发了finalize方法:

img

上次更新: 2025/03/09, 18:29:07
如何判断堆上的对象没有被引用??
ThreadLocal中为什么要使用弱引用?

← 如何判断堆上的对象没有被引用?? ThreadLocal中为什么要使用弱引用?→

Theme by Vdoing | Copyright © 2022-2025 Salmon's Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式