当前位置: 首页 > news >正文

Java中对象序列化机制的优化研究

Java中对象序列化机制的优化研究

对象序列化(Serialization)是Java编程中一种非常重要的机制,它允许将对象的状态转换为字节流,从而方便存储或网络传输。然而,Java的默认序列化机制虽然功能强大,但在性能、灵活性和兼容性等方面仍存在一定的不足。本篇文章将深入探讨Java对象序列化机制的优化研究,帮助开发者提高系统的效率与性能。

1. 什么是对象序列化?

对象序列化是指将对象的状态转化为字节流的过程,序列化后的对象可以存储到磁盘或通过网络进行传输。Java通过java.io.Serializable接口实现对象的序列化。

1.1 默认序列化

Java对象实现Serializable接口后,可以通过ObjectOutputStream类来将对象序列化,ObjectInputStream类来将字节流反序列化为对象。下面是一个简单的序列化和反序列化的示例:

import java.io.*;

class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

public class SerializationExample {
    public static void main(String[] args) {
        try {
            // 序列化
            Person person = new Person("John", 30);
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"));
            out.writeObject(person);
            out.close();

            // 反序列化
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser"));
            Person deserializedPerson = (Person) in.readObject();
            in.close();

            System.out.println("反序列化后的对象:" + deserializedPerson);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在上述代码中,我们定义了一个Person类并实现了Serializable接口,之后使用ObjectOutputStream将对象序列化到文件中,再用ObjectInputStream将其反序列化回对象。

2. 默认序列化机制的缺点

虽然Java的默认序列化机制非常简单,但它也存在一些问题,主要包括:

  • 性能问题:默认序列化需要遍历对象的所有字段,并进行逐个序列化,这样会带来不必要的开销,尤其是在对象很大时。
  • 兼容性问题:当类发生变更(如字段添加或删除)时,默认序列化可能会导致反序列化失败。
  • 缺乏灵活性:Java的默认序列化机制是自动的,没有提供对序列化过程的细粒度控制。

因此,为了提高序列化的性能和灵活性,许多开发者选择使用优化的序列化机制。

3. 对象序列化优化的常见方法

3.1 使用transient关键字

transient关键字可以标记那些不需要序列化的字段。这样可以避免不必要的字段序列化,从而提高性能。

例如,如果Person类有一个字段是临时计算的值(比如缓存的年龄),我们可以将其标记为transient,这样它就不会被序列化。

class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private transient int age; // 不序列化这个字段

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

使用transient后,age字段在序列化时不会被写入,反序列化后它会被初始化为默认值(如0)。

3.2 自定义序列化方法

Java允许开发者通过实现readObjectwriteObject方法来自定义序列化和反序列化过程。这使得开发者可以完全控制哪些字段需要被序列化,以及如何序列化。

class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    private void writeObject(ObjectOutputStream out) throws IOException {
        out.defaultWriteObject();  // 写入默认的字段
        out.writeInt(age * 2);  // 将age字段做一些处理再序列化
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
        in.defaultReadObject();  // 读取默认的字段
        this.age = in.readInt() / 2;  // 对反序列化后的age字段进行处理
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

在这个例子中,我们通过自定义writeObjectreadObject方法实现了对age字段的特殊处理。这样可以避免默认序列化行为的一些问题,并提供更高效的序列化。

3.3 使用Externalizable接口

Java提供了Externalizable接口,它比Serializable接口更加灵活,允许开发者完全控制序列化和反序列化的过程。与Serializable不同,Externalizable要求实现两个方法:writeExternal()readExternal()

import java.io.*;

class Person implements Externalizable {
    private String name;
    private int age;

    public Person() {  // 必须有无参构造函数
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeObject(name);
        out.writeInt(age);  // 自定义序列化过程
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        name = (String) in.readObject();
        age = in.readInt();  // 自定义反序列化过程
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

Externalizable的优势在于它提供了更高的灵活性和性能,因为它允许开发者明确控制序列化的细节。

3.4 使用第三方库(如Kryo)

除了Java自带的序列化机制,第三方库(如Kryo)提供了更高效的序列化方式。Kryo是一个高效的Java对象图序列化/反序列化库,速度比Java原生序列化机制要快得多,并且支持更加灵活的对象处理。

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.io.Input;

public class KryoExample {
    public static void main(String[] args) {
        Kryo kryo = new Kryo();
        kryo.register(Person.class);

        Person person = new Person("John", 30);

        // 序列化
        Output output = new Output(1024);
        kryo.writeObject(output, person);
        output.close();

        // 反序列化
        Input input = new Input(1024);
        Person deserializedPerson = kryo.readObject(input, Person.class);
        input.close();

        System.out.println("反序列化后的对象:" + deserializedPerson);
    }
}

Kryo的序列化性能相比Java原生序列化机制有显著提升,尤其适用于需要频繁进行对象序列化的场景。

4. 对象序列化优化的进阶技术

在实际应用中,对于对象序列化的优化不仅限于基础方法,许多复杂的场景还需要更多的定制化和进阶技巧。以下是一些较为高级的优化方案,能够在极端场景下进一步提升性能与可靠性。

4.1 对象图的深度优化

在默认的序列化机制中,所有被引用的对象都会被递归序列化,这在对象图较为复杂时会导致严重的性能问题。一个常见的优化策略是使用浅拷贝,避免递归序列化那些不需要的嵌套对象。

例如,在序列化一个嵌套复杂的对象图时,可以通过在类中手动控制深拷贝和引用关系的方式,来避免不必要的递归序列化,减少不必要的计算和内存消耗。

import java.io.*;

class Employee implements Serializable {
    private String name;
    private transient Department department;  // 临时字段,不序列化

    public Employee(String name, Department department) {
        this.name = name;
        this.department = department;
    }

    public Department getDepartment() {
        return department;
    }

    @Override
    public String toString() {
        return "Employee{name='" + name + "', department=" + department + "}";
    }
}

class Department implements Serializable {
    private String deptName;

    public Department(String deptName) {
        this.deptName = deptName;
    }

    @Override
    public String toString() {
        return "Department{deptName='" + deptName + "'}";
    }
}

public class OptimizedSerialization {
    public static void main(String[] args) {
        try {
            Department dept = new Department("IT");
            Employee emp = new Employee("John", dept);

            // 自定义序列化:浅拷贝,避免递归序列化department对象
            ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("employee.ser"));
            out.writeObject(emp);
            out.close();

            // 反序列化
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("employee.ser"));
            Employee deserializedEmp = (Employee) in.readObject();
            in.close();

            System.out.println("反序列化后的员工:" + deserializedEmp);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

通过对Department字段使用transient关键字并且手动管理引用,我们避免了在序列化时对Department对象进行递归序列化。

4.2 对序列化数据进行压缩

另一种优化方法是在序列化后对字节流进行压缩处理,尤其适用于对象较大且需要频繁传输的场景。Java标准库提供了java.util.zip包来实现压缩和解压缩功能。

import java.io.*;
import java.util.zip.GZIPOutputStream;
import java.util.zip.GZIPInputStream;

public class CompressedSerialization {
    public static void main(String[] args) {
        try {
            Person person = new Person("John", 30);

            // 使用GZIP压缩序列化数据
            ObjectOutputStream out = new ObjectOutputStream(new GZIPOutputStream(new FileOutputStream("person.gz")));
            out.writeObject(person);
            out.close();

            // 解压并反序列化数据
            ObjectInputStream in = new ObjectInputStream(new GZIPInputStream(new FileInputStream("person.gz")));
            Person deserializedPerson = (Person) in.readObject();
            in.close();

            System.out.println("反序列化后的对象:" + deserializedPerson);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

在这个例子中,我们使用GZIPOutputStreamGZIPInputStream对序列化数据进行了压缩和解压。压缩不仅能减小存储空间,还能提高网络传输效率,特别是在对象较大时,效果尤为明显。

4.3 使用对象池减少序列化次数

在某些场景下,频繁地进行序列化和反序列化操作会导致性能瓶颈。如果系统中存在大量重复使用的对象,可以通过对象池(Object Pool)技术来减少重复的序列化操作。

对象池可以缓存一些已序列化的对象,当需要重复使用时直接取出缓存对象,避免了每次都进行序列化/反序列化的开销。

import java.io.*;
import java.util.concurrent.*;

public class ObjectPoolSerialization {
    private static final int POOL_SIZE = 10;
    private static final BlockingQueue<Employee> objectPool = new LinkedBlockingQueue<>(POOL_SIZE);

    public static void main(String[] args) throws InterruptedException {
        // 预先填充对象池
        for (int i = 0; i < POOL_SIZE; i++) {
            Employee emp = new Employee("Employee" + i, new Department("Department" + i));
            objectPool.offer(emp);
        }

        // 模拟获取对象池中的对象
        Employee emp = objectPool.take();
        System.out.println("从池中获取的对象:" + emp);
    }
}

通过使用BlockingQueue等并发工具类,可以在高并发的场景下更高效地管理对象池,减少多次序列化的操作,提高系统的响应速度。

4.4 结合缓存优化序列化

对于一些频繁需要序列化和反序列化的对象,如果对象内容不会频繁变化,可以考虑结合缓存(如Redis)进行优化。通过在缓存中存储序列化结果,避免重复的序列化/反序列化操作。这样不仅能节省CPU和内存开销,还能加速数据访问。

import java.io.*;
import java.util.HashMap;
import java.util.Map;

public class CachedSerialization {
    private static final Map<String, byte[]> cache = new HashMap<>();

    public static byte[] serializeWithCache(Object obj, String key) throws IOException {
        // 检查缓存是否存在
        if (cache.containsKey(key)) {
            return cache.get(key);
        }

        // 序列化对象
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteStream);
        out.writeObject(obj);
        out.flush();

        byte[] data = byteStream.toByteArray();
        cache.put(key, data); // 将序列化结果缓存
        return data;
    }

    public static Object deserializeWithCache(byte[] data) throws IOException, ClassNotFoundException {
        ByteArrayInputStream byteStream = new ByteArrayInputStream(data);
        ObjectInputStream in = new ObjectInputStream(byteStream);
        return in.readObject();
    }

    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person person = new Person("John", 30);
        byte[] serializedPerson = serializeWithCache(person, "person");

        // 从缓存中反序列化
        Person deserializedPerson = (Person) deserializeWithCache(serializedPerson);
        System.out.println("从缓存中反序列化的对象:" + deserializedPerson);
    }
}

在这个例子中,我们实现了一个简单的缓存机制,当序列化对象时先检查缓存,只有在缓存中没有时才进行实际的序列化操作。这可以显著减少序列化的次数,提升系统的响应效率。

4.5 性能评估与监控

无论采用何种优化策略,都需要进行严格的性能评估。通过Java的JMH(Java Microbenchmarking Harness)等工具进行基准测试,帮助开发者识别性能瓶颈并验证优化策略的效果。

4.6 高并发下的序列化优化

在高并发环境下,频繁的序列化操作可能会导致竞争条件和锁的争用。可以通过异步序列化、批量序列化和线程池等技术进行优化。例如,异步序列化能够将序列化任务放入队列中,交给单独的线程处理,从而避免主线程被阻塞。

5. 高效序列化与反序列化的应用场景

在一些特定的应用场景下,选择合适的序列化方式不仅能提升性能,还能解决实际业务中遇到的问题。以下是几个常见的场景,针对这些场景可以采用不同的优化策略,来确保序列化与反序列化的高效运行。

5.1 分布式系统中的数据传输

在分布式系统中,数据需要频繁在不同的节点之间进行传输。为了确保数据的快速传输,序列化效率至关重要。在这种场景下,通常不推荐使用Java默认的序列化机制,因为它相对较慢且体积较大。

优化方法:使用高效的序列化框架

如Apache Avro、Protocol Buffers(Protobuf)、Thrift等,这些框架都提供了比Java原生序列化更高效的序列化机制。

  • Protobuf:是一种语言中立、平台中立的序列化协议。它比Java默认序列化更加紧凑,并且速度更快,广泛应用于分布式系统中。

示例:使用Protobuf进行序列化

import com.google.protobuf.InvalidProtocolBufferException;

public class ProtobufExample {
    public static void main(String[] args) {
        try {
            // 创建一个Person对象
            PersonProtos.Person person = PersonProtos.Person.newBuilder()
                .setName("John")
                .setAge(30)
                .build();

            // 序列化
            byte[] serializedPerson = person.toByteArray();
            System.out.println("序列化数据长度:" + serializedPerson.length);

            // 反序列化
            PersonProtos.Person deserializedPerson = PersonProtos.Person.parseFrom(serializedPerson);
            System.out.println("反序列化后的对象:" + deserializedPerson.getName() + ", " + deserializedPerson.getAge());

        } catch (InvalidProtocolBufferException e) {
            e.printStackTrace();
        }
    }
}

Protobuf的优势在于它生成的字节码较小,反序列化速度快,尤其在高并发、高吞吐的分布式系统中,Protobuf可以显著减少网络传输的延迟。

5.2 微服务架构中的消息传递

在微服务架构中,服务之间通常通过消息队列或事件总线进行异步通信。在这种模式下,系统的消息传递效率和序列化性能变得尤为重要。

优化方法:选择合适的消息中间件与序列化方式
  • Kafka:作为分布式消息队列系统,Kafka的消息传输量非常大,消息内容序列化的性能直接影响到系统的整体吞吐量。
  • 选择合适的序列化协议:例如,使用JSON、Protobuf、Avro等格式来序列化消息数据。JSON格式相对灵活且易于调试,但在数据量大时性能较差。而Protobuf和Avro在序列化时更加高效,特别适合高负载、高并发的环境。
// 示例:使用Avro序列化消息
import org.apache.avro.Schema;
import org.apache.avro.generic.GenericRecord;
import org.apache.avro.generic.GenericData;
import org.apache.avro.io.DatumWriter;
import org.apache.avro.io.DataFileWriter;

import java.io.File;
import java.io.IOException;

public class AvroExample {
    public static void main(String[] args) throws IOException {
        // 定义Avro schema
        String schemaString = "{\"type\":\"record\",\"name\":\"Person\",\"fields\":[{\"name\":\"name\",\"type\":\"string\"},{\"name\":\"age\",\"type\":\"int\"}]}";
        Schema schema = new Schema.Parser().parse(schemaString);

        // 创建Avro记录
        GenericRecord person = new GenericData.Record(schema);
        person.put("name", "John");
        person.put("age", 30);

        // 序列化
        File file = new File("person.avro");
        DatumWriter<GenericRecord> datumWriter = new org.apache.avro.specific.SpecificDatumWriter<>(schema);
        DataFileWriter<GenericRecord> dataFileWriter = new DataFileWriter<>(datumWriter);
        dataFileWriter.create(schema, file);
        dataFileWriter.append(person);
        dataFileWriter.close();

        System.out.println("Avro序列化完成");
    }
}

在这个示例中,我们使用Avro来定义消息的schema,并序列化Person对象。通过使用Avro,我们可以确保在分布式微服务中快速传输大量结构化数据。

5.3 持久化存储中的对象存储

在需要将对象存储到数据库或文件系统时,序列化的效率和数据压缩的能力尤为重要,尤其是在数据库中存储大规模对象时,数据存储和读取的效率直接影响到应用的响应速度和系统的扩展性。

优化方法:结合数据库索引与数据压缩
  • 使用Blob字段存储二进制数据:在关系型数据库中,通常将序列化后的对象作为Blob(二进制大对象)存储。为了提高性能,可以考虑将数据进行压缩处理,减少存储空间并提高读取速度。
  • 结合全文索引和压缩技术:对于需要频繁查询的对象数据,考虑使用索引或全文搜索引擎(如Elasticsearch)来增强检索性能。
import java.io.*;
import java.sql.*;

public class DatabaseSerialization {
    public static void main(String[] args) throws SQLException, IOException, ClassNotFoundException {
        // 数据库连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "password");
        PreparedStatement pstmt = conn.prepareStatement("INSERT INTO objects (data) VALUES (?)");

        // 创建对象并序列化
        Person person = new Person("John", 30);
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteStream);
        out.writeObject(person);
        byte[] serializedData = byteStream.toByteArray();

        // 插入数据库
        pstmt.setBytes(1, serializedData);
        pstmt.executeUpdate();

        // 从数据库中取出并反序列化
        ResultSet rs = conn.createStatement().executeQuery("SELECT * FROM objects");
        if (rs.next()) {
            byte[] data = rs.getBytes("data");
            ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(data));
            Person deserializedPerson = (Person) in.readObject();
            System.out.println("从数据库反序列化的对象:" + deserializedPerson);
        }

        conn.close();
    }
}

在这个示例中,我们将Person对象序列化后作为Blob类型存储在MySQL数据库中。对于数据库中的大量对象数据,使用压缩技术可以大幅度减小存储空间,并且结合合适的查询策略,可以提高存储和查询的效率。

5.4 缓存系统中的序列化

在大多数高性能系统中,缓存(如Redis、Memcached)常被用来存储临时数据。缓存系统中的数据通常需要高效序列化和反序列化,以保证快速访问和最小化内存占用。

优化方法:高效的序列化格式

使用适合缓存系统的高效序列化协议,如JSON、MsgPack、Protobuf等。Redis自带的String类型支持序列化数据,因此选择一个适合的序列化格式可以减少缓存存取的延迟。

import org.msgpack.core.MessagePack;
import org.msgpack.core.MessageBufferPacker;

import java.io.IOException;

public class MsgPackSerialization {
    public static void main(String[] args) throws IOException {
        Person person = new Person("John", 30);

        // 使用MsgPack进行序列化
        MessageBufferPacker packer = MessagePack.newDefaultPacker(System.out);
        packer.packString(person.getName());
        packer.packInt(person.getAge());
        packer.close();

        System.out.println("MsgPack序列化数据完成");
    }
}

在这个示例中,使用MsgPack进行序列化可以显著减少序列化后的数据大小,尤其适合高效的缓存和网络传输。

5.5 网络传输中的数据压缩

在进行网络通信时,尤其是带宽有限的场景中,压缩序列化数据是提高网络性能的一项重要技术。通过压缩,可以显著减少传输的数据量,加快数据传输速度,减少带宽消耗。

优化方法:结合序列化与压缩算法
  • 使用GZIPSnappyLZ4等高效压缩算法对序列化数据进行压缩,降低网络传输的负担。
  • 结合Protobuf等高效的序列化协议,这样可以在保证数据结构的紧凑性和反序列化速度的同时,进一步压缩数据。
import java.io.*;
import java.util.zip.GZIPOutputStream;

public class CompressedNetworkTransfer {
   

 public static void main(String[] args) throws IOException {
        String data = "This is a sample data to be compressed and serialized";

        // 压缩数据
        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
        GZIPOutputStream gzip = new GZIPOutputStream(byteStream);
        gzip.write(data.getBytes());
        gzip.close();
        byte[] compressedData = byteStream.toByteArray();

        // 模拟网络传输
        System.out.println("压缩后的数据长度:" + compressedData.length);
    }
}

在这个示例中,我们通过GZIPOutputStream压缩数据,模拟了网络传输场景。在实际应用中,这种方法能有效减少网络带宽消耗并提高数据传输速度。

6. 总结

Java的对象序列化机制作为Java语言的重要特性之一,广泛应用于数据存储、网络通信、分布式系统等领域。然而,默认的序列化方式存在性能瓶颈,特别是在高并发、大数据量传输和存储的场景下。为了提高系统的效率和响应速度,开发者可以采取多种优化策略。

6.1 序列化优化的关键方法

  • 选择合适的序列化协议:如Protobuf、Avro、Thrift等,这些外部库在性能上优于Java原生的序列化方式,尤其适合在分布式系统和微服务架构中使用。
  • 对象图深度优化:通过减少不必要的递归序列化、使用transient字段等方式减少内存开销。
  • 数据压缩:对序列化后的数据进行压缩(如使用GZIP、Snappy等),可以显著降低存储空间和网络传输的开销,特别适用于大规模数据存储或高负载的网络通信。
  • 对象池与缓存:通过对象池来避免频繁的序列化和反序列化操作,提升性能;通过缓存机制存储序列化后的对象,减少重复计算。
  • 高效的序列化策略:在性能要求较高的场景中,结合并发优化(如异步序列化、批量处理)和高效的序列化框架,进一步减少性能瓶颈。

6.2 应用场景中的实践

  • 分布式系统与微服务架构:在服务之间的数据传输中,使用高效的序列化协议(如Protobuf、Avro)可以显著提升吞吐量,降低延迟。
  • 持久化存储:在数据库和文件系统中存储对象时,通过优化序列化和使用压缩算法,减少存储空间并提高读写效率。
  • 缓存系统:对于高并发访问的缓存数据,采用高效的序列化格式(如MsgPack、Protobuf)和压缩算法,提升系统的整体性能。
  • 网络传输:在网络通信中结合序列化与压缩技术,降低传输的数据量和提高网络带宽的利用率。

6.3 性能评估与监控

序列化优化需要在实际应用中进行性能测试和监控,以确保优化策略能有效提升系统性能。使用工具如JMH进行基准测试、性能分析工具(如VisualVM、YourKit)来识别瓶颈,并根据测试结果进行调整。

6.4 未来展望

随着系统复杂度和数据量的不断增加,序列化优化将成为开发中不可忽视的一个环节。未来,我们可以期待更加高效、更加智能的序列化技术和框架的出现,这些技术将帮助开发者更好地应对复杂系统中的性能挑战。

在实际应用中,开发者需要根据业务需求和技术栈的特点,灵活选择和结合不同的优化方法,不断提升系统的性能和可扩展性。

在这里插入图片描述

http://www.dtcms.com/a/20560.html

相关文章:

  • C++ std::atomic可以使用复杂类型(类和结构体)吗
  • 【C++】vector的使用练习 + 模拟实现
  • pnpm和npm安装TailwindCss
  • 【C++】34.智能指针(1)
  • 2025年免费量化交易软件——PTrade(含开通攻略)
  • JavaScript 中的“无限套娃”与“魔法优化”:递归与尾调用优化(TCO)
  • 2025年前端工程师职业发展的系统性应聘规划
  • 【效率技巧】怎么做思维导图||数学思维||费曼学习法
  • 算法笔记——字典树
  • Leetcode 712. Minimum ASCII Delete Sum for Two Strings
  • 机器学习 - 学习线性模型的重要性
  • 智能交通路线规划:让 AI 帮你躲避拥堵
  • Express 路由路径正则详解
  • Python随笔
  • 大模型炼丹基础--GPU内存计算
  • Redis c++安装使用教程(redis-plus-plus)
  • LabVIEW利用CANopen的Batch SDO写入
  • 乘积最大 之 连续与非联系子数组
  • Linux上Scapy完全指南:从入门到实战攻防
  • 基于SpringBoot实现的大学社团平台系统实现功能六
  • 【16届蓝桥杯寒假刷题营】第2期DAY1I
  • Visionpro 齿轮测量
  • frp与云服务器内网穿透
  • 网上购物|基于SprinBoot+vue的网上购物系统(源码+数据库+文档)
  • 大模型应用开发时如何调试提示词?
  • 深入解析504网关超时错误:服务器通信故障的诊断与解决
  • 每日复盘20250216
  • TensorFlow项目GPU运行 安装步骤
  • ThreadLocal详解
  • 2.buuctf [CISCN 2019 初赛]Love Math