JVM 学习(2)—简单理解Java 四大引用(强、软、弱、虚)

news2024/9/23 19:24:22

一、Java 引用概述

Java 中出现四种引用是为了更加灵活地管理对象的生命周期,以便在不同场景下灵活地处理对象的回收问题。不同类型的引用在垃圾回收时的处理方式不同,可以用来实现不同的垃圾回收策略。Java 目前将其分成四类,类图如下:

在这里插入图片描述

Java 技术允许使用 finalize() 方法在垃圾收集器将对象从内存中清除出去之前做必要的清理工作。

    @Deprecated(since="9")
    protected void finalize() throws Throwable { }
}

传统的 finalize() 方法在 Java 9 中已经被标记为废弃(deprecated),而且在 Java 11 中已经被删除(推荐使用 Cleaner是 Java 新一代的内存回收处理机制)。这是因为 finalize() 方法存在许多问题,包括但不限于以下几点:

  1. finalize() 方法并不能保证被及时地调用,这可能会导致内存泄漏等问题。
  2. finalize() 方法的执行时机是不确定的,可能会导致程序的不可预测行为。
  3. finalize() 方法是被 JVM 调用,手动调用容易出错,不够灵活。

=>Cleaner 的执行过程,如下:

在 JDK9 中,Java 提供了一种新的机制来代替传统的 finalize() 方法来清理对象,就是 Cleaner。Cleaner 是一个 Java 类,用于注册需要进行清理的对象以及相关的清理方法。它使用了本地内存技术,可以在 Java 对象释放后及时进行清理。

当一个 Java 对象不再被引用时,其所占用的内存并不会立即释放,而是会被标记为“可回收”状态。JVM 会在后台开启一个线程,定期扫描这些“可回收”对象,清理其中已经没有引用的对象。Cleaner 就是在这个扫描过程中对于对象的清理操作。

使用 Cleaner 机制时,开发人员需要创建一个 Cleaner 对象,并使用该对象对需要清理的对象进行注册,同时定义一个清理方法。当被注册的对象被回收时,JVM 会在后台的扫描过程中调用注册的清理方法,对对象进行清理操作。相较于传统的 finalize() 方法,Cleaner 机制的清理操作更加灵活,可以在多种情况下触发,并且不会受到 finalize() 方法的不稳定性的影响。

下面是一个使用 Cleaner 的例子:

public class Resource implements AutoCloseable {
    private static final Cleaner cleaner = Cleaner.create();

    private final Cleaner.Cleanable cleanable;
    private ByteBuffer buffer;

    public Resource(int size) {
        buffer = ByteBuffer.allocate(size);
        cleanable = cleaner.register(this, new ResourceCleaner(buffer));
    }

    @Override
    public void close() throws Exception {
        cleanable.clean();
    }

    private static class ResourceCleaner implements Runnable {
        private ByteBuffer buffer;

        public ResourceCleaner(ByteBuffer buffer) {
            this.buffer = buffer;
        }

        @Override
        public void run() {
            System.out.println("Cleaner is cleaning the resource");
            buffer = null;
        }
    }
}

在上面的例子中,Resource 类使用了 Cleaner 来释放资源。Resource 类中的 buffer 对象在实例化时会被分配内存。当使用完 Resource 实例时,需要调用 close() 方法来释放资源。close() 方法调用了 Cleaner.Cleanable 的 clean() 方法来触发资源的释放。同时,ResourceCleaner 类实现了 Runnable 接口,run() 方法被用来进行资源的清理操作。在这个例子中,资源的清理操作是将 buffer 设置为 null。这样,一旦实例被垃圾收集器回收,clean() 方法就会自动被调用,从而释放资源。

  1. 一般来说,实现了 CloseableAutoCloseable 接口的类,在使用完后都需要手动调用 close() 方法来释放资源。但是有些情况下,JVM 会自动调用 close() 方法,比如在 try-with-resources 语句中,当 try 代码块执行完后,JVM会自动调用相应资源的 close() 方法来释放资源,而无需手动调用。但是,如果在 try-with-resources 语句中使用了多个资源,需要注意它们的释放顺序,确保后打开的资源先关闭,以避免可能出现的资源泄露问题。
     
  2. ResourceCleaner 是由 JVM 的垃圾回收器自动触发的,当一个对象不再被引用时,它的 clean() 方法就会被自动调用。具体来说,当垃圾回收器扫描到一个对象时,如果这个对象实现了 Cleaner.Cleanable 接口,那么垃圾回收器会把它注册到一个全局的 ReferenceHandler 队列中,这个队列中的线程会定期触发这些对象的 clean() 方法。这个过程是由 JVM 自动进行的,程序员无需手动触发。

