【JDBC】使用原生JDBC,数据量很大且内存空间较小的情况下,JavaHeap溢出

news2024/12/27 5:43:29

文章目录

  • 使用原生JDBC,数据量很大且内存空间较小的情况下,JavaHeap非常容易溢出
    • 问题背景
      • `java.lang.OutOfMemoryError: Java heap space
      • 错误分析
      • 解决方案
        • 1. **优化数据库查询**
        • 2. **调整 JVM 堆内存**
        • 3. **批量处理数据**
        • 4. **线程池优化**
      • 总结
      • **`ResultSet.getString`** 导致的内存溢出与以下几个关键点有关:
      • **问题来源分析**
      • **代码中的优化点**
        • **1. 查询返回数据过多**
        • **2. 列筛选**
        • **3. 大字段处理**
        • **4. 分批发送到 MQ**
        • **5. 增加 JVM 内存**
        • **6. 避免不必要的 JSON 处理**
      • **总结**
    • 最终处理

使用原生JDBC,数据量很大且内存空间较小的情况下,JavaHeap非常容易溢出

问题背景

在sendRecord方法中 ,通过dbDirectService.getDBRecords获取数据库的数据,然后rs遍历每条记录进行操作。在数据量很大且内存空间较小的情况下,JavaHeap非常容易溢出,该如何改进呢?

public int sendRecord(Connection conn, String timeStart, String timeEnd, DataSwitchControl dataSwitchControl, DataSwitchSubControl
                          dataSwitchSubControl, String driver) throws Exception{
        PreparedStatement ps = null;
        ResultSet rs = null;
        int thisCount = 0;
        try {
            long dbStartTime = System.currentTimeMillis();
            ps = dbDirectService.getDBRecords(conn, timeStart, timeEnd,dataSwitchSubControl.getSrcTbName(),dataSwitchSubControl.getQueryColumn(),driver);
            rs = ps.executeQuery();
            long dbEndTime = System.currentTimeMillis();
            logger.debug("本次数据库耗时: "+(dbEndTime - dbStartTime));
            /*if (rs.first()) {*/
                // 数据库json数组数据转string填入消息体,并放入消息列表
                ResultSetMetaData rsm = rs.getMetaData();
                int columnCount = rsm.getColumnCount();
                //rs.beforeFirst();
                while (rs.next()) {
                    long t1 = System.currentTimeMillis();
                    JSONObject jsonObj = new JSONObject();
                    for (int i = 0; i < columnCount; i++) {
                        String colName = rsm.getColumnName(i + 1);
                        String colType = rsm.getColumnTypeName(i + 1);
                        String value = rs.getString(colName);
                        if (value == null) {
                            value = "";
                        }
                        if(colName.equals("extract_time"))continue;//入库时间字段不做处理
                        jsonObj.put(colName + "|" + colType, value);
                    }

                    MessageModel model = new MessageModel();

                    model.setStartTime(timeStart);
                    model.setEndTime(timeEnd);
                    model.setSrcTbName(dataSwitchSubControl.getSrcTbName());
                    model.setDistTbName(dataSwitchSubControl.getDistTbName());
                    model.setJsonObject(jsonObj);
                    model.setId(String.valueOf(dataSwitchControl.getRowId()));
                    model.setOrderId(rs.getString(dataSwitchSubControl.getRowColumn()));
                    model.setOrderName(dataSwitchSubControl.getRowColumn());
                    long t4 = System.currentTimeMillis();
                    //logger.info("组对象:"+(t4-t1));
                    //发送消息到队列
                    queueFactory.addQueue(model,dataSwitchControl.getBatchFlag());
                    thisCount += 1;
                }
                long sendEndTime = System.currentTimeMillis();
                logger.debug("本次转移任务主键:"+dataSwitchControl.getRowId()+",转移表名:"+dataSwitchSubControl.getSrcTbName()+",发送mq耗时: "+(sendEndTime - dbEndTime)+",记录数: " +thisCount);
            //}
            return thisCount;
        }catch (Exception e)
        {
            throw new Exception(e);
        }finally {
            dbDirectService.closeConnection(null,ps,rs);
        }
    }
 public PreparedStatement getDBRecords(Connection conn, String start, String next, String tabName, String queryColum, String driver) throws Exception {
        PreparedStatement ps = null;
        try {
            String sql = "";
            if (DataSwitchConstants.DB_DRIVER_MYSQL.equals(driver)) {
                sql = "select * from " + tabName + " where " + queryColum + " >= STR_TO_DATE(?, '%Y-%m-%d %H:%i:%s') and " + queryColum + " < STR_TO_DATE(?, '%Y-%m-%d %H:%i:%s')";
            }
            ps = conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
            ps.setFetchSize(50);
            ps.setFetchDirection(ResultSet.FETCH_FORWARD);
            //ps.clearParameters();
            ps.setString(1, start);
            ps.setString(2, next);
            logger.info("sql:" + sql);
        } catch (Exception e) {
            throw new Exception();
        }
        return ps;
    }

`java.lang.OutOfMemoryError: Java heap space

