JConsole:JDK性能监控利器之JConsole的使用说明与案例实践

news2025/3/16 9:43:25

在这里插入图片描述

🪁🍁 希望本文能给您带来帮助,如果有任何问题,欢迎批评指正!🐅🐾🍁🐥


文章目录

  • 一、背景
  • 二、JConsole的启动与连接
    • 2.1 JConsole的启动
    • 2.2 进程连接
      • 2.2.1 本地进程连接
      • 2.2.2 远程进程连接
  • 三、JConsole的界面介绍与实战
    • 3.1 概览
    • 3.2 内存监控
    • 3.3 线程监控
    • 3.4 类
    • 3.5 vm概要
    • 3.6 MBean
  • 四、作用
  • 五、总结

一、背景

我们在开发java项目时不可避免会遇到一些问题,比如遇到了死锁及一些其他的性能问题,那么发生这些问题该如何去排查呢?之前在公司可能或多或少会去使用它,但是没有专门去记录这个过程,而为了更加全面地认识这个工具,专门写下此篇文章记录一下。在此,也希望本篇文章对您有所帮助。


二、JConsole的启动与连接

2.1 JConsole的启动

  1. 点击JDK/bin 目录下面的“jconsole.exe”即可启动
  2. 然后会自动搜索本机运行的所有虚拟机进程
  3. 选择其中一个进程可开始进行监控

在这里插入图片描述

2.2 进程连接

JConsole有两种连接方式,一种是连接本地的进程,一种是连接远程的程序。

2.2.1 本地进程连接

JConsole的本地连接是通过Java的Attach API实现的,Attach API是Java提供的一种机制,允许一个Java进程动态连接到另一个运行中的Java进程(JVM),然后执行一些操作,比如加载代理、获取运行时信息等。

注意:Attach API是Java5引入的功能,位于com.sun.tools.attach包中,它的主要类包括VirtualMachine(表示一个JVM进程)、VirtualMachineDescriptor(描述一个JVM进程的信息)。

本地进程连接流程在JConsole中被简化成了直接选择待attach的目标JVM进程ID。但是其背后进行本地进程连接的完整过程可以大致分如下几个步骤:

  1. 获取本地JVM进程列表:JConsole启动时会调用VirtualMachine.list()方法,获取当前机器上所有运行的JVM进程列表,每个JVM进程由一个VirtualMachineDescriptor 对象表示,包含进程ID和显示名称等信息。
  2. 选择目标JVM进程:JConsole 会展示获取到的 JVM 进程列表,用户可以选择一个进程进行连接。
  3. 连接到目标JVM:调用 VirtualMachine.attach(pid) 方法,连接到目标 JVM 进程,连接成功后,返回一个 VirtualMachine 对象,表示与目标 JVM 的连接。
  4. 加载JMX代理:通过 VirtualMachine 对象,JConsole 可以加载 JMX 代理(management-agent.jar),JMX 代理会启动目标 JVM 的 JMX 服务,并返回 JMX 连接地址。
  5. 建立JMX连接:JConsole 使用 JMX 连接地址,通过 JMX 连接到目标 JVM 的 MBeanServer,连接成功后,JConsole 可以访问目标 JVM 的 MBean,获取运行时数据。

下面有一个死锁的Demo案例,这个案例是本文全文会使用的案例。我们先把它运行起来之后,分别通过启动JConsole工具、手写Java代码去进行一个本地进程连接。

package com.wasteland.blogsourcecode.jconsole;

/**
 * @author wasteland
 * @create 2025-02-12
 */
public class DeadLockDemo {
    private static Object resource1 = new Object();
    private static Object resource2 = new Object();

    public static void main(String[] args) {

        new Thread(() -> {
            synchronized (resource1) {
                System.out.println(Thread.currentThread() + "get resource1");
                try {
                	int result = cacluate(4, 5);
                    System.out.println("thread1 calculate 4 + 5 result is : " + result);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "waiting get resource2");
                synchronized (resource2) {
                    System.out.println(Thread.currentThread() + "get resource2");
                }
            }
        }, "thread1").start();

        new Thread(() -> {
            synchronized (resource2) {
                System.out.println(Thread.currentThread() + "get resource2");
                try {
                	int result = cacluate(1, 3);
                    System.out.println("thread2 calculate 1 + 3 result is : " + result);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "waiting get resource1");
                synchronized (resource1) {
                    System.out.println(Thread.currentThread() + "get resource1");
                }
            }
        }, "thread2").start();
    }
    