二、强、软、弱、虚引用简单介绍

1、强引用(Reference 默认)

是最常见的引用类型,也是默认的引用类型。当内存不足,JVM 开始垃圾回收,对于强引用的对象,就算是出现 OOM 也不会对该对象进行回收,死都不放。

强引用只要还指向一个对象,就表示对象还活着,垃圾收集器不会碰这些对象。在 Java 最常见的就是把一个对象赋值给一个引用变量,这个引用变量就是一个强引用。当一个对象被强引用引用时,它处理可达状态,它不可能被垃圾回收器回收,即使该对象后面永远都不会被使用,JVM 也不会进行回收。因此强引用是造成 Java 内存泄露的主要原因之一。

比如下面这个例子:

class MyObject {

    public byte[] buff = new byte[1000 * 1000 * 3];
    @Override
    protected void finalize() throws Throwable {
        System.out.println(">>>>>>调用 finalize 清理资源...");
    }
}
public class ReferenceDemo {

    public static void main(String[] args) {

        MyObject myObject = new MyObject()System.out.println("gc before myObject = " + myObject);
        try {
            byte[] bytes = new byte[1000 * 1000 * 8];
            System.gc();
        } finally {
            System.out.println("gc after myObject = " + myObject);
        }
    }

首先通过 -Xms10m -Xmx10m 命令把 JVM 堆内存修改成 10M 方便测试,然后定义一个类 MyObject 里面分配一个数组占用堆内存 3M 空间,然后再 main() 方法中又分配一个 8M 大小数组,这样加起来肯定超过 10M 堆内存,会 OOM,但是可以手动调用 System.gc 触发垃圾回收,但是这里并不是回收(finalize() 方法肯定不会被调用),因为这里是属于强引用,就算 OOM 也不会被回收。

在 JVM 准备垃圾回收之前会先去调用 finalize() 方法做一些清理工作,所以只需要覆写该方法演示效果(工作中不会这样干),然后手动调用 System.gc() 让他触发 finalize() 方法调用。
垃圾回收线程是一个后台守护线程,定时在回收垃圾,这里提供 System.gc() 方便手动触发 GC 垃圾回收

效果如下:

gc before myObject = main.juc.MyObject@5ebec15
gc after myObject = main.juc.MyObject@5ebec15
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
	at main.juc.ReferenceDemo.main(ReferenceDemo.java:27)

那么怎么可以让他回收呢?可以用最简单的方式,将强引用直接赋值 null。修改之后的代码如下:

class MyObject {

    public byte[] buff = new byte[1000 * 1000 * 1];
    @Override
    protected void finalize() throws Throwable {
        System.out.println(">>>>>>调用 finalize 清理资源...");
    }
}
public class ReferenceDemo {

    public static void main(String[] args) {

        MyObject myObject = new MyObject();

        System.out.println("gc before myObject = " + myObject);
        try {
            byte[] bytes = new byte[1000 * 1000 * 7];
            myObject = null;
            System.gc();
            System.out.println(">>>>>>垃圾回收..."+myObject);
        } finally {

            System.out.println("gc after myObject = " + myObject);
        }
    }
}

输出结果如下:

gc before myObject = main.juc.MyObject@5ebec15
>>>>>>调用 finalize 清理资源...
>>>>>>垃圾回收...null
gc after myObject = null

Process finished with exit code 0

myObject = null 可以帮助 JVM 进行垃圾回收。

2、软引用(SoftReference 装饰对象)

如果一个对象只具有软引用,则在系统内存不足时,垃圾回收器会尝试回收该对象。这种引用类型通常用于缓存中,以便在内存不足时自动释放缓存,可以通过 SoftReference 类来创建软引用。例子如下:

class MyObject {