在这里插入图片描述
出现了一个 java.lang.OutOfMemoryError: Java heap space 错误。具体原因分析如下:

从你提供的日志截图来看,出现了一个 java.lang.OutOfMemoryError: Java heap space 错误。具体原因分析如下:


错误分析

  1. 日志核心内容

    • 日志显示错误来源于 数据库查询结果的处理过程中
    • 错误堆栈表明:
      • 数据库操作由 Druid 连接池处理;
      • ResultSet.getString 方法导致了内存溢出;
      • 具体原因可能是从数据库中读取了大量数据导致内存占用过高。
  2. 具体原因

    • 数据库查询语句:
      SELECT * FROM gw_backwards_order_archive_03 
      WHERE extract_time >= STR_TO_DATE(?, '%Y-%m-%d %H:%i:%s') 
        AND extract_time < STR_TO_DATE(?, '%Y-%m-%d %H:%i:%s')
      
      • 查询结果的范围取决于 extract_time 参数的值。
      • 如果查询条件范围过大,可能导致 返回结果集数据量过多
    • 从堆栈可以看到,ResultSet.getString 尝试将大量数据加载到内存中,导致 堆内存不足
  3. 线程模型

    • 日志中的 main-producer-threadPool 表明这个任务由线程池中的某个线程执行。
    • 如果线程池中有多个线程同时执行类似的任务,而每个任务都读取了大量数据,内存使用会进一步增加。
  4. 程序设计可能问题

    • 数据库查询返回的数据集未进行分页或限制,导致一次性加载过多数据到内存。
    • 查询结果被处理为大对象(如字符串拼接、复杂的映射转换等),增加了内存占用。
    • 线程池中多个线程并行处理类似任务,争夺堆内存资源。

解决方案

1. 优化数据库查询
  • 分页查询
    确保数据库查询的返回结果集不会过大,可以通过分页限制返回的数据量。例如:

    SELECT * FROM gw_backwards_order_archive_03 
    WHERE extract_time >= STR_TO_DATE(?, '%Y-%m-%d %H:%i:%s') 
      AND extract_time < STR_TO_DATE(?, '%Y-%m-%d %H:%i:%s') 
    LIMIT ?, ?
    
    • 每次只查询一部分数据,通过分页参数动态调整 OFFSETLIMIT
  • 只查询必要字段
    如果不需要所有字段,使用精确字段代替 SELECT *,例如:

    SELECT field1, field2 FROM gw_backwards_order_archive_03 WHERE ...
    
2. 调整 JVM 堆内存
  • 如果确实需要加载大量数据,可以尝试增大堆内存:

    -Xms512m -Xmx2048m
    
    • -Xms: JVM 初始化堆大小;
    • -Xmx: JVM 最大堆大小。
  • 注意:这只能缓解内存问题,但不能根本解决大数据量问题,还是需要优化查询逻辑。

3. 批量处理数据
  • 不一次性加载所有数据,而是分批次处理:
    int batchSize = 1000;
    while (hasMoreData) {
        List<Data> dataBatch = fetchNextBatch(batchSize);
        processBatch(dataBatch);
    }
    
    这样可以显著降低内存占用。
4. 线程池优化
  • 限制线程池中可同时运行的任务数量,避免内存争用。例如:
    ExecutorService executor = new ThreadPoolExecutor(
        2, // core pool size
        4, // max pool size
        60L, TimeUnit.SECONDS,
        new LinkedBlockingQueue<>(10) // task queue size
    );
    
    • 减少同时运行的任务数量,以控制并发对内存的占用。

总结

你的问题主要是 数据库查询返回结果集过大 导致内存不足。建议按照以下优先级逐步优化:

  1. 优化数据库查询,通过分页和字段选择减少结果集大小;
  2. 分批次处理数据,避免一次性加载所有数据;
  3. 适当调整 JVM 堆内存配置,缓解现有内存压力;
  4. 优化线程池配置,控制并发任务数。

