Java 应用中的内存泄漏:常见场景及最佳实践

news2024/11/15 11:50:04

内存泄漏是 Java 应用程序中一个常见而棘手的问题,它会导致应用程序的内存使用不断增长,最终影响性能和稳定性。尽管 Java 提供了垃圾回收机制来自动管理内存,但内存泄漏问题依然普遍存在。内存泄漏的根源可能包括不再使用的对象仍被持有引用、未关闭的资源、线程局部变量未清理、或者事件监听器和回调函数未被移除等。

在这篇文章中,我们将深入探讨 Java 内存泄漏的各种原因,提供实际的代码示例,帮助你识别和理解这些问题。我们还将介绍预防和解决内存泄漏的最佳实践,包括代码审查、静态分析、以及内存分析工具的使用。无论你是 Java 开发新手还是资深工程师,这篇文章都将为你提供宝贵的见解和实用的技巧,帮助你有效管理应用程序的内存,提升代码质量和系统性能。


文章目录

      • 1、什么是内存泄漏
      • 2、长生命周期对象持有短生命周期对象的引用引起的内存泄漏
        • 2.1、问题描述
        • 2.2、解决方案
          • 2.2.1、使用 WeakHashMap
          • 2.2.2、使用带有过期策略的缓存
      • 3、未关闭的资源引起的内存泄漏
        • 3.1、问题描述
        • 3.2、解决方法
      • 4、监听器和回调未移除引起的内存泄漏
        • 4.1、问题描述
        • 4.2、解决方法
          • 4.2.1、移除监听器和回调
          • 4.2.2、使用弱引用监听器
          • 4.2.3、确保组件销毁时的资源管理
          • 4.2.4、使用框架提供的机制
      • 5、ThreadLocal 变量
        • 5.1、问题描述
        • 5.2、解决方法
          • 5.2.1、在适当的时候调用 remove 方法
          • 5.2.2、避免长时间持有线程池中的 ThreadLocal 变量
          • 5.2.3、使用 ThreadLocal 的弱引用模式
      • 6、预防和检测内存泄漏的方法
        • 6.1、代码审查和静态分析
          • 6.1.1、代码审查
          • 6.1.2、静态分析工具
        • 6.2、使用内存分析工具
          • 6.2.1、VisualVM
          • 6.2.2、Eclipse MAT (Memory Analyzer Tool)
          • 6.2.3、JProfiler


1、什么是内存泄漏

内存泄漏的标准定义是当应用程序不再使用对象时发生的情况,但是垃圾回收器无法将其从工作内存中删除,因为它们仍在被引用。因此,应用程序会消耗越来越多的资源,最终导致致命的 OutOfMemoryError

垃圾回收操作需要消耗 CPU、线程、时间等资源,所以容易理解的是垃圾回收操作不是实时发生,当内存消耗完或者是达到某一个指标,才能触发垃圾回收操作。

在 JVM 堆中,有两种类型的对象——引用和未引用,垃圾回收器可以删除未被引用的对象。即使不再被应用程序使用,也不会收集被引用的对象。

img

检测内存泄漏可能很困难,虽然许多工具都会执行静态分析来确定潜在的泄漏事故,但这些技术并不完美,因为它们无法对运行系统的实际运行时行为作出判断。

所以,通过分析一些常见的情况,可以让我们有效理解和防止内存泄漏的发生。


2、长生命周期对象持有短生命周期对象的引用引起的内存泄漏

2.1、问题描述

当长生命周期对象(如静态集合类)持有短生命周期对象的引用时,即使这些短生命周期对象不再需要,垃圾回收器也无法回收它们,从而导致内存泄漏。下面详细说明这个问题,并举例解释如何解决。

举例说明:假设我们有一个简单的缓存机制,使用 HashMap 作为静态缓存来存储一些短期使用的数据。

public class Cache {
    private static Map<String, Object> cache = new HashMap<>();

    public static void put(String key, Object value) {
        cache.put(key, value);
    }

    public static Object get(String key) {
        return cache.get(key);
    }
}

在这个例子中,cache 是一个静态变量,具有长生命周期。它持有短生命周期的对象,如果这些对象不再需要,但没有被从 cache 中移除,它们将无法被回收。