    public byte[] buff = new byte[1000 * 1000 * 3];
    @Override
    protected void finalize() throws Throwable {
        System.out.println(">>>>>>调用 finalize 清理资源...");
    }
}
public class ReferenceDemo {

    public static void main(String[] args) {

        SoftReference<MyObject> softReference = new SoftReference<>(new MyObject());
        try {

            System.out.println("gc before myObject = " + softReference.get());
            byte[] buf = new byte[1000*1000*3];
            System.gc();
            Thread.sleep(3000);

            System.out.println("gc after myObject = " + softReference.get());
        } finally {
            System.out.println(">>>>>>内存不够 myObject="+softReference.get());
        }

    }
}

首先通过 -Xms10m -Xmx10m 命令把 JVM 堆内存修改成 10M 方便测试,然后把需要标识为软引用的对象通过 SoftReference 关键字进行包装。MyObject 类中首先分配一个数组,数组占堆内存大约 3M 空间,mian() 方法 3M 空间,远远还没有超过 10M 分配内存大小,然后手动调用 gc,效果如下:

gc before myObject = main.juc.MyObject@5ebec15
gc after myObject = main.juc.MyObject@5ebec15
>>>>>>内存不够 myObject=main.juc.MyObject@5ebec15

明显在内存足够的情况下,不会触发 GC 垃圾回收。现在将代码修改成下面这样,代码如下:

class MyObject {

    public byte[] buff = new byte[1000 * 1000 * 3];
    @Override
    protected void finalize() throws Throwable {
        System.out.println(">>>>>>调用 finalize 清理资源...");
    }
}
public class ReferenceDemo {

    public static void main(String[] args) {

        SoftReference<MyObject> softReference = new SoftReference<>(new MyObject());
        try {

            System.out.println("gc before myObject = " + softReference.get());
            byte[] buf = new byte[1000*1000*7];
            System.gc();
            Thread.sleep(3000);

            System.out.println("gc after myObject = " + softReference.get());
        } finally {
            System.out.println(">>>>>>内存不够 myObject="+softReference.get());
        }
    }
}

效果如下:

gc before myObject = main.juc.MyObject@5ebec15
gc after myObject = null
>>>>>>内存不够 myObject=null

明显在内存不足时,会触发 GC 垃圾回收,将 SoftReference 引用的内存空间释放,这个就是 SoftReference 引用的好处。内存足,不回收,反之,回收。

3、弱引用( WeakReference 装饰对象)

如果一个对象有弱引用,只要当垃圾回收器扫描到这个对象时,无论内存是否充足,都会回收该对象。弱引用通常用于实现缓存、内存敏感的高速缓存、监视器等功能。可以通过 WeakReference 类来创建弱引用。通过 -Xms10m -Xmx10m 命令把 JVM 堆内存修改成 10M 方便测试。代码如下:

通过 -Xms10m -Xmx10m 命令把 JVM 堆内存修改成 10M 方便测试。

class MyObject {

    public byte[] buff = new byte[1000 * 1000 * 3];
    @Override
    protected void finalize() throws Throwable {
        System.out.println(">>>>>>调用 finalize 清理资源...");
    }
}
public class ReferenceDemo {

