用Java手写jvm之尝试解析clazz文件内容

news2025/1/12 6:00:32

写在前面

源码 。
本文尝试来解析下class文件的内容,了解了class文件内容后,对我们提升java认知将会带来很大的帮助,有多大呢,不好说,总之很大很大,大到受不了😍😍😍。

1:前置知识

在开始正式解析class文件的结构之前,我们需要对class文件的结构有一个大概的了解,关于这部分内容,你可以参考我的另一篇文章class字节码文件结构是什么样子的? 。

另外还需要对class查找相关知识有一些了解,关于这部分内容你可以参考我的另一篇文章用Java手写jvm之实现查找class 。以下内容涉及到class查找的内容将不做单独讲解,源码部分也不再做单独说明,请知悉!

你还需要对常量池的结构有所了解,关于这部分内容可以参考我的另一篇文章class字节码文件常量池的结构以及都有哪些类型的数据 。

内容稍多,稳住!!!

2:正戏

在Java中万物皆对象,所以先来定义一个代表class文件的对象ClassFile:

/**
 * class字节码映射类
 * ClassFile {
 *      u4       magic;
 *      u2       minor_version;
 *      u2       major_version;
 *      u2       constant_pool_count;
 *      cp_info     constant_pool[constant_pool_count-1];
 *      u2       access_flags;
 *      u2       this_class;
 *      u2       super_class;
 *      u2       interfaces_count;
 *      u2       interfaces[interfaces_count]
 *      u2       fields_count;
 *      field_info    fields[fields_count];
 *      u2       methods_count;
 *      method_info  methods[methods_count];
 *      u2       attributes_count;
 *      attribute_info  attributes[attributes_count]
 * }
 */
public class ClassFile {
    // minor version,小版本,一般是0
    private int minorVersion;
    // major version,主版本,8 52 其他依次+1和-1
    private int majorVersion;
    // 常量池
    private ConstantPool constantPool;
    // 访问修饰符,不同的int值代表不同的访问修饰符
    private int accessFlags;
    // 类信息值 常量池位置
    private int thisClassIdx;
    // 父类信息值 常量池位置
    private int supperClassIdx;
    // 父接口信息值 常量池位置
    private int[] interfaces;
    // 字段信息
    private MemberInfo[] fields;
    // 方法信息
    private MemberInfo[] methods;
    private AttributeInfo[] attributes;

    /**
     * @param classData class对应的字节码二进制内容
     */
    public ClassFile(byte[] classData) {
        ClassReader reader = new ClassReader(classData);
        this.readAndCheckMagic(reader);
        this.readAndCheckVersion(reader);
        this.constantPool = this.readConstantPool(reader);
//        this.accessFlags = reader.readUint16();
        /*
        访问修饰符(十六进制表示)
        ACC_PUBLIC       = 0x0001;
        ACC_PRIVATE      = 0x0002;
        ACC_PROTECTED    = 0x0004;
        ACC_STATIC       = 0x0008;
        ACC_FINAL        = 0x0010;
        ACC_SUPER        = 0x0020;
        ACC_SYNCHRONIZED = 0x0020;
        ACC_VOLATILE     = 0x0040;
        ACC_BRIDGE       = 0x0040;
        ACC_TRANSIENT    = 0x0080;
        ACC_VARARGS      = 0x0080;
        ACC_NATIVE       = 0x0100;
        ACC_INTERFACE    = 0x0200;
        ACC_ABSTRACT     = 0x0400;
        ACC_STRICT       = 0x0800;
        ACC_SYNTHETIC    = 0x1000;
        ACC_ANNOTATION   = 0x2000;
        ACC_ENUM         = 0x4000;
        */
        this.accessFlags = reader.readU2();
//        this.thisClassIdx = reader.readUint16();
        // 当前所属类 utf-8常量值 常量池数组索引地址
        this.thisClassIdx = reader.readU2();
//        this.supperClassIdx = reader.readUint16();
        // 父类
        this.supperClassIdx = reader.readU2();
        // 实现的接口们
        this.interfaces = reader.readUint16s();
        this.fields = MemberInfo.readMembers(reader, constantPool);
        this.methods = MemberInfo.readMembers(reader, constantPool);
        this.attributes = AttributeInfo.readAttributes(reader, constantPool);
    }
    // ...
}