public class Main {
    public static void main(String[] args) {
        // 将对象存入缓存
        Cache.put("key1", new Object());

        // ... 其他代码

        // 对象实际上不再需要,但仍然被缓存引用
    }
}

上述代码中,尽管某些对象在应用程序的执行过程中不再需要,但由于它们仍然被缓存引用,无法被垃圾回收器回收,导致内存泄漏。

2.2、解决方案

解决这种内存泄漏问题的一种常见方法是使用弱引用 (WeakReference) 或带有过期策略的缓存实现(如 Guava 的 CacheBuilder)。

2.2.1、使用 WeakHashMap

WeakHashMapHashMap 的一个特殊版本,键是弱引用的。当键不再有强引用时,键-值对会被垃圾回收。

import java.util.WeakHashMap;
import java.util.Map;

public class Cache {
    private static Map<String, Object> cache = new WeakHashMap<>();

    public static void put(String key, Object value) {
        cache.put(key, value);
    }

    public static Object get(String key) {
        return cache.get(key);
    }
}
2.2.2、使用带有过期策略的缓存

使用 Google Guava 提供的缓存库,可以轻松地实现带有过期策略的缓存,从而自动清理不再需要的对象。

import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;

import java.util.concurrent.TimeUnit;

public class Cache {
    private static LoadingCache<String, Object> cache = CacheBuilder.newBuilder()
        .expireAfterWrite(10, TimeUnit.MINUTES)  // 设置过期时间
        .build(new CacheLoader<String, Object>() {
            @Override
            public Object load(String key) throws Exception {
                return new Object();  // 实际应用中应返回需要缓存的对象
            }
        });

    public static void put(String key, Object value) {
        cache.put(key, value);
    }

    public static Object get(String key) {
        return cache.getUnchecked(key);
    }
}

3、未关闭的资源引起的内存泄漏

未关闭的资源(如文件流、数据库连接、网络连接等)在使用后如果没有及时关闭,会导致这些资源无法被回收,从而造成内存泄漏。这不仅会浪费系统资源,还会导致应用程序运行效率降低,甚至崩溃。

3.1、问题描述

举例说明:假设我们有一个读取文件内容的程序,它在读取文件内容后没有正确关闭文件流:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderExample {
    public static void readFile(String filePath) {
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(filePath));
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 资源未正确关闭
            try {
                if (reader != null) {
                    reader.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        readFile("example.txt");
    }
}

在这个例子中,虽然 BufferedReaderfinally 块中被关闭,但这种关闭方式容易出错,如果在多个地方使用相同的资源管理方式,可能会遗漏某些关闭操作。

3.2、解决方法

使用 try-with-resources 语句是解决资源泄漏问题的一种有效方法。try-with-resources 语句可以确保每个资源在使用后自动关闭。以下是改进后的代码:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class FileReaderExample {
    public static void readFile(String filePath) {
        // 使用 try-with-resources 确保资源自动关闭
        try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        readFile("example.txt");
    }
}

在这个改进后的例子中,try-with-resources 语句确保了 BufferedReader 在使用完毕后自动关闭,从而避免了资源泄漏。


4、监听器和回调未移除引起的内存泄漏

在 Java 应用程序中,事件监听器和回调函数用于处理异步事件和操作。注册这些监听器和回调函数时,如果在不需要时没有适当移除它们,可能会导致内存泄漏。这是因为监听器和回调函数通常会持有对对象的引用,导致这些对象无法被垃圾回收器回收。

4.1、问题描述

举例说明:假设我们在一个图形用户界面(GUI)应用程序中注册了一个事件监听器来处理按钮点击事件。如果在按钮被销毁时没有移除监听器,那么该监听器会持续持有对按钮的引用,导致按钮无法被垃圾回收器回收。

以下是一个简单的例子:

import javax.swing.JButton;
import javax.swing.JFrame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MemoryLeakExample {
    private JFrame frame;
    private JButton button;

    public MemoryLeakExample() {
        frame = new JFrame("Memory Leak Example");
        button = new JButton("Click me");

        // 注册事件监听器
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Button clicked!");
            }
        });

        frame.add(button);
        frame.setSize(300, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        MemoryLeakExample example = new MemoryLeakExample();
    }
}