    public static void main(String[] args) {
        WeakReference<MyObject> softReference = new WeakReference<>(new MyObject());
        try {
            System.out.println("gc before myObject = " + softReference.get());
            System.gc();
            Thread.sleep(3000);
            System.out.println("gc after myObject = " + softReference.get());
        } catch (Exception e) {
            System.out.println(">>>>");
        } finally {
            System.out.println(">>>>>>内存不够 myObject="+softReference.get());
        }
    }
}

输出结果如下:

gc before myObject = main.juc.MyObject@5ebec15
>>>>>>调用 finalize 清理资源...
gc after myObject = null
>>>>>>内存不够 myObject=null

看到不管内存是否还有,只要 GC 扫描(System.gc() 可以出发 GC 扫描)到就会直接被回收。

补充:通过弱引用举个实际案例如下:

import java.lang.ref.WeakReference;
import java.util.HashMap;
import java.util.Map;

public class ImageLoader {
    private Map<String, WeakReference<Image>> cache = new HashMap<>();

    public Image loadImage(String filename) {
        Image image = null;
        WeakReference<Image> ref = cache.get(filename);

        if (ref != null) {
            image = ref.get();
        }

        if (image == null) {
            image = loadImageFromDisk(filename);
            cache.put(filename, new WeakReference<>(image));
        }

        return image;
    }

    private Image loadImageFromDisk(String filename) {
        // load image from disk
        return null;
    }
}

在这个示例中,ImageLoader 类使用一个 HashMap 来缓存已加载到内存中的图片,每个图片对应一个弱引用。当需要加载图片时,首先从缓存中查找图片是否已经加载到内存中,如果是,则返回弱引用所引用的图片对象;否则,从磁盘中加载图片,并将其添加到缓存中,同时返回图片对象。

由于缓存中的每个图片对象都是弱引用,因此在内存不足时,垃圾回收器会自动回收这些图片对象所占用的内存空间,从而实现内存管理。

补充:ThreadLocal 中为什么需要使用弱引用?

ThreadLocal 使用弱引用是为了防止内存泄漏。由于 ThreadLocalMap 是存在 Thread 中的,如果使用强引用,一旦ThreadLocal 对象被回收,它在 ThreadLocalMap 中对应的 Entry 对象也不会被回收,这样就会导致 Entry 对象中的value对象长时间得不到回收,最终导致内存泄漏。
使用弱引用可以让 ThreadLocal 对象被回收后,Entry 对象中的 value 对象在下一次 ThreadLocalMap 的操作时被顺便回收,从而避免了内存泄漏的问题。

4、虚引用(PhantomReference 装饰对象)

也称为幽灵引用,如果一个对象只具有虚引用,那么这个对象就和没有引用一样,在任何时候都可能被垃圾回收器回收。虚引用通常用于跟踪对象被垃圾回收器回收的状态,可以通过 PhantomReference 类来创建虚引用。

顾明思义,就是形同虚设,与其他几种引用都不同,虚拟引用并不会决定对象的声明周期。

如果一个对象仅持有虚引用,那么他就和没有任何引用一样,在任何时候都可能被垃圾回收器回收,它不能单独实用也不能通过虚引用访问对象,虚引用必须和引用队列 ReferenceQueue 一起使用

虚引用的主要作用就是跟踪对象被垃圾回收的状态。仅仅是提供了一种确保对象被 finalize() 以后,可以做某些操作。PhantomReference.get() 方法总是会返回 null,因此无法访问对应的引用对象。比如:在这个对象被垃圾收集器回收的时候收一个系统通知或者做进一步的处理。举个例子:

通过 -Xms10m -Xmx10m 命令把 JVM 堆内存修改成 10M 方便测试。

class MyObject {

    @Override
    protected void finalize() throws Throwable {
        System.out.println(">>>>>>调用 finalize 清理资源...");
    }
}

public class ReferenceDemo {

    public static void main(String[] args) throws InterruptedException {
        ReferenceQueue<MyObject> queue = new ReferenceQueue<>();
        PhantomReference<MyObject> softReference = new PhantomReference<>(new MyObject(), queue);
        List<byte[]> list = new ArrayList<>();
        new Thread(() -> {
            while (true) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                list.add(new byte[1000 *  1000  * 1]);
                System.out.println("softReference.get() = " + softReference.get());
            }
        }).start();

        new Thread(() -> {
            while (true) {
                if (queue.poll() != null) {
                    System.out.println(">>>>>>引用队列有值啦...");
                }
            }
        }).start();