在ClassFile中定义了魔法数字,版本号,常量值,字段信息等,这些数据都来自于class字节码文件,所以我们需要拥有解析class文件的能力,为此,需要再来定义一个class文件的读取类ClassReader:

/**
 * 负责读取class文件信息的类
 */
public class ClassReader {
    /**
     * class文件对应的二进制数据
     */
    private byte[] clazzData;

    public ClassReader(byte[] clazzData) {
        this.clazzData = clazzData;
    }

    /**
     * jvm虚拟机规范,定义了u1,u2,u4三种数据类型,分别表示1字节无符号整数,2字节无符号整数,4字节无符号整数
     * class字节码文件中的各种结构,基本都是通过这3中数据类型来定义的,可参考如下结构体
     * ClassFile {
     *     u4 magic;
     *     u2 minor_version;
     *     u2 major_version;
     *     ...
     * }
     * 说明magic是u4类型,即magic是一个4字节的无符号整数,其他类似
     */
    private final static int JVM_DATA_TYPE_U1 = 1;
    private final static int JVM_DATA_TYPE_U2 = 2;
    private final static int JVM_DATA_TYPE_U4 = 4;

    // u1 转int 读1个字节
    public int readU1() {
//        byte[] val = readByte(1);
        byte[] val = readByte(JVM_DATA_TYPE_U1);
        return byte2int(val);
    }

    // u2 转int 读2个字节
    public int readU2() {
//        byte[] val = readByte(2);
        byte[] val = readByte(JVM_DATA_TYPE_U2);
        return byte2int(val);
    }

    // u4 转long 转long就可以高位填0,就可以作为正数显示
    public long readU4() {
//        byte[] val = readByte(4);
        byte[] val = readByte(JVM_DATA_TYPE_U4);
        String str_hex = new BigInteger(1, val).toString(16);
        return Long.parseLong(str_hex, 16);
    }

    // u4 转int
    public int readU4TInteger(){
        byte[] val = readByte(4);
        return new BigInteger(1, val).intValue();
    }

    public float readUint64TFloat() {
        byte[] val = readByte(8);
        return new BigInteger(1, val).floatValue();
    }

    public long readUint64TLong() {
        byte[] val = readByte(8);
        return new BigInteger(1, val).longValue();
    }

    public double readUint64TDouble() {
        byte[] val = readByte(8);
        return new BigInteger(1, val).doubleValue();
    }

    public int[] readUint16s() {
//        int n = this.readUint16();
        int n = this.readU2();
        int[] s = new int[n];
        for (int i = 0; i < n; i++) {
//            s[i] = this.readUint16();
            s[i] = this.readU2();
        }
        return s;
    }

    public byte[] readBytes(int n) {
        return readByte(n);
    }

    private byte[] readByte(int length) {
        byte[] copy = new byte[length];
        System.arraycopy(clazzData, 0, copy, 0, length);
        System.arraycopy(clazzData, length, clazzData, 0, clazzData.length - length);
        return copy;
    }

    private int byte2int(byte[] val) {
        String str_hex = new BigInteger(1, val).toString(16);
        return Integer.parseInt(str_hex, 16);
    }
}

在我们了解了class文件的结构之后就可以通过该工具类按照u1,u2,u4的长度来读取对应的信息,之后进行封装了。

2.1:常量池

为了表示常量池,我们定义类ConstantPool:

public class ConstantPool {

    private ConstantInfo[] constantInfos;
    private final int siz;