在这个例子中,按钮 button 被创建并注册了一个 ActionListener 监听器。如果 frame 被关闭但 button 仍然持有对监听器的引用,监听器将无法被垃圾回收器回收,从而导致内存泄漏。

4.2、解决方法
4.2.1、移除监听器和回调

在组件或对象不再需要时,务必及时移除已注册的监听器和回调函数。以下是如何在 JFrame 关闭时移除监听器的示例:

import javax.swing.JButton;
import javax.swing.JFrame;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class MemoryLeakExample {
    private JFrame frame;
    private JButton button;
    private ActionListener listener;

    public MemoryLeakExample() {
        frame = new JFrame("Memory Leak Example");
        button = new JButton("Click me");

        listener = new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                System.out.println("Button clicked!");
            }
        };

        // 注册事件监听器
        button.addActionListener(listener);

        frame.add(button);
        frame.setSize(300, 200);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
    }

    public void cleanup() {
        // 移除事件监听器
        button.removeActionListener(listener);
        frame.dispose(); // 关闭窗口
    }

    public static void main(String[] args) {
        MemoryLeakExample example = new MemoryLeakExample();

        // Simulate application running and then cleanup
        Runtime.getRuntime().addShutdownHook(new Thread(example::cleanup));
    }
}
4.2.2、使用弱引用监听器

在一些情况下,可以使用 WeakReference 来避免内存泄漏。JavaFX 提供了 WeakListener,但对于 Swing,通常需要手动管理监听器的生命周期。

4.2.3、确保组件销毁时的资源管理

在创建 GUI 组件或其他对象时,确保在组件或对象销毁时适当清理资源。这包括移除所有监听器和回调函数。**

4.2.4、使用框架提供的机制

一些框架提供了自动管理监听器和回调函数的机制。例如,JavaFX 提供了 WeakListener 和其他工具来帮助处理监听器。


5、ThreadLocal 变量

ThreadLocal 是 Java 提供的一种机制,用于为每个线程提供独立的变量副本。这意味着每个线程可以在 ThreadLocal 变量中存储和访问独立的数据,而不会受到其他线程的干扰。这在需要线程隔离的数据时非常有用,如数据库连接或线程本地缓存。

然而,如果 ThreadLocal 变量没有在适当的时候调用 remove() 方法,可能会导致内存泄漏。原因在于,ThreadLocal 变量通常会将线程本地数据存储在 ThreadLocalMap 中,该映射的条目(键值对)会在 ThreadLocal 实例被垃圾回收时保留,直到线程结束。因此,如果线程池中的线程没有清除其 ThreadLocal 数据,这些数据会在整个应用程序的生命周期内占用内存。

5.1、问题描述

举例说明:假设我们在一个应用程序中使用 ThreadLocal 来存储每个线程的数据库连接:

public class DatabaseConnectionManager {
    private static final ThreadLocal<Connection> threadLocalConnection = new ThreadLocal<>();

    public static Connection getConnection() {
        Connection connection = threadLocalConnection.get();
        if (connection == null) {
            connection = createNewConnection();
            threadLocalConnection.set(connection);
        }
        return connection;
    }

    public static void removeConnection() {
        threadLocalConnection.remove();
    }

    private static Connection createNewConnection() {
        // 创建新的数据库连接
        return new Connection();
    }
}

在这个例子中,ThreadLocal 被用来存储每个线程的数据库连接。如果在处理完成后没有调用 removeConnection() 方法,线程将保持对 ThreadLocal 变量的引用,导致连接对象不能被垃圾回收器回收,特别是在线程池中,这些线程可能会被重用,导致潜在的内存泄漏。

5.2、解决方法
5.2.1、在适当的时候调用 remove 方法

确保在使用完 ThreadLocal 变量后调用 remove() 方法,以清除当前线程的 ThreadLocal 变量的值,防止内存泄漏。

public class Task implements Runnable {
    @Override
    public void run() {
        try {
            Connection connection = DatabaseConnectionManager.getConnection();
            // 执行任务
        } finally {
            DatabaseConnectionManager.removeConnection(); // 清除 ThreadLocal 数据
        }
    }
}
5.2.2、避免长时间持有线程池中的 ThreadLocal 变量

尽量避免将 ThreadLocal 变量用于长期存在的线程池。如果使用线程池,确保在任务执行结束时清理 ThreadLocal 数据。