    private static int cacluate(int a, int b) {
        return a + b;
    }
}

运行JConsole工具后,窗口展示如下图,我们可以勾选上要连接的进程如图中运行的死锁Demo的进程ID,勾选后点击连接即可。

在这里插入图片描述

使用JConsole工具进行本地进程连接非常简单,那么用Java代码该如何去实现呢?下面是Java语言实现本地进程连接的Demo:

package com.wasteland.blogsourcecode.jconsole;

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.List;

/**
 * @author wasteland
 * @create 2025-03-15
 */
public class AttachExample {
    public static void main(String[] args) {
        try {
            // 获取本地 JVM 进程列表
            List<VirtualMachineDescriptor> vmDescriptors = VirtualMachine.list();
            String targetPid = "";
            for (VirtualMachineDescriptor descriptor : vmDescriptors) {
                System.out.println("PID: " + descriptor.id() + ", Name: " + descriptor.displayName());
                if ("com.wasteland.blogsourcecode.jconsole.DeadLockDemo".equals(descriptor.displayName())) {
                    // 找到目标进程,进行连接
                    targetPid = descriptor.id();
                    break;
                }
            }

            VirtualMachine vm = VirtualMachine.attach(targetPid);

            // 获取 JMX 连接地址
            String connectorAddress = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
            if (connectorAddress == null) {
                // 如果 JMX 代理未启动,则加载代理
                String agentPath = vm.getSystemProperties().getProperty("java.home") +
                        "/lib/management-agent.jar";
                vm.loadAgent(agentPath);
                connectorAddress = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
            }

            System.out.println("JMX Connector Address: " + connectorAddress);

            // 建立 JMX 连接
            JMXServiceURL url = new JMXServiceURL(connectorAddress);
            JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
            MBeanServerConnection connection = jmxConnector.getMBeanServerConnection();

            // 获取 ThreadMXBean
            ThreadMXBean threadMXBean = ManagementFactory.newPlatformMXBeanProxy(
                    connection, ManagementFactory.THREAD_MXBEAN_NAME, ThreadMXBean.class);   
            // 断开连接
            jmxConnector.close();
            vm.detach();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代码执行效果如下图:

在这里插入图片描述

可以看到控制台已经打印了所有的JVM进程,而检测到后可以attach上指定进程,进而去获取JVM内部信息。

2.2.2 远程进程连接

Console 的远程连接是通过 JMX(Java Management Extensions) 实现的。远程连接需要目标 JVM 启用 JMX 远程管理功能,并配置相应的端口和认证信息。以下是JConsole远程进程连接的具体流程:

  1. 目标JVM启用JMX远程管理:在启动目标 Java 应用程序时,需要通过 JVM 参数启用 JMX 远程管理功能。
  2. 启动JConsole
  3. 选择远程连接:在 JConsole 的启动界面中,选择 远程连接(Remote Process),可以选择输入<目标主机IP>:<JMX端口号>或者service:jmx:rmi:///jndi/rmi://<目标主机IP>:<JMX端口号>/jmxrmi
  4. 认证(可选):如果目标 JVM 启用了认证,JConsole 会弹出认证对话框,要求输入用户名和密码。
  5. 建立连接:JConsole 通过 JMX 连接到目标 JVM 的 MBeanServer,连接成功后,JConsole 可以访问目标 JVM 的 MBean,获取运行时数据。

这里由于没有云服务器,就把本机服务器当作远程服务器,然后选择远程进程方式进行连接测试了。
首先按照上面介绍的步骤,我们需要配置一些JVM启动参数:

/ 启用 JMX 远程管理
-Dcom.sun.management.jmxremote
/ 指定 JMX 连接的端口号
-Dcom.sun.management.jmxremote.port=12345
/ 禁用认证
-Dcom.sun.management.jmxremote.authenticate=false
/ 禁用 SSL 加密(仅用于测试环境,生产环境建议启用 SSL)。
-Dcom.sun.management.jmxremote.ssl=false

/ 如果需要启用认证和 SSL,可以做以下配置:
/-Dcom.sun.management.jmxremote.authenticate=true
/-Dcom.sun.management.jmxremote.ssl=true
/-Dcom.sun.management.jmxremote.password.file=/path/to/jmxremote.password
/-Dcom.sun.management.jmxremote.access.file=/path/to/jmxremote.access

在启动程序时,JVM参数配置里配置如上参数:

在这里插入图片描述
然后启动JConsole,输入<目标主机IP>:<JMX端口号>或者service:jmx:rmi:///jndi/rmi://<目标主机IP>:<JMX端口号>/jmxrmi都可以进行远程连接,因为这里是选的本机当远程主机,因此主机IP是127.0.0.1,JMX端口号是上面配置的12345。连接上后显示如下图所示 :

在这里插入图片描述
它在JConsole的远程连接过程如上文中所介绍,那么它背后是怎么实现的呢?它是通过JMX ConnectorRMI实现的。JMX 远程连接是通过 JMX Connector 实现,JMX Connector 是一个客户端-服务器模型,客户端(如 JConsole)通过 JMX Connector 连接到服务器(目标 JVM)。JMX 远程连接底层使用 RMI(远程方法调用) 进行通信。目标 JVM 会启动一个 RMI 注册表,并将 JMX Connector Server 绑定到 RMI 注册表中,客户端通过 RMI 查找 JMX Connector Server,并建立连接。

使用JConsole工具进行远程进程连接非常简单,那么用Java代码该如何去实现呢?下面是Java语言实现本地进程连接的Demo:

package com.wasteland.blogsourcecode.jconsole;

import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

/**
 * @author wasteland
 * @create 2025-03-15
 */
public class JMXRemoteExample {
    public static void main(String[] args) {
        try {
            // JMX 服务地址
            String jmxUrl = "service:jmx:rmi:///jndi/rmi://127.0.0.1:12345/jmxrmi";
            JMXServiceURL url = new JMXServiceURL(jmxUrl);

            // 创建 JMX 连接
            JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
            MBeanServerConnection connection = jmxConnector.getMBeanServerConnection();

            // 获取运行时信息
            System.out.println("MBean Count: " + connection.getMBeanCount());

            // 关闭连接
            jmxConnector.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代码执行效果如下图:

在这里插入图片描述

在了解过这两种不同的连接方式之后,不难发现本质都是根据JMX服务地址建立的连接,JMX(Java Management Extensions)是JConsole工具实现的根本原理。


三、JConsole的界面介绍与实战

在选择一个进程并点击连接后,可以看到界面展示如下图,JConsole界面主要分为六大部分,概览,内存,线程,类,VM和MBean。

3.1 概览

概览展示了堆内存使用量,线程,类和CPU占用率这四大内容。
在这里插入图片描述

3.2 内存监控

在这里插入图片描述
根据上图中我们可以监控堆内存和非堆内存的使用量,具体又细化到Old Gen、Eden Space、Survivor Space、Metaspace、Code Cache以及Compressed Class Space几大存储区域。由于本图中项目是前文中的死锁代码,其开发环境是JDK8,而JDK8默认垃圾收集器是Parallel Scavenge 收集器,因此可以看到图中它的年轻代和老年代名称具体为:PS Old Gen、PS Eden Space以及PS Survivor Space。

各区域存储内容总结如下:

内存区域存储内容
PS Old Gen生命周期较长的对象或者说大对象
PS Eden Space对象生命周期很短的对象
PS Survivor Space对象生命周期相对较长的对象
Metaspace类的结构信息、类的字节码、类的注解信息、类的静态变量等
Code CacheJIT编译后的方法代码、运行时生成的适配器及方法句柄等代码、JVM内部使用的解释器及编译器本身代码
Compressed Class Space存放压缩后的类原数据:类的结构信息、类的静态变量、类的字节码信息

点击执行GC按钮即可进行垃圾回收。通过两块内存监控图表也能看出:点击执行GC按钮后,会将年轻代里的对象放到老年代中。

在这里插入图片描述

3.3 线程监控

线程Tab中列出了程序目前正在运行的线程,如果点击具体的线程还可以看到线程中的堆栈跟踪和线程状态统计,非常有用。

在这里插入图片描述
因为attach上的是前文开头介绍的死锁Demo,点击检测死锁按钮后它可以直接帮我们定位到代码发生死锁的位置:

在这里插入图片描述
有朋友会好奇它这是怎么做到的呢?😱我们要记住其背后的原理:通过建立JMX连接后使用暴露出的MBean获取到JVM内部运行状态。那我们自己用Java代码可以实现这个检测死锁的过程吗?毫无疑问是可以的,我们只需要在2.2.1章节本地连接进程Demo上继续前进一步即可完成,Java代码实现过程如下:

package com.wasteland.blogsourcecode.jconsole;

import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;

import javax.management.MBeanServerConnection;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.List;

/**
 * @author wasteland
 * @create 2025-03-15
 */
public class AttachExample {
    public static void main(String[] args) {
        try {
            // 获取本地 JVM 进程列表
            List<VirtualMachineDescriptor> vmDescriptors = VirtualMachine.list();
            String targetPid = "";
            for (VirtualMachineDescriptor descriptor : vmDescriptors) {
                System.out.println("PID: " + descriptor.id() + ", Name: " + descriptor.displayName());
                if ("com.wasteland.blogsourcecode.jconsole.DeadLockDemo".equals(descriptor.displayName())) {
                    // 找到目标进程,进行连接
                    targetPid = descriptor.id();
                    break;
                }
            }

            VirtualMachine vm = VirtualMachine.attach(targetPid);

            // 获取 JMX 连接地址
            String connectorAddress = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
            if (connectorAddress == null) {
                // 如果 JMX 代理未启动,则加载代理
                String agentPath = vm.getSystemProperties().getProperty("java.home") +
                        "/lib/management-agent.jar";
                vm.loadAgent(agentPath);
                connectorAddress = vm.getAgentProperties().getProperty("com.sun.management.jmxremote.localConnectorAddress");
            }

            System.out.println("JMX Connector Address: " + connectorAddress);

            // 建立 JMX 连接
            JMXServiceURL url = new JMXServiceURL(connectorAddress);
            JMXConnector jmxConnector = JMXConnectorFactory.connect(url);
            MBeanServerConnection connection = jmxConnector.getMBeanServerConnection();

            // 获取 ThreadMXBean
            ThreadMXBean threadMXBean = ManagementFactory.newPlatformMXBeanProxy(
                    connection, ManagementFactory.THREAD_MXBEAN_NAME, ThreadMXBean.class);

            // 检测死锁
            long[] deadlockedThreads = threadMXBean.findDeadlockedThreads();
            if (deadlockedThreads != null) {
                System.out.println("Deadlock detected!");
                for (long threadId : deadlockedThreads) {
                    // 获取死锁的线程信息
                    // 为了确保堆栈跟踪信息被打印完全不被截断,这里最好指定线程堆栈跟踪的深度
                    ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId, Integer.MAX_VALUE);
                    System.out.println("Deadlocked Thread: " + threadInfo.getThreadName());
                    System.out.println("Stack Trace:");
                    for (StackTraceElement element : threadInfo.getStackTrace()) {
                        System.out.println("\t" + element.toString());
                    }
                }
            } else {
                System.out.println("No deadlock detected.");
            }

            // 断开连接
            jmxConnector.close();
            vm.detach();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代码执行效果如下图,能够看出和用JConsole工具检测出的死锁信息基本一致:

在这里插入图片描述

3.4 类

在类的这个tab下展示就相对来说比较简单了,仅显示了加载的类的总数。

在这里插入图片描述

3.5 vm概要

vm信息会展示虚拟机相关的一些参数比如虚拟机版本、JIT编译器及垃圾收集器等。

在这里插入图片描述

3.6 MBean

JConsole中最后一个tab展示的就是MBean,需要知道的是前文介绍的内存、类及VM概要信息其实从根本上来说都是基于MBean获取到的。

在这里插入图片描述

MBean(Managed Bean) 是 Java 管理扩展(JMX)的核心组件,用于暴露和管理 Java 应用程序的内部状态和操作。MBean 提供了一种标准化的方式,允许外部工具(如 JConsole)监控和管理应用程序的运行状态。

MBean的作用如下:

  1. 暴露应用程序的内部状态:MBean 可以暴露应用程序的各种运行时数据,如内存使用情况、线程状态、类加载信息、GC 信息等。例如,java.lang:type=Memory MBean 提供了 JVM 内存使用情况的信息。
  2. 提供管理工作:MBean 可以定义一些管理操作,允许外部工具动态修改应用程序的配置或执行特定任务。例如,java.lang:type=Threading MBean 提供了查找死锁线程、重置线程 CPU 时间等操作。
  3. 标准化接口:MBean 遵循 JMX 规范,提供标准化的接口,使得不同的监控工具(如 JConsole、VisualVM)可以统一访问和管理应用程序。

有朋友读到这里可能又会有疑问了,上面JConsole工具界面里展示的是默认已经实现的MBean,那么用户是否可以自定义MBean呢?当然是可以的,开发者不仅可以自定义MBean,并且在JConsole的工具的MBean目录下可以找到自定义的MBean。本文接下来会自己实现一个MBean,并且注册到MBean服务器中,案例还是前文介绍的死锁Demo,自定义MBean的作用是监测死锁Demo案例中的calculate这个方法的执行次数,具体结构分为3个类:MBean接口、MBean接口实现类、DeadLockDemo类。

MBean接口:

package com.wasteland.blogsourcecode.jconsole;

/**
 * @author wasteland
 * @create 2025-03-15
 */
public interface MethodMonitorMBean {
	// 获取方法执行次数
    int getInvocationCount();
    // 重置方法执行次数统计
    void resetInvocationCount();
}

MBean接口实现类:

package com.wasteland.blogsourcecode.jconsole;

/**
 * @author wasteland
 * @create 2025-03-15
 */
public class MethodMonitor implements MethodMonitorMBean {
    private int invocationCount = 0;

    @Override
    public int getInvocationCount() {
        return invocationCount;
    }

    @Override
    public void resetInvocationCount() {
        invocationCount = 0;
    }

    public void monitoredMethod() {
        invocationCount++;
        System.out.println("Method invoked. Current count: " + invocationCount);
    }
}

DeadLockDemo类:

package com.wasteland.blogsourcecode.jconsole;

import javax.management.MBeanServer;
import javax.management.ObjectName;
import java.lang.management.ManagementFactory;

/**
 * @author wasteland
 * @create 2025-03-15
 */
public class DeadLockDemo {
    private static Object resource1 = new Object();
    private static Object resource2 = new Object();

    private static volatile MethodMonitor methodMonitor;
    public static MethodMonitor getInstance() {
        // 判断对象是否已经实例化过,没有实例化才进入加锁代码
        if (methodMonitor == null) {
            // 类对象加锁
            synchronized (DeadLockDemo.class) {
                if (methodMonitor == null) { // 可以避免重复创建对象
                    methodMonitor = new MethodMonitor();
                }
            }
        }
        return methodMonitor;
    }

    public static void main(String[] args) throws Exception {
        // 获取 MBean 服务器
        MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();

        // 创建 MBean 实例
        MethodMonitor methodMonitor = DeadLockDemo.getInstance();

        // 注册 MBean
        ObjectName objectName = new ObjectName("com.wasteland.blogsourcecode.jconsole:type=MethodMonitor");
        mBeanServer.registerMBean(methodMonitor, objectName);

        new Thread(() -> {
            synchronized (resource1) {
                System.out.println(Thread.currentThread() + "get resource1");
                try {
                    int result = cacluate(4, 5);
                    System.out.println("thread2 calculate 4 + 5 result is : " + result);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "waiting get resource2");
                synchronized (resource2) {
                    System.out.println(Thread.currentThread() + "get resource2");
                }
            }
        }, "thread1").start();

        new Thread(() -> {
            synchronized (resource2) {
                System.out.println(Thread.currentThread() + "get resource2");
                try {
                    int result = cacluate(1, 3);
                    System.out.println("thread2 calculate 1 + 3 result is : " + result);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread() + "waiting get resource1");
                synchronized (resource1) {
                    System.out.println(Thread.currentThread() + "get resource1");
                }
            }
        }, "thread2").start();

    }

    private static int cacluate(int a, int b) {
        methodMonitor.monitoredMethod();
        return a + b;
    }
    
}

程序启动后,利用JConsole工具attach上这个死锁进程,连接成功后点击到MBean的Tab下,可以看到我们自己注册的MethodMonitor,其定义的带返回值InvocationCount方法在属性目录下,无返回值的resetInovationCount方法在操作目录下:

在这里插入图片描述


四、作用

根据前文介绍,不难发现JConsole工具有如下几大作用:

  • 系统资源实时监控
    • CPU使用率监控、内存使用情况监控、线程状态监控及类加载情况监控
  • JVM参数配置
    • JConsole可以显示当前JVM各种参数配置 ,还可以根据程序实际运行情况动态调整一些JVM参数
  • 性能分析与故障诊断
    • 当系统出现卡顿时,可以查看CPU、内存和线程状态,进而判断是资源耗尽还是线程阻塞导致,还可以结合jstack及jmap命令获得更加详细信息

五、总结

整体来说,JConsole是一个比较简单但是也很实用的profile工具,能够满足基本监控与性能分析的需求。在实际业务开发中,业务功能实现很重要,但是代码的性能和效率也同样重要,希望大家能够在编写代码之余多多关注。


创作不易,如果有帮助到你的话请给点个赞吧!我是Wasteland,下期文章再见!

在这里插入图片描述

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

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

相关文章

Neural Architecture Search for Transformers:A Survey

摘要 基于 Transformer 的深度神经网络架构因其在自然语言处理 (NLP) 和计算机视觉 (CV) 领域的各种应用中的有效性而引起了极大的兴趣。这些模型是多种语言任务&#xff08;例如情绪分析和文本摘要&#xff09;的实际选择&#xff0c;取代了长短期记忆 (LSTM) 模型。视觉 Tr…

Browser Copilot 开源浏览器扩展,使用现有或定制的 AI 助手来完成日常 Web 应用程序任务。

一、软件介绍 文末提供源码和开源扩展程序下载 Browser Copilot 是一个开源浏览器扩展&#xff0c;允许您使用现有或定制的 AI 助手来帮助您完成日常 Web 应用程序任务。 目标是提供多功能的 UI 和简单的框架&#xff0c;以实现和使用越来越多的 copilots&#xff08;AI 助手&…

基于PSO粒子群优化的XGBoost时间序列预测算法matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 XGBoost算法原理 4.2 XGBoost优化 5.算法完整程序工程 1.算法运行效果图预览 (完整程序运行后无水印) 2.算法运行软件版本 matlab2024b 3.部分核心程序 &#xff08;完整版代码包含…

aws(学习笔记第三十三课) 深入使用cdk 练习aws athena

文章目录 aws(学习笔记第三十三课) 深入使用cdk学习内容&#xff1a;1. 使用aws athena1.1 什么是aws athena1.2 什么是aws glue1.2 为什么aws athena和aws glue一起使用 2. 开始练习aws athena2.1 代码链接2.2 整体架构2.3 代码解析2.3.1 创建测试数据的S3 bucket2.3.2 创建保…

基于RAGFlow本地部署DeepSpeek-R1大模型与知识库:从配置到应用的全流程解析

作者&#xff1a;后端小肥肠 &#x1f34a; 有疑问可私信或评论区联系我。 &#x1f951; 创作不易未经允许严禁转载。 姊妹篇&#xff1a; DeepSpeek服务器繁忙&#xff1f;这几种替代方案帮你流畅使用&#xff01;&#xff08;附本地部署教程&#xff09;-CSDN博客 10分钟上手…

SpringBoot 集成logback日志链路追踪

项目场景 有时候一个业务调用链场景&#xff0c;很长&#xff0c;调了各种各样的方法&#xff0c;看日志的时候&#xff0c;各个接口的日志穿插&#xff0c;确实让人头大。 为了解决这个痛点&#xff0c;就使用了 TraceId&#xff0c;根据 TraceId 关键字进入服务器查询日志中…

【Python办公】Excel通用匹配工具(双表互匹)

目录 专栏导读1、背景介绍2、库的安装3、核心代码4、完整代码总结专栏导读 🌸 欢迎来到Python办公自动化专栏—Python处理办公问题,解放您的双手 🏳️‍🌈 博客主页:请点击——> 一晌小贪欢的博客主页求关注 👍 该系列文章专栏:请点击——>Python办公自动化专…

【JAVA】七、基础知识“if+switch+循环结构”详细讲解~简单易懂!

目录 7、逻辑控制 7.1 分支结构 7.1.1 if 语句 语法格式1 语法格式2 语法格式3 7.1.2 switch语句 基本语法 执行流程 7.2 循环结构 7.2.1 while循环 语法格式 7.2.2 Break 7.2.3 Continue 7.2.4 for循环 语法格式 执行过程 7.2.5 do while循环 语法格式 7.3 …

【C++】每日一练(轮转数组)

本篇博客给大家带来的是用C语言来解答轮转数组&#xff01; &#x1f41f;&#x1f41f;文章专栏&#xff1a;每日一练 &#x1f680;&#x1f680;若有问题评论区下讨论&#xff0c;我会及时回答 ❤❤欢迎大家点赞、收藏、分享&#xff01; 今日思想&#xff1a;不服输的少年啊…

Python(学习一)

做网站有成熟的框架像FLASK、DJANGO、TORNADO&#xff0c;写爬虫有好用到哭的REQUESTS&#xff0c;还有强大到没盆友的SCRAPY 随着NUMPY、SCIPY、MATLOTLIB等众多第三方模块的开发和完善&#xff0c;不仅支持py支持各种数学运算&#xff0c;还可以绘制高质量的2D和3D图像&…

Java中类和对象

类和对象 面向对象的认识类的定义和使用1 类的定义2 类的创建3 类的实例化 构造方法1 构造方法的概念2 构造方法的注意事项 this关键字 面向对象的认识 前言 何为面向对象何为面向过程呢&#xff1f;&#xff0c;C语言是最经典的面向过程的语言,但是C语言虽然可以解决一定的问…

文本组件+Image组件+图集

Canvas部分知识补充 元素渲染顺序 以Hierarchy参考 下方物体在上方物体前显示 子物体在父物体前显示 下方物体永远在前显示&#xff0c;无论上方的层次结构 资源导入 绝对路径&#xff1a;C:\Windows\Fonts下的许多字体可以用做UIText的字体资源 图片导入&#xff1a; 1.图…

PyCharm 2019.1.3使用python3.9创建虚拟环境setuptools-40.8.0报错处理

目录 前置&#xff1a; 一劳永逸方法&#xff08;缺最后一步&#xff0c;没有成行&#xff09; step one: 下载高版本的pip、setuptools、virtualenv的tar.gz包 step two: 进入PyCharm安装目录的 helpers 目录下 step three: 下载并安装grep和sed命令&#xff0c;然后执行 …

服务器部署RocketMQ----Docker方式

拉取镜像并创建docker network 按照官方文档提供的方式拉取镜像&#xff1a;docker pull apache/rocketmq:4.9.6 创建一个docker网络&#xff1a;docker network create rocketmq 启动NameServer以及Broker 启动NameServer # 启动NameServer docker run -d --name rmqnames…

【推荐项目】052-用水监控管理系统

052-用水监控管理系统 介绍 用水监控管理系统 springboot java vuejs jdk1.8 当然&#xff0c;以下是一个简洁的用水监控管理系统的功能模块划分&#xff0c;基于Spring Boot&#xff08;JDK 1.8&#xff09;后端和Vue.js前端&#xff1a; 用水监控管理系统功能模块 后端&…

零基础上手Python数据分析 (2):Python核心语法快速入门

写在前面 场景:每周销售数据报表整理 任务描述: 你需要每周从多个Excel文件中汇总销售数据,计算各项指标(销售额、订单量、客单价等),并生成周报。Excel操作痛点: 文件太多,手动打开复制粘贴,效率低下,容易出错。 多个Excel文件,每个都要打开、筛选、复制数据,重复…

游戏引擎学习第160天

回顾和今天的计划 我们没有使用任何游戏引擎和库&#xff0c;完全靠我们自己&#xff0c;使用的是老式的编程方式。 我们已经构建了很多内容&#xff0c;游戏引擎开发也慢慢接近尾声。现在我们已经接近完成了所有为支持游戏开发所需要的工作&#xff0c;接下来将逐步过渡到游戏…

从零搭建微服务项目Pro(第2-2章——JSR303自定义文件校验+整合至微服务公共模块)

前言&#xff1a; JSR 303&#xff0c;即 Bean Validation&#xff0c;是 Java EE 6 中的一项子规范&#xff0c;旨在为 Java Bean 提供一种标准化的数据验证机制。它通过注解的方式&#xff0c;允许开发者在 Java 类的字段或方法上直接定义验证规则&#xff0c;从而将验证逻辑…

如何用URDF文件构建机械手模型并与MoveIt集成

机械手URDF文件的编写 我们用urdf文件来描述我们的机械手的外观以及物理性能。这里为了简便&#xff0c;就只用了基本的圆柱、立方体了。追求美观的朋友&#xff0c;还可以用dae文件来描述机械手的外形。 import re def remove_comments(text):pattern r<!--(.*?)-->…

【训练细节解读】文本智能混合分块(Mixtures of Text Chunking,MoC)引领RAG进入多粒度感知智能分块阶段

喜欢本文可以在主页订阅专栏哟 核心创新&#xff1a;双重评估指标与混合分块架构&#xff1a; 第一章&#xff1a;检索增强生成&#xff08;RAG&#xff09;技术演进与分块挑战 1.1 RAG架构的核心演变 检索增强生成&#xff08;Retrieval-Augmented Generation&#xff09…