    public ConstantPool(ClassReader reader) {
//        siz = reader.readUint16();
        siz = reader.readU2();
        constantInfos = new ConstantInfo[siz];
        for (int i = 1; i < siz; i++) {

            constantInfos[i] = ConstantInfo.readConstantInfo(reader, this);

            switch (constantInfos[i].tag()) {
                case ConstantInfo.CONSTANT_TAG_DOUBLE:
                case ConstantInfo.CONSTANT_TAG_LONG:
                    i++;
                    break;
            }
        }
    }
    // ...
}

ConstantInfo是代表常量类型的接口,如下:

/**
 * 常量池接口
 */
public interface ConstantInfo {

    int CONSTANT_TAG_CLASS = 7;
    int CONSTANT_TAG_FIELDREF = 9;
    int CONSTANT_TAG_METHODREF = 10;
    int CONSTANT_TAG_INTERFACEMETHODREF = 11;
    int CONSTANT_TAG_STRING = 8;
    int CONSTANT_TAG_INTEGER = 3;
    int CONSTANT_TAG_FLOAT = 4;
    int CONSTANT_TAG_LONG = 5;
    int CONSTANT_TAG_DOUBLE = 6;
    int CONSTANT_TAG_NAMEANDTYPE = 12;
    int CONSTANT_TAG_UTF8 = 1;
    int CONSTANT_TAG_METHODHANDLE = 15;
    int CONSTANT_TAG_METHODTYPE = 16;
    int CONSTANT_TAG_INVOKEDYNAMIC = 18;
}

因为不同类型的常量取值方式都都不尽相同,所以针对每种常量定义对应的子类,如下:
在这里插入图片描述
具体看源码吧!

为了方便测试,我们先来编写一个要解析的类:

public class TestConstantsPool {
    private static final String nameA = "jackkkk";
    private static final int ageB = 56;
    private static final Object objC = new Object();

    public static void main(String[] args) {
        System.out.println("jjjjjjjjjjjjjjjjj");
    }
}

接着如下代码测试:

/**
 * program arguments:-Xthejrepath D:\programs\javas\java1.8/jre -Xthetargetclazz D:\test\itstack-demo-jvm-master\find-class-from-classpath\target\test-classes\com\dahuyou\find\clazz\test\ShowMeByteCode
 */
public class Main {

    public static void main(String[] args) {
        Cmd cmd = Cmd.parse(args);
        if (!cmd.ok || cmd.helpFlag) {
            System.out.println("Usage: <main class> [-options] class [args...]");
            return;
        }
        if (cmd.versionFlag) {
            //注意案例测试都是基于1.8,另外jdk1.9以后使用模块化没有rt.jar
            System.out.println("java version \"1.8.0\"");
            return;
        }
        startJVM(cmd);
    }

    private static void startJVM(Cmd cmd) {
        // 创建classpath
        Classpath cp = new Classpath(cmd.thejrepath, cmd.classpath);
//        System.out.printf("classpath:%s class:%s args:%s\n", cp, cmd.getMainClass(), cmd.getAppArgs());
        System.out.printf("classpath:%s parsed class:%s \n", cp, cmd.thetargetclazz);
        //获取className
//        String className = cmd.getMainClass().replace(".", "/");
        try {
//            byte[] classData = cp.readClass(className);
            /*byte[] classData = cp.readClass(cmd.thetargetclazz.replace(".", "/"));
            System.out.println(Arrays.toString(classData));
            System.out.println("classData:");
            for (byte b : classData) {
                //16进制输出
                System.out.print(String.format("%02x", b & 0xff) + " ");
            }*/
            String clazzName = cmd.thetargetclazz.replace(".", "/");
            // 创建className对应的ClassFile对象
            ClassFile classFile = loadClass(clazzName, cp);
            printClassInfo(classFile);

        } catch (Exception e) {
            System.out.println("Could not find or load main class " + cmd.getMainClass());
            e.printStackTrace();
        }
    }