5.2.3、使用 ThreadLocal 的弱引用模式

在某些情况下,使用 WeakReference 来管理 ThreadLocal 变量,可以帮助避免内存泄漏。虽然 ThreadLocal 本身不直接支持弱引用模式,但可以通过其他技术或库来实现。


6、预防和检测内存泄漏的方法

6.1、代码审查和静态分析

代码审查,是一种手动检查代码的过程,目的是发现潜在的错误和改进代码质量。静态分析 工具则自动分析代码,帮助发现潜在的问题,如内存泄漏、线程安全问题等。下面详细介绍这些方法:

6.1.1、代码审查
  • 概念:代码审查是团队成员互相检查代码的过程,以发现错误、改进代码质量和确保代码符合设计规范。

  • 步骤:

    • 计划审查:确定审查的目标、范围和参与人员。
    • 执行审查:逐行检查代码,讨论发现的问题,提出改进建议。
    • 记录和修复:记录发现的问题,并对代码进行修改。
    • 验证:确保修复的问题不会引入新的问题。
  • 注意事项:

    • 关注资源管理:特别注意资源的创建和释放,确保文件流、数据库连接等资源在使用后被及时关闭。
    • 检查线程管理:检查线程的创建和管理,确保线程在任务完成后得到适当处理。
    • 防止常见泄漏模式:注意 ThreadLocal、缓存和事件监听器等可能导致内存泄漏的场景。
6.1.2、静态分析工具

静态分析工具 可以自动化地分析代码,发现潜在的内存泄漏和其他问题。以下是一些常用的静态分析工具:

  • FindBugs:

    • 概念:FindBugs 是一个用于 Java 代码的静态分析工具,它可以发现代码中的 bug 和潜在问题。
    • 功能:识别常见的错误模式,如空指针异常、并发问题和内存泄漏。
    • 使用:可以集成到开发环境(如 Eclipse)或构建工具(如 Maven)中。
  • PMD:

    • 概念:PMD 是一个静态代码分析工具,用于发现代码中的潜在错误和不良编码习惯。
    • 功能:检查代码中的编码规范、复杂度问题、未使用的变量和方法等。
    • 使用:支持多种编程语言,包括 Java,并可以集成到构建流程中。

静态分析工具 的优点是能够在编译时发现潜在问题,而不需要运行代码,但它们可能会产生假阳性或漏检问题,需要结合实际代码和经验来判断。

6.2、使用内存分析工具

内存分析工具可以实时监控应用程序的内存使用情况,帮助发现和分析内存泄漏。以下是一些常用的内存分析工具:

6.2.1、VisualVM
  • 概念:VisualVM 是一个免费的 Java 监控、分析和故障排除工具,它集成了多种监控功能,如内存、线程和 CPU 使用情况。

  • 功能:

    • 内存分析:监控堆内存的使用情况,查看对象的分布和垃圾回收活动。
    • 内存泄漏检测:通过堆转储和对象实例分析,帮助发现内存泄漏。
    • 线程分析:查看线程的活动和状态,识别潜在的线程瓶颈。
  • 使用:

    • 可以通过 JDK 提供的 jvisualvm 启动。
    • 连接到正在运行的 Java 应用程序,实时监控和分析。
6.2.2、Eclipse MAT (Memory Analyzer Tool)
  • 概念:Eclipse MAT 是一个开源的 Java 内存分析工具,用于分析堆转储文件,帮助检测内存泄漏和性能问题。

  • 功能:

    • 堆转储分析:分析堆转储文件,查找内存泄漏和高内存占用的对象。
    • 泄漏检测:提供详细的泄漏报告和建议,帮助开发者理解内存使用情况。
    • 对象遍历:查看堆中的对象及其引用,识别不再需要的对象。
  • 使用:

    • 通过 Eclipse IDE 插件安装或独立版本使用。
    • 从应用程序中生成堆转储文件并加载到 MAT 中进行分析。
6.2.3、JProfiler
  • 概念:JProfiler 是一个商业 Java 性能分析工具,提供全面的内存、CPU 和线程分析功能。

  • 功能:

    • 内存泄漏检测:实时监控和分析内存使用情况,提供详细的泄漏分析。
    • 性能分析:分析 CPU 使用情况、内存分配和线程活动。
    • 数据可视化:提供图形化界面展示分析结果,帮助快速识别问题。
  • 使用:

    • 集成到 IDE 或作为独立工具运行。
    • 连接到运行中的 Java 应用程序进行实时分析和调试。