ResultSet.getString 导致的内存溢出与以下几个关键点有关:


问题来源分析

  1. 大量数据加载到内存

    • 你的 SQL 查询 (SELECT *) 没有限制结果集大小,可能会返回大量数据行,所有这些数据会被加载到内存中。
    • 即使你在 PreparedStatement 中设置了 fetchSize(50),这只会影响数据库和 JDBC 驱动之间的数据抓取效率,但数据仍会逐条加载到 JVM 内存中
  2. 全表扫描

    • SQL 查询语句 SELECT * 会将表中的所有字段加载进结果集中,尤其是有大字段(如 TEXTBLOB)时,每一行的数据都会占用较大的内存。
    • 如果表的列较多(例如几十列)或某些列的内容较大(例如 JSON 或 XML 字符串),内存压力会迅速增加。
  3. ResultSet.getString 和 JSON 构造

    • 代码中通过 ResultSet.getString 方法逐一读取每一列的值,并将其存入 JSONObject
      String value = rs.getString(colName);
      if (value == null) {
          value = "";
      }
      jsonObj.put(colName + "|" + colType, value);
      
    • 如果某些列的值是非常大的字符串(例如日志数据、JSON 数据等),则每次读取时都会在内存中分配大量内存来存储这些字符串。
  4. 没有分批处理数据

    • 整个代码逻辑是基于一个大循环从 ResultSet 中逐行读取并处理,没有限制单次任务的内存占用。
    • 即使你设置了批量发送到 MQ,但在每次循环中,每条记录的 JSON 都暂时存储在内存中,累计处理大量记录时会消耗 JVM 堆内存。

代码中的优化点

以下是你的代码可能导致内存问题的关键点,以及相应的优化建议:

1. 查询返回数据过多
  • 问题
    • SELECT * 会查询整个表的数据。
    • 没有分页机制,可能会一次性返回上百万条记录。
  • 优化建议
    • 使用分页查询来限制每次返回的数据量。例如:
      SELECT * FROM table_name 
      WHERE query_column >= ? 
        AND query_column < ? 
      LIMIT 100 OFFSET ?;
      
    • 在代码中使用分页逻辑:
      int pageSize = 100; // 每页记录数
      int offset = 0; // 起始位置
      while (true) {
          String sql = "SELECT * FROM " + tableName + " WHERE query_column >= ? AND query_column < ? LIMIT ? OFFSET ?";
          PreparedStatement ps = conn.prepareStatement(sql);
          ps.setString(1, startTime);
          ps.setString(2, endTime);
          ps.setInt(3, pageSize);
          ps.setInt(4, offset);
          ResultSet rs = ps.executeQuery();
      
          if (!rs.next()) break; // 没有更多数据,退出循环 ps这里实际上埋了一个大问题,数据丢失了
          while (rs.next()) {
              // 处理数据
          }
          offset += pageSize; // 更新分页偏移量
      }
      

2. 列筛选
  • 问题
    • SELECT * 会返回所有列,可能包括许多不必要的列。
  • 优化建议
    • 只查询必要的列,例如:
      SELECT column1, column2, column3 FROM table_name WHERE query_column >= ? AND query_column < ?;
      

3. 大字段处理
  • 问题
    • 数据表中可能存在大字段(例如 TEXTBLOB),getString 读取这些字段会占用大量内存。
  • 优化建议
    • 对于大字段,避免直接使用 ResultSet.getString,而是使用流式读取:
      InputStream inputStream = rs.getBinaryStream("large_column");
      // 使用流读取数据,避免将整个内容加载到内存
      

4. 分批发送到 MQ
  • 问题
    • 当前代码是读取一行数据就立即发送到 MQ,但如果某个批次数据量太大,仍然会导致内存积压。
  • 优化建议
    • 使用批量发送模式,例如将一定数量的 MessageModel 累积后再发送:
      List<MessageModel> messageBatch = new ArrayList<>();
      while (rs.next()) {
          // 构造 MessageModel
          MessageModel model = new MessageModel();
          // ...
          messageBatch.add(model);
      
          // 当累积到一定数量时发送
          if (messageBatch.size() >= 100) {
              queueFactory.addBatchQueue(messageBatch);
              messageBatch.clear();
          }
      }
      // 发送剩余数据
      if (!messageBatch.isEmpty()) {
          queueFactory.addBatchQueue(messageBatch);
      }
      