        Thread.sleep(8000);
    }
}

输出结果如下:

softReference.get() = null
softReference.get() = null
softReference.get() = null
softReference.get() = null
softReference.get() = null
softReference.get() = null
>>>>>>调用 finalize 清理资源...
softReference.get() = null
softReference.get() = null
>>>>>>引用队列有值啦...java.lang.ref.PhantomReference@5c4efc23>>>type=class java.lang.ref.PhantomReference
softReference.get() = null

Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "Thread-0"
Java HotSpot(TM) 64-Bit Server VM warning: Exception java.lang.OutOfMemoryError occurred dispatching signal SIGINT to handler- the VM may need to be forcibly terminated

上述代码中,通过 PhantomReference 创建出虚引用,并且传入 ReferenceQueue 队列。线程1不断地在堆内存中申请空间,每次 1M 大小,因为虚引用包裹的对象,通过 softReference.get() 都是返回 null。线程1一直这样添加,直到内存不足时,虚引用对象会被放到 ReferenceQueue 队列中,ReferenceQueue 队列可以帮助我们在虚引用对象被回收时及时得到通知,从而进行必要的清理工作。其实就是表示这个虚引用在死之前想要留一些遗言,人类就可以监控到这个引用队列看到谁在死之前还有遗言,去帮他实现一下。

补充:

虚引用(Phantom Reference)是 Java 引用类型中最弱的一种,虚引用对象被GC回收的时候会放入一个由 ReferenceQueue管理的队列中。

虚引用的一个常见用途是管理 DirectByteBuffer 对象,它可以让我们对 DirectByteBuffer 对象的回收时间进行监控,一旦 DirectByteBuffer 对象被GC回收,就可以通知我们进行必要的资源释放操作,比如释放内存映射文件等。

以下是一个使用虚引用管理DirectByteBuffer的简单示例代码:

import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.nio.ByteBuffer;

public class DirectByteBufferTest {
    private static final int BUFFER_SIZE = 1024 * 1024;
    private static final int MAX_BUFFERS = 10;
    private static final ByteBuffer[] buffers = new ByteBuffer[MAX_BUFFERS];
    private static final ReferenceQueue<ByteBuffer> queue = new ReferenceQueue<>();

    static {
        for (int i = 0; i < MAX_BUFFERS; i++) {
            buffers[i] = ByteBuffer.allocateDirect(BUFFER_SIZE);
            new PhantomReference<>(buffers[i], queue);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < MAX_BUFFERS; i++) {
            ByteBuffer buffer = buffers[i];
            System.out.println("buffer " + i + ": " + buffer);
            buffers[i] = null;
            buffer = null;
        }

        System.gc();
        Thread.sleep(1000);

        Reference<? extends ByteBuffer> ref;
        while ((ref = queue.poll()) != null) {
            ByteBuffer buffer = ref.get();
            System.out.println("buffer " + buffer + " is released");
            // Release resources here
        }
    }
}

该示例程序通过创建10个 DirectByteBuffer 对象并将它们的虚引用对象放入队列中来管理这些 DirectByteBuffer 对象。在程序运行的过程中,首先会打印出10个 DirectByteBuffer 对象的地址,然后将它们的引用全部置为 null,这样它们就可以被GC回收。程序接着调用 System.gc() 来触发一次垃圾回收,然后调用 Thread.sleep() 让程序睡眠1秒钟等待垃圾回收完成。最后程序从 ReferenceQueue 中取出所有被回收的 DirectByteBuffer 对象的虚引用,进行必要的资源释放操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/377078.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

彻底搞懂inner join,left join,right join

1.inner join A inner join B where 条件&#xff0c; 对于A表中的每一行都会去B表的所有行去查找&#xff0c;去匹配&#xff0c;符合条件的就将这两行连接起来 下面用一个例子来帮助实际理解这句话&#xff1a; 创建下面两个表&#xff0c;customers表orders表 输入以下s…