预防和检测内存泄漏是确保 Java 应用程序稳定性和性能的关键步骤。代码审查和静态分析工具可以帮助在开发阶段发现潜在问题,而内存分析工具则提供了实时监控和深入分析的能力。结合这两种方法,可以有效地管理和优化内存使用,减少内存泄漏的风险。

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

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

相关文章

第四范式上线搜广推一体化平台 赋能企业高效增长

产品上新 Product Release 今天&#xff0c;第四范式产品再度上新&#xff0c;正式升级并推出的“搜广推”一体化平台——天枢。 天枢拥有全面的用户画像分析、端到端的搜索推荐一体化、一站式流量运营管理等能力&#xff0c;集合智能搜索、智能推荐和智能推广三大能力于一身&a…

酷柚易汛ERP全新功能插件上线“业绩提成”很多老板期待已久,终于来啦!

业绩提成基于进销存系统销货业务设计的、用于自动化处理业务员销货业绩与提成计算过程的插件&#xff0c;汇总累计进销存系统产生的业绩额度并根据自定规则计算对应提成金额&#xff0c;以减少人力计算成本 多场景适配 集成了常用的提成方式&#xff0c;并且可设置提成额度限…

启动 /使用/关闭 Redis 服务器

1. Linux 启动 Linux 系统启动 Redis 有两种方法&#xff0c;分别是前台启动&#xff0c;后台启动&#xff0c;两者各有差异&#xff1b; &#xff08;1&#xff09;前台启动 首先&#xff0c;需要进入 bin 路径(安装路径不同输入的命令也不同); 个人的命令&#xff08;一般…

Ubantu中Docker-Compose的安装与卸载

文章目录 一、卸载二、安装安装Docker-Compose添加权限验证 一、卸载 sudo rm /usr/local/bin/docker-compose二、安装 安装Docker-Compose curl -L https://github.com/docker/compose/releases/download/1.21.1/docker-compose-uname -s-uname -m -o /usr/local/bin/docke…

【Python正则-驯化】一文学会通过Python中的正则表达式提取文本中的日期

【Python正则-驯化】一文学会通过Python中的正则表达式提取文本中的日期 本次修炼方法请往下查看 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我工作、学习、实践 IT领域、真诚分享 踩坑集合&#xff0c;智慧小天地&#xff01; &#x1f387; 免费获取相关内容…

吴恩达机器学习-C1W3L2-逻辑回归之S型函数

可选实验:逻辑回归 在这个不评分的实验中&#xff0c;你会 探索sigmoid函数(也称为logistic函数)探索逻辑回归;哪个用到了s型函数 import numpy as np %matplotlib widget import matplotlib.pyplot as plt from plt_one_addpt_onclick import plt_one_addpt_onclick from l…

java远程调试

java远程调试 idea2024创一个Spring Web项目springdemo1 使用maven-assembly-plugin插件打包成JAR文件 pom.xml参考如下 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi&quo…

【C#之WPF,给Border设置背景图片,代码运行后图片无法显示】

C#之WPF&#xff0c;给Border设置背景图片&#xff0c;代码运行后图片无法显示 方案 方案 选中你的图片找到属性&#xff1b; 这两个都改一下&#xff1b; 改好之后在运行就可以正常显示了。

uni-app中使用支付宝扫码插件并且在真机调试时使用(详细教程)

前言&#xff1a;uni-app自带的扫码api 识别不灵敏&#xff0c;每次都得扫很长时间且不断调整才能扫出来码&#xff0c;所以决定使用支付宝扫码插件&#xff0c;官方插件地址&#xff1a;https://ext.dcloud.net.cn/plugin?id2636#detail 使用步骤: 1、下载插件到项目中 2、…

ai绘画软件哪个好?5个工具让你绘图快人一步

最近&#xff0c;沉浸式感受火把节的体验在网上引起了广泛关注&#xff0c;成为了热门话题。然而&#xff0c;我们这些忙碌的打工人&#xff0c;因为工作繁忙&#xff0c;只能羡慕地看着别人分享的欢乐瞬间。 别灰心&#xff01;谁说我们不能以另一种方式参与这场盛宴呢&#…