5. 增加 JVM 内存
  • 问题
    • 如果数据量确实过大,JVM 堆内存不足也是导致问题的原因之一。
  • 优化建议
    • 增大 JVM 堆内存,例如:
      java -Xms512m -Xmx4g -jar your-application.jar
      
    • 但这只是缓解手段,根本问题还是要限制数据量。

6. 避免不必要的 JSON 处理
  • 问题
    • 每行数据都会被封装成一个 JSONObject,这会增加额外的内存开销。
  • 优化建议
    • 使用更轻量级的对象(如简单的 POJO 或 Map)来代替 JSONObject

总结

内存溢出的核心原因是:SQL 查询返回的数据量太大ResultSet.getString 读取数据时占用内存过多。优化建议包括:

  1. 使用分页查询限制数据量。
  2. 避免 SELECT *,只查询必要的列。
  3. 对大字段使用流式读取。
  4. 使用批量发送模式减少内存压力。
  5. 增加 JVM 堆内存作为辅助手段。

可以先从查询限制(分页和列筛选)开始优化!

最终处理

  1. 分页
  2. 减少jsonObject中不必要的字段

分页如上述所讲的,加上 limit 和 offset的参数,在外层需用while将数据全部取出,判定没数据需要if (!rs.next()) break; // 没有更多数据,退出循环后面需用while (rs.next())取数据,需注意if (!rs.next()) break;执行后实际上浪费了一条数据!因此需要rs.beforeFirst(); // 重新设置游标到数据的起始位置使用这个需要注意不能使用ResultSet.TYPE_FORWARD_ONLY,会抛出异常!

public int sendRecord(Connection conn, String timeStart, String timeEnd, DataSwitchControl dataSwitchControl, DataSwitchSubControl
            dataSwitchSubControl, String driver) throws Exception {
        PreparedStatement ps = null;
        ResultSet rs = null;
        int thisCount = 0;
        try {
            int pageSize = 100; // 每页记录数
            int offset = 0; // 起始位置
            while (true) {
                long dbStartTime = System.currentTimeMillis();
                ps = dbDirectService.getDBRecords(conn, timeStart, timeEnd, dataSwitchSubControl.getSrcTbName(), dataSwitchSubControl.getQueryColumn(), driver,pageSize ,offset );
                rs = ps.executeQuery();
                long dbEndTime = System.currentTimeMillis();
                logger.debug("本次数据库耗时: " + (dbEndTime - dbStartTime));
                /*if (rs.first()) {*/
                // 数据库json数组数据转string填入消息体,并放入消息列表
                ResultSetMetaData rsm = rs.getMetaData();
                int columnCount = rsm.getColumnCount();

                if (!rs.next()) break; // 没有更多数据,退出循环

                rs.beforeFirst();  // 重新设置游标到数据的起始位置
                //rs.beforeFirst();
                while (rs.next()) {
                    JSONObject jsonObj = new JSONObject();
                    for (int i = 0; i < columnCount; i++) {
                        String colName = rsm.getColumnName(i + 1);
                        if (colName.equals("extract_time")) continue;//入库时间字段不做处理

                        String value = rs.getString(colName);
                        if (value == null) {
                            value = "";
                        }
                        jsonObj.put(colName, value);
                    }

                    //DATA_SWITCH_CONFIG表ROW_ID+主键
                    MessageModel model = new MessageModel();
//                    model.setProcessingType(dataSwitchControl.getProcessingType());
                    model.setStartTime(timeStart);
                    model.setEndTime(timeEnd);
                    /*model.setSrcTbName(dataSwitchSubControl.getSrcTbName());*/
                    model.setDistTbName(dataSwitchSubControl.getDistTbName());
                    model.setJsonObject(jsonObj);
//                    model.setId(String.valueOf(dataSwitchControl.getRowId()));
                    model.setOrderId(rs.getString(dataSwitchSubControl.getRowColumn()));
                    model.setOrderName(dataSwitchSubControl.getRowColumn());
                    logger.info("MessageModel:" + JSONObject.toJSONString(model));
                    //发送消息到队列
                    queueFactory.addQueue(model, dataSwitchControl.getBatchFlag());
                    thisCount += 1;
                }
                offset += pageSize; // 更新分页偏移量

            }
            return thisCount;
        } catch (Exception e) {
            throw new Exception(e);
        } finally {
            dbDirectService.closeConnection(null, ps, rs);
        }
    }