银行软件测试面试题目总结,希望可以帮到你

目录 一、根据题目要求写出具体LINUX操作命令 二、JMETER题目 三、根据题目要求写出具体SQL语句 总结感谢每一个认真阅读我文章的人&#xff01;&#xff01;&#xff01; 重点&#xff1a;配套学习资料和视频教学 一、根据题目要求写出具体LINUX操作命令 1、分别写出一种…

HTML标签——列表标签 之 自定义列表

HTML标签——列表标签 之 自定义列表 目录HTML标签——列表标签 之 自定义列表一、 场景&#xff1a;在网页的底部导航中通常会使用自定义列表实现。二、标签组成&#xff1a;三、案例实操四、运行效果五、显示特点&#xff1a;六、注意点&#xff1a;七、小结一、 场景&#x…

DeepPath: A Reinforcement Learning Method forKnowledge Graph Reasoning

Innovation使用RL学习KG中的关系路径推理使用Supervised Policy Learning解决&#xff1a;KG中关系图大&#xff0c;如试错训练RL&#xff0c;难以收敛使用双向路径搜索&#xff0c;减少中间节点数量IntroductionPRA是一种学习推理路径的方法&#xff0c;使用基于RandomWalk的重…

智能语音信息处理团队18篇论文被语音技术顶会ICASSP 2023接收

近日&#xff0c;ICASSP 2023会议发出了审稿结果通知&#xff0c;语音及语言信息处理国家工程研究中心智能语音信息处理团队共18篇论文被会议接收&#xff0c;论文方向涵盖语音识别、语音合成、话者识别、语音增强、情感识别、声音事件检测等&#xff0c;各接收论文简介见后文。…

FreeRTOS入门(05):事件组

文章目录目的基础说明相关函数使用演示总结目的 事件组是RTOS中相对常用的用于任务间交互的功能&#xff0c;这篇文章将对相关内容做个介绍。 本文代码测试环境见前面的文章&#xff1a;《FreeRTOS入门&#xff08;01&#xff09;&#xff1a;基础说明与使用演示》 基础说明…

华为OD机试模拟题 用 C++ 实现 - 寻找连续区间(2023.Q1)

最近更新的博客 【华为OD机试模拟题】用 C++ 实现 - 最多获得的短信条数(2023.Q1)) 文章目录 最近更新的博客使用说明寻找连续区间题目输入输出示例一输入输出说明示例二输入输出Code使用说明 参加华为od机试,一定要注意不要完全背诵代码,需要理解之后模仿写出,通过率…

嵌入式环境配置—VMware 软件安装和虚拟机的创建

目录 一、VMware软件的安装 二、虚拟机的创建 三、Linux操作系统的安装 VMware软件的安装 为什么要虚拟机? 嵌入式Linux开发需要在Linux系统下进行&#xff0c;我们选择了Ubuntu。 1.双系统安装 有问题&#xff0c;一次只能使用一个系统。Ubuntu基本只做编译用。需求&…

Qt之QTableView自定义排序/过滤(QSortFilterProxyModel实现,含源码+注释)

一、效果示例图 1.1 自定义表格排序示例图 本文过滤条件为行索引取余2等于0时返回true&#xff0c;且从下图中可以看到&#xff0c;奇偶行是各自挨在一起的。 1.2 自定义表格过滤示例图 下图添加两列条件&#xff08;当前数据大于当前列条件才返回true&#xff0c;且多个列…

美团前端常见vue面试题(必备)

v-model 是如何实现的&#xff0c;语法糖实际是什么&#xff1f; &#xff08;1&#xff09;作用在表单元素上 动态绑定了 input 的 value 指向了 messgae 变量&#xff0c;并且在触发 input 事件的时候去动态把 message设置为目标值&#xff1a; <input v-model"sth…

Java程序设计基础学习笔记——类与对象、方法、递归、重载、可变参数、作用域、构造器、this