4款免费且安全:常用的PDF转Word在线转换工具推荐

现在办公越来越离不开电脑了&#xff0c;PDF文件和Word文档来回转换的需求也越来越大。作为一个天天跟文件打交道的上班族&#xff0c;我特别明白找个好用、靠谱的PDF转Word在线转换工具有多重要。今儿个&#xff0c;给大家说说五个免费的转换工具&#xff0c;都是我试过觉得挺…

SPSSAU | CatBoost模型原理及案例实操分析

CatBoost 是一种基于梯度提升的决策树(Gradient Boosting Decision Trees, GBDT)算法&#xff0c;专门优化了处理分类特征和序列数据的能力。算法步骤如下&#xff1a; 第一&#xff1a;生成初始模型&#xff0c;从简单的模型开始&#xff0c;通常是所有目标值的平均值&#x…

telnet与ping:测试IP及端口连通性

AI应用开发相关目录 本专栏包括AI应用开发相关内容分享&#xff0c;包括不限于AI算法部署实施细节、AI应用后端分析服务相关概念及开发技巧、AI应用后端应用服务相关概念及开发技巧、AI应用前端实现路径及开发技巧 适用于具备一定算法及Python使用基础的人群 python使用API实现…

压路机土方压实摊铺精确施工引导系统

整体架构 一、土方压实摊铺工程现状 二、整体介绍 1、在施工现场利用 GNSS 定位设备终端、振动传感器、温度传感器&#xff0c;实现对施工机械的位置、运行状态实时监测&#xff0c;实现底层数据的多参数、多种类高效采集。 2、本系统基于卫星差分定位技术&#xff0c;通过与…

JNPF全新V5.0版本!重磅升级——协同办公篇

尊敬的JNPF用户们&#xff1a; 我们非常高兴地宣布&#xff0c;经过团队数月的辛勤努力和不断的技术创新&#xff0c;JNPF快速开发平台终于迎来了里程碑式的全新升级——V5.0版本&#xff01;这一版本的更新发布&#xff0c;不仅代表着我们技术实力的进一步提升&#xff0c;是…

点线面的智慧:转转JTS技术如何塑造上门履约地理布局

1 引言 如上图所示&#xff0c;在转转上门履约的场景中&#xff0c;上门服务的覆盖区域是在地图上画电子围栏来划定的。这就涉及到一些几何图形的操作和空间关系判断&#xff0c;其中最核心问题就是要解决如何判断位置是否在上门覆盖范围内。下面介绍下 JTS&#xff0c;以及如何…

微服务实战系列之玩转Docker(八)

前言 “Compose”一词的原意是创作、组成、构成&#xff08;一个整体&#xff09;&#xff0c;那么“Docker Compose”一词可以理解为一组docker。今天博主着重介绍一下这个“容器导演” —— 一个docker自带的容器管理工具。 Docker Compose入门 1. 定义 Docker Compose is …

这八本大模型书籍看完你就是LLM大师,非常详细收藏我这一篇就够了

如果你想深入了解大模型领域&#xff0c;无论是为了学术研究还是实际应用&#xff0c;选择合适的书籍是非常重要的。以下是精选的八本大模型相关书籍&#xff0c;涵盖了从基础理论到高级实践的内容&#xff0c;可以帮助你构建全面的知识体系。 《大模型应用开发极简入门》 作者…

PyTorch图像分割的基本形式

【图书推荐】《PyTorch深度学习与计算机视觉实践》-CSDN博客 图像分割是计算机视觉领域中的一个重要任务&#xff0c;旨在将图像划分为多个不同的区域或对象。简单来说&#xff0c;图像分割就是将图像中的像素或区域按照某种规则或标准进行分类&#xff0c;使得同一区域内的像…

如何更改软件ip地址:方法详解

在数字化时代&#xff0c;网络连接已成为我们日常生活与工作中不可或缺的一部分。无论是日常娱乐、办公协作&#xff0c;还是商业运营&#xff0c;软件应用都需要通过IP地址与互联网建立联系。然而&#xff0c;在某些特定情况下&#xff0c;我们可能需要更改软件的IP地址&#…