public PreparedStatement getDBRecords(Connection conn, String start, String next, String tabName, String queryColum, String driver, int pageSize, int offset) throws Exception {
        PreparedStatement ps = null;
        try {
            String sql = "";
            if (DataSwitchConstants.DB_DRIVER_MYSQL.equals(driver)) {
                sql = "select * from " + tabName + " where " + queryColum + " >= STR_TO_DATE(?, '%Y-%m-%d %H:%i:%s') and " + queryColum + " < STR_TO_DATE(?, '%Y-%m-%d %H:%i:%s') LIMIT  ?  OFFSET ?";
            }
            //ps = conn.prepareStatement(sql,ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_READ_ONLY);
            ps = conn.prepareStatement(sql, ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
            ps.setFetchSize(50);
            ps.setFetchDirection(ResultSet.FETCH_FORWARD);
            //ps.clearParameters();
            ps.setString(1, start);
            ps.setString(2, next);
            ps.setInt(3, pageSize);
            ps.setInt(4, offset);
            logger.info("sql:" + sql);
        } catch (Exception e) {
            throw new Exception();
        }
        return ps;
    }

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

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

相关文章

uniapp 微信小程序 数据空白展示组件

效果图 html <template><view class"nodata"><view class""><image class"nodataimg":src"$publicfun.locaAndHttp()?localUrl:$publicfun.httpUrlImg(httUrl)"mode"aspectFit"></image>&l…

12.26 学习卷积神经网路(CNN)

完全是基于下面这个博客来进行学习的&#xff0c;感谢&#xff01; ​​【深度学习基础】详解Pytorch搭建CNN卷积神经网络LeNet-5实现手写数字识别_pytorch cnn-CSDN博客 基于深度神经网络DNN实现的手写数字识别&#xff0c;将灰度图像转换后的二维数组展平到一维&#xff0c;…

【团标】《信息工程造价政务信息化项目造价评估方法》(TCQAE11021-2023)-费用标准解读系列33

《信息工程造价政务信息化项目造价评估方法》&#xff08;TCQAE11021-2023&#xff09;是中国电子质量管理协会2023年发布&#xff0c;2023年12月16日开始实施的标准&#xff08;了解更多可直接关注我们咨询&#xff09;。该标准适用于政务信息化项目的造价评估&#xff0c;政务…

mybatisplu设置自动填充

/*** mybatisplus的自动化填充*/public class JboltMetaObjectHandler implements MetaObjectHandler {Overridepublic void insertFill(MetaObject metaObject) {LocalDateTime now LocalDateTime.now(ZoneId.of("Asia/Shanghai"));this.strictInsertFill(metaObje…

音视频入门基础:MPEG2-TS专题(23)——通过FFprobe显示TS流每个packet的信息

一、引言 通过FFprobe命令&#xff1a; ffprobe -of json -show_packets XXX.ts 可以显示TS流/TS文件每个packet&#xff08;也称为数据包或多媒体包&#xff09;的信息&#xff1a; 对于TS流&#xff0c;上述的“packet”&#xff08;数据包或多媒体包&#xff09;是指&…

Linux电源管理——CPU Hotplug 流程

目录 一、相关概念 二、基本原理 三、代码分析 1、CPU_ON 2、CPU_OFF References Linux Version&#xff1a;linux-5.4.239 一、相关概念 在单核操作系统中&#xff0c;操作系统只需管理一个CPU&#xff0c;当系统有任务需要执行时&#xff0c;所有的任务会在该CPU的就绪…

探索数据的艺术:R语言与Origin的完美结合

探索数据的艺术&#xff1a;R语言与Origin的完美结合 R语言统计分析与可视化从入门到精通内容简介获取方式 Origin绘图深度解析&#xff1a;科研数据的可视化艺术内容简介获取方式 R语言统计分析与可视化从入门到精通 内容简介 本书循序渐进、深入讲解了R语言数据统计分析与应…

python基础训练之元组的基本操作

主页包含元组基础知识点 【练习要求】 针对于元组的知识点进行常用的创建、定义、查询元素、查看元组长度等操作。效果实现如下 (注&#xff1a;特别要注意一下切片的用法) #创建元组的两种方法 T1 () T2 tuple() #定义一个元组并存储数据张三, 李四, 王五 T3 (张三, 李四…

选煤厂可视化技术助力智能化运营

通过图扑 HT 可视化搭建智慧选煤厂管理平台&#xff0c;优化了选煤生产流程&#xff0c;提高了资源利用率和安全性&#xff0c;助力企业实现智能化运营和可持续发展目标。

C语言基础:指针(数组指针与指针数组)

数组指针与指针数组 数组指针 概念&#xff1a;数组指针是指向数组的指针&#xff0c;本质上还是指针 特点&#xff1a; 先有数组&#xff0c;后有指针 它指向的是一个完整的数组 一维数组指针&#xff1a; 语法&#xff1a; 数据类型 (*指针变量名)[行容量][列容量]; 案…

接口测试Day03-postman断言关联

postman常用断言 注意&#xff1a;不需要手敲&#xff0c;点击自动生成 断言响应状态码 Status code&#xff1a;Code is 200 //断言响应状态码为 200 pm.test("Status code is 200", function () {pm.response.to.have.status(200); });pm: postman的实例 test() …

01- 三自由度串联机械臂位置分析

三自由度串联机械臂如下图所示&#xff08;d180mm&#xff0c;L1100mm&#xff0c;L280mm&#xff09;&#xff0c;利用改进DH法建模&#xff0c;坐标系如下所示&#xff1a; 利用改进DH法建模&#xff0c;该机器人的DH参数表如下所示&#xff1a; 对该机械臂进行位置分析&…

lxml 解析xml\html

from lxml import etree# XML文档示例 xml_doc """ <root><book><title>Python编程指南</title><author>张三</author></book><book><title>Python高级编程</title><author>李四</autho…

用Python写炸金花游戏

文章目录 **代码分解与讲解**1. **扑克牌的生成与洗牌**2. **给玩家发牌**3. **打印玩家的手牌**4. **定义牌的优先级**5. **判断牌型**6. **确定牌型优先级**7. **比较两手牌的大小**8. **计算每个玩家的牌型并找出赢家**9. **打印结果** 完整代码 以下游戏规则&#xff1a; 那…

基于 SpringBoot微信小程序的医院预约挂号系统

摘 要 时代在飞速进步&#xff0c;每个行业都在努力发展现在先进技术&#xff0c;通过这些先进的技术来提高自己的水平和优势&#xff0c;医院预约挂号系统当然不能排除在外。医院预约挂号系统是在实际应用和软件工程的开发原理之上&#xff0c;运用微信开发者、java语言以及…

高仿CSDN编辑器,前端博客模板

高仿CSDN编辑器纯前端模板&#xff0c;使用的js、html、vue、axios等技术&#xff0c;网络请求库已进行封装&#xff0c;可以按需调整界面,需要源码联系(4k左右)。 1.支持代码高亮 2.支持目录点击定位 3.支持文件上传、图片上传&#xff08;需要自己写后端接口&#xff09; 4.M…

【C++11】类型分类、引用折叠、完美转发

目录 一、类型分类 二、引用折叠 三、完美转发 一、类型分类 C11以后&#xff0c;进一步对类型进行了划分&#xff0c;右值被划分纯右值(pure value&#xff0c;简称prvalue)和将亡值 (expiring value&#xff0c;简称xvalue)。 纯右值是指那些字面值常量或求值结果相当于…

在线oj项目 Ubuntu安装vue/cil(vue脚手架)

参考:https://blog.csdn.net/weixin_66062303/article/details/129046198 笔记 参考:https://blog.csdn.net/m0_74352571/article/details/144076227 https://cli.vuejs.org/zh/guide/installation.html 确保nodejs已经安装 npm换源淘宝镜像&#xff08;可以不操作或者使用魔…

Python字符串及正则表达式(十一):正则表达式、使用re模块实现正则表达式操作

前言&#xff1a;在 Python 编程的广阔天地中&#xff0c;字符串处理无疑是一项基础而关键的技能。正则表达式&#xff0c;作为处理字符串的强大工具&#xff0c;以其灵活的模式匹配能力&#xff0c;在文本搜索、数据清洗、格式验证等领域发挥着不可替代的作用。本系列博客已经…

项目37:简易个人健身记录器 --- 《跟着小王学Python·新手》

项目37&#xff1a;简易个人健身记录器 — 《跟着小王学Python新手》 《跟着小王学Python》 是一套精心设计的Python学习教程&#xff0c;适合各个层次的学习者。本教程从基础语法入手&#xff0c;逐步深入到高级应用&#xff0c;以实例驱动的方式&#xff0c;帮助学习者逐步掌…