    private static void printClassInfo(ClassFile cf) {
        System.out.println("version: " + cf.majorVersion() + "." + cf.minorVersion());
        System.out.println("constants count:" + cf.constantPool().getSiz());
        ConstantInfo[] constantInfoArr = cf.constantPool().getConstantInfos();
        System.out.println("-------常量池信息开始------");
        for (int i = 1; i < constantInfoArr.length; i++) {
            System.out.println("常量位置:" + i);
            ConstantInfo constantInfo = constantInfoArr[i];
            if (constantInfo != null) constantInfo.showInfo();
        }
        System.out.println("-------常量池信息结束------");
        /*System.out.format("access flags:0x%x\n", cf.accessFlags());
        System.out.println("this class:" + cf.className());
        System.out.println("super class:" + cf.superClassName());
        System.out.println("interfaces:" + Arrays.toString(cf.interfaceNames()));
        System.out.println("fields count:" + cf.fields().length);
        for (MemberInfo memberInfo : cf.fields()) {
            System.out.format("000000%s \t\t %s\n", memberInfo.name(), memberInfo.descriptor());
        }*/

        /*System.out.println("methods count: " + cf.methods().length);
        for (MemberInfo memberInfo : cf.methods()) {
            System.out.format("%s \t\t %s\n", memberInfo.name(), memberInfo.descriptor());
        }*/
    }

    /**
     * 生成class文件对象
     * @param clazzName
     * @param cp
     * @return
     */
    private static ClassFile loadClass(String clazzName, Classpath cp) {
        try {
            // 获取类class对应的byte数组
            byte[] classData = cp.readClass(clazzName);
            return new ClassFile(classData);
        } catch (Exception e) {
            System.out.println("无法加载到类: " + clazzName);
            return null;
        }
    }

}

然后要配置program arguments,如下:

-Xthejrepath D:\programs\javas\java1.8/jre -Xthetargetclazz D:\test\itstack-demo-jvm-master\try-to-parse-clazz-file\target\test-classes\com\dahuyou\test\TestConstantsPool

运行:

classpath:com.dahuyou.tryy.too.parse.clazz.file.classpath.Classpath@bebdb06 parsed class:D:\test\itstack-demo-jvm-master\try-to-parse-clazz-file\target\test-classes\com\dahuyou\test\TestConstantsPool 
version: 52.0
constants count:47
-------常量池信息开始------
常量位置:1
常量位置:2
tag is: 9, 字段符号引用信息是:{name=out, _type=Ljava/io/PrintStream;}
常量位置:3
tag 是:8, string 常量值是:jjjjjjjjjjjjjjjjj
常量位置:4
常量位置:5
tag 是:7, class 或 interface的符号引用是: java/lang/Object
常量位置:6
tag is: 9, 字段符号引用信息是:{name=objC, _type=Ljava/lang/Object;}
常量位置:7
tag 是:7, class 或 interface的符号引用是: com/dahuyou/test/TestConstantsPool
常量位置:8
tag 是:1, utf8值是:nameA
常量位置:9
tag 是:1, utf8值是:Ljava/lang/String;
常量位置:10
tag 是:1, utf8值是:ConstantValue
常量位置:11
tag 是:8, string 常量值是:jackkkk
常量位置:12
tag 是:1, utf8值是:ageB
常量位置:13
tag 是:1, utf8值是:I
常量位置:14
tag 是:3, integer值是:56
常量位置:15
tag 是:1, utf8值是:objC
常量位置:16
tag 是:1, utf8值是:Ljava/lang/Object;
常量位置:17
tag 是:1, utf8值是:<init>
常量位置:18
tag 是:1, utf8值是:()V
常量位置:19
tag 是:1, utf8值是:Code
常量位置:20
tag 是:1, utf8值是:LineNumberTable
常量位置:21
tag 是:1, utf8值是:LocalVariableTable
常量位置:22
tag 是:1, utf8值是:this
常量位置:23
tag 是:1, utf8值是:Lcom/dahuyou/test/TestConstantsPool;
常量位置:24
tag 是:1, utf8值是:main
常量位置:25
tag 是:1, utf8值是:([Ljava/lang/String;)V
常量位置:26
tag 是:1, utf8值是:args
常量位置:27
tag 是:1, utf8值是:[Ljava/lang/String;
常量位置:28
tag 是:1, utf8值是:<clinit>
常量位置:29
tag 是:1, utf8值是:SourceFile
常量位置:30
tag 是:1, utf8值是:TestConstantsPool.java
常量位置:31
tag 是:12, name and type 是:
常量位置:32
tag 是:7, class 或 interface的符号引用是: java/lang/System
常量位置:33
tag 是:12, name and type 是:
常量位置:34
tag 是:1, utf8值是:jjjjjjjjjjjjjjjjj
常量位置:35
tag 是:7, class 或 interface的符号引用是: java/io/PrintStream
常量位置:36
tag 是:12, name and type 是:
常量位置:37
tag 是:1, utf8值是:java/lang/Object
常量位置:38
tag 是:12, name and type 是:
常量位置:39
tag 是:1, utf8值是:com/dahuyou/test/TestConstantsPool
常量位置:40
tag 是:1, utf8值是:jackkkk
常量位置:41
tag 是:1, utf8值是:java/lang/System
常量位置:42
tag 是:1, utf8值是:out
常量位置:43
tag 是:1, utf8值是:Ljava/io/PrintStream;
常量位置:44
tag 是:1, utf8值是:java/io/PrintStream
常量位置:45
tag 是:1, utf8值是:println
常量位置:46
tag 是:1, utf8值是:(Ljava/lang/String;)V
-------常量池信息结束------

Process finished with exit code 0

可以看到变量名称,变量的值字面量,符号应用信息等都可以正常输出出来了。

2.2:字段信息和方法信息

System.out.format("access flags:0x%x\n", cf.accessFlags());
System.out.println("this class:" + cf.className());
System.out.println("super class:" + cf.superClassName());
System.out.println("interfaces:" + Arrays.toString(cf.interfaceNames()));
System.out.println("fields count:" + cf.fields().length);
for (MemberInfo memberInfo : cf.fields()) {
    System.out.format("字段信息:%s \t\t %s\n", memberInfo.name(), memberInfo.descriptor());
}

System.out.println("methods count: " + cf.methods().length);
for (MemberInfo memberInfo : cf.methods()) {
    System.out.format("方法信息:%s \t\t %s\n", memberInfo.name(), memberInfo.descriptor());
}

输出:

classpath:com.dahuyou.tryy.too.parse.clazz.file.classpath.Classpath@bebdb06 parsed class:D:\test\itstack-demo-jvm-master\try-to-parse-clazz-file\target\test-classes\com\dahuyou\test\TestConstantsPool 
version: 52.0
constants count:47
access flags:0x21
this class:com/dahuyou/test/TestConstantsPool
super class:java/lang/Object
interfaces:[]
fields count:3
字段信息:nameA 		 Ljava/lang/String;
字段信息:ageB 		 I
字段信息:objC 		 Ljava/lang/Object;
methods count: 3
方法信息:<init> 		 ()V
方法信息:main 		 ([Ljava/lang/String;)V
方法信息:<clinit> 		 ()V

Process finished with exit code 0

写在后面

参考文章列表

JVM系列—J2SE8 Class文件 。

class字节码文件结构是什么样子的? 。

用Java手写jvm之实现查找class 。

class字节码文件常量池的结构以及都有哪些类型的数据 。

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

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

相关文章

安泰高压放大器在工业领域中的应用

高压放大器是一种在工业领域中发挥重要作用的电子设备&#xff0c;其功能是将低电压信号放大到更高的电压水平。这种设备在多个工业应用中都发挥着关键作用&#xff0c;提供了稳定、可调节的高电压输出。以下是高压放大器在工业领域中的主要应用&#xff1a; 1.精密仪器和实验室…

尚硅谷电商实时数仓笔记-《二》数仓建模概述

上一篇&#xff1a; 尚硅谷电商实时数仓笔记-《一》数仓概述-CSDN博客 二、数仓建模概述 2.1 数据仓库建模的意义 如果把数据看作图书馆里的书&#xff0c;我们希望看到它们在书架上分门别类地放置&#xff1b; 如果把数据看作城市的建筑&#xff0c;我们希望城市规划布局合理…

面试官没想到一个ArrayList,我都能跟他扯半小时

点赞再看&#xff0c;Java进阶一大半 南哥在stackoverflow社区看到14年前的这么一个问题&#xff1a;Java 的 Vector.add() 和 Vector.addElement() 有什么区别&#xff0c;大家有答案吗&#xff1f; 它们实际上没有区别&#xff01;&#xff01;&#xff01;1996年的JDK 1.0版…

每日一题,力扣leetcode Hot100之56. 合并区间

解题思路&#xff1a; 先按照每个数组的第一个数字sort排序。 每次进来一个数组&#xff0c;如果你的第1项大于别人的第2项&#xff0c;那么直接添加。 否则的话则存在可以合并的区间&#xff0c;然后合并的区间的第2项则为旧的和新的第2项的最大值。 class Solution:def m…

SpringBoot框架简单整合ShardingSphere-JDBC实现MySQL分库分表和读写分离及加密混合

1. 主从配置 1.1 主机1&#xff08;IP:192.168.186.77&#xff09; 1.1.1 docker-compose.yml version: 3.8services:mysql-master:image: mysql:latestcontainer_name: mysql-masterenvironment:MYSQL_ROOT_PASSWORD: 123456MYSQL_USER: masterMYSQL_PASSWORD: 123456MYSQL…

计算机毕业设计Hadoop+Spark旅游景点可视化 旅游景点推荐系统 景区游客满意度预测与优化 Apriori算法 景区客流量预测 旅游大数据 景点规划

### 开题报告 **论文题目&#xff1a;** 基于Spark的旅游景点可视化系统的设计与实现 **研究背景与意义&#xff1a;** 随着旅游业的快速发展&#xff0c;人们对旅游信息的获取和处理需求越来越高。传统的旅游信息系统虽然能够提供静态的数据查询和展示功能&#xff0c;但在…

短视频矩阵管理系统开发

在短视频内容爆炸式增长的今天&#xff0c;如何高效管理多个账号&#xff0c;同时保持内容的创新性和互动性&#xff0c;成为了创作者和品牌面临的重大挑战。短视频矩阵管理系统的开发&#xff0c;正是为了解决这一问题&#xff0c;提供一个全面的解决方案。 多账号管理&#…

本地调试指引文档

在开发组件库时&#xff0c;我们经常需要在真实的项目中测试组件库的功能&#xff0c;所以需要进行本地调试&#xff0c;本文介绍两种组件库本地调试流程&#xff0c; 1.使用beta版本 2.使用npm link 两种都可以作为本地调试的方案&#xff0c;本文作为一个参考资料&#xff0…

【机器学习】机器学习的分类算法以及使用逻辑回归算法进行分类

引言 机器学习中的分类算法是一种监督学习算法&#xff0c;其目的是根据已知的输入和输出&#xff08;即特征和标签&#xff09;来训练模型&#xff0c;以便能够预测未标记数据的类别标签 文章目录 引言一、分类算法1.1 逻辑回归&#xff08;Logistic Regression&#xff09;1.…

新手小白,开放式耳机该如何挑选?自费无广开放式耳机评测

作为一个在耳机领域探索多年的人&#xff0c;看到这个问题&#xff0c;我觉得有必要分享一些实用的经验和见解&#xff0c;希望能帮助到各位新手朋友。 首先要来说的就是在挑选开放式耳机的时候需要注意的几个点: 一、佩戴的稳固性。开放式耳机的设计较为独特&#xff0c;如果…

详细指南丨Apache环境下如何配置HTTPS证书

在Apache环境下部署SSL证书通常涉及几个步骤&#xff0c;包括生成私钥、创建CSR&#xff08;证书签名请求&#xff09;、安装SSL证书以及配置Apache服务器。下面是详细的步骤说明&#xff1a; 1. 准备工作 确认服务器和域名&#xff1a;需要有一台已经设置好的Apache服务器和…

后台管理系统(springboot+vue3+mysql)

系列文章目录 1.SpringBoot整合RabbitMQ并实现消息发送与接收 2. 解析JSON格式参数 & 修改对象的key 3. VUE整合Echarts实现简单的数据可视化 4. List&#xff1c;HashMap&#xff1c;String,String&#xff1e;&#xff1e;实现自定义字符串排序&#xff08;key排序、Val…

规划决策算法(四)---Frenet坐标系

知乎&#xff1a;坐标系转换 1.Frenet 坐标系 什么是 Frenet 坐标系&#xff1a; 为什么使用 Frenet 坐标系&#xff1a; 通常情况&#xff0c;我们只会关注车辆当前距离左右车道线的距离&#xff0c;来判断是否偏离车道&#xff0c;是否需要打方向盘进行方向微调。而不是基于…

学习测试15-实战6-根据说明书建工程

CAN协议说明书&#xff1a;含义 一&#xff0c;得到表 1&#xff0c;先建信号 2&#xff0c;建报文&#xff0c;将对应信号拖入其中 3&#xff0c;建节点&#xff0c;将报文添加进TX msg里 调整起始位 数据库建立完成 二&#xff0c;不需要面板&#xff0c;直接导入数据库&…

OpenCV图像滤波(4)构建图像金字塔函数buildPyramid()的使用

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在计算机视觉和图像处理中&#xff0c;构建图像金字塔&#xff08;Image Pyramid&#xff09;是一种常用的技术&#xff0c;它生成一系列分辨率逐…

怎么保护电脑文件夹?文件夹保护方法大盘点

文件夹是管理电脑数据的重要工具&#xff0c;可以有效避免数据混乱。而为了避免文件夹数据泄露&#xff0c;我们需要严格保护文件夹。下面我们就来盘点一下文件夹的保护方法。 文件夹隐藏 隐藏文件夹是一种简单有效的保护方式&#xff0c;通过隐藏文件夹来避免其他人发现&…

【算法】道格拉斯

一、引言 道格拉斯算法是一种用于曲线拟合的数学方法&#xff0c;特别是在处理曲线插值问题时非常有用。道格拉斯-普克算法&#xff08;Douglas-Peucker Algorithm&#xff09;&#xff0c;简称D-P算法&#xff0c;是一种用于简化多边形或折线的高效算法&#xff0c;由David Do…

如何优化PyTorch以加快模型训练速度?

PyTorch是当今生产环境中最流行的深度学习框架之一。随着模型变得日益复杂、数据集日益庞大&#xff0c;优化模型训练性能对于缩短训练时间和提高生产力变得至关重要。 本文将分享几个最新的性能调优技巧&#xff0c;以加速跨领域的机器学习模型的训练。这些技巧对任何想要使用…

【ROS 最简单教程 002/300】ROS 集成开发环境安装 (虚拟机版): Noetic

&#x1f497; 有遇到安装问题可以留言呀 ~ 当时踩了挺多坑&#xff0c;能帮忙解决的我会尽力 &#xff01; 1. 安装操作系统环境 Linux ❄️ VM / VirtualBox Ubuntu20.04 &#x1f449; 保姆级图文安装教程指路&#xff0c;有经验的话 可以用如下资源自行安装 ITEMREFERENCE…

遥感领域新方向!Mamba+RS论文汇总!

本文总结了将Mamba应用至遥感领域的相关论文&#xff08;14篇&#xff09;&#xff0c;涉及到的论文见文末链接&#xff0c;具体如下&#xff1a; 文章目录 1. 遥感图像处理2. 多/高光谱图像分类3. 变化检测/语义分割4. 遥感图像融合/超分辨率 1. 遥感图像处理 论文题目&#…