以下笔记整理自B站UP主韩顺平【零基础 快速学Java】韩顺平 零基础30天学会Java课程OOP面向对象程序设计(Object Oriented Programming)类就是数据类型&#xff0c;有属性和行为&#xff1b;对象是一个具体的实例从类到对象&#xff0c;目前有几种说法&#xff1a;创建一个对象实…

【网络安全入门】零基础小白必看!!!

看到很多小伙伴都想学习 网络安全 &#xff0c;让自己掌握更多的 技能&#xff0c;但是学习兴趣有了&#xff0c;却发现自己不知道哪里有 学习资源◇瞬间兴致全无&#xff01;◇ &#x1f604;在线找人要资料太卑微&#xff0c;自己上网下载又发现要收费0 &#x1f643;差点当…

HttpRunnerManager部署

基于HttpRunner的接口自动化测试平台: HttpRunner, djcelery and Django_. HttpRunner手册: http://cn.httprunner.org/git地址&#xff1a;httprunner/HttpRunnerManager: 基于 HttpRunner 的 Web 测试平台&#xff0c;已停止维护。 (github.com)部署机器&#xff1a;linux部署…

强强角逐,筑梦开源——2022年度启智社区优秀项目及开发者评选结果正式揭晓

2月24日&#xff0c;第四届OpenI/O启智开发者大会在深圳隆重开幕。本届大会以“算网筑基、开源启智、AI赋能”为主题&#xff0c;邀请国内人工智能开源领域领军院士亲自参加&#xff0c;汇聚学术界、产业界的技术专家&#xff0c;围绕中国算力网资源基座、开源社区服务支撑环境…

Tik Tok shop2023年度策略解读

一、TikTok Shop跨境电商数据显示自2022年上半年东南亚开放跨境电商入驻以来&#xff0c;全年GMV月均复合增速近90%&#xff1b;英国市场2022年下半年的平均交易客单价也同比2021年增长超40%&#xff1b;全年多个重要节点跨境GMV增长显著&#xff0c;例如2022年的圣诞季跨境GMV…

ESP32设备驱动-PCF8574IO扩展模块驱动

PCF8574IO扩展模块驱动 1、PCF8574介绍 PCF8574 是一个使用 I2C 协议的 8 位 I/O 端口扩展器。 通过开发板的SDA 和 SCL 引脚来控制多达 8 个数字 I/O 端口。 其中,A0,A1,A2 为地址引脚,P0,P1,P2,P3,P4,P5,P6,P7 为数字端口。PCF8574的地址如下: 在前面的文章,对PCF8574的…

【渐进交互学习网络:轻量级:超分:工业应用】

Progressive Interaction-Learning Network for Lightweight Single-Image Super-Resolution in Industrial Applications &#xff08;工业应用中轻量级单幅图像超分辨率渐进交互学习网络&#xff09; 近年来&#xff0c;基于深度学习&#xff08;DL&#xff09;的工业应用因…

大数据技术之Hive(四)分区表和分桶表、文件格式和压缩

一、分区表和分桶表1.1 分区表partitionhive中的分区就是把一张大表的数据按照业务需要分散的存储到多个目录&#xff0c;每个目录就称为该表的一个分区。在查询时通过where子句中的表达式选择式选择查询所需要的分区&#xff0c;这样的查询效率辉提高很多。1.1.1 分区表基本语…

Python 不同分辨率图像峰值信噪比[PSNR]

PNNR&#xff1a;全称为“Peak Signal-to-Noise Ratio”&#xff0c;中文直译为峰值信噪比 前言 一、定义 二、Python代码 1.自定义 2.Tensorflow 总结 前言 峰值信噪比是一种衡量图像质量的指标&#xff0c;描述的是最大值信号与北京噪音之间的关系。 一般来说&#xff0c;P…

Allegro如何删除铜皮上多余的空洞操作指导

Allegro如何删除铜皮上多余的空洞操作指导 在做PCB设计的时候,设计铜皮的时候是不希望铜皮上有多余的空洞的,设计完成前需要把多余的空洞删除,如下图 如何删除,具体操作如下 点击Shape点击Manual Void/Cavity