对象池(连接池):commons-pool2源码解析:GenericObjectPool的returnObject方法解析

2023-10-31

为什么会有对象池

在实际的应用工程当中,存在一些被频繁使用的、创建或者销毁比较耗时、持有的资源也比较昂贵的一些对象。比如:数据库连接对象、线程对象。所以如果能够通过一种方式,把这类对象统一管理,让这类对象可以被循环利用的话,就可以减少很多系统开销(内存、CPU、IO等),极大的提升应用性能。

Apache Commons Pool

Apache Commons Pool就是一个对象池的框架,他提供了一整套用于实现对象池化的API,以及若干种各具特色的对象池实现。Apache Commons Pool是很多连接池实现的基础,比如DBCP连接池、Jedis连接池等。
Apache Commons Pool有个两个大版本,commons-pool和commons-pool2。commons-pool2是对commons-pool的重构,里面大部分核心逻辑实现都是完全重写的。我们所有的源码分析都是基于commons-pool2。

在commons-pool2中,对象池的核心接口叫做ObjectPool,他定义了对象池的应该实现的行为。

  • addObject方法:往池中添加一个对象。池子里的所有对象都是通过这个方法进来的。
  • borrowObject方法:从池中借走到一个对象。借走不等于删除。对象一直都属于池子,只是状态的变化。
  • returnObject方法:把对象归还给对象池。归还不等于添加。对象一直都属于池子,只是状态的变化。
  • invalidateObject:销毁一个对象。这个方法才会将对象从池子中删除,当然这其中最重要的就是释放对象本身持有的各种资源。
  • getNumIdle:返回对象池中有多少对象是空闲的,也就是能够被借走的对象的数量。
  • getNumActive:返回对象池中有对象对象是活跃的,也就是已经被借走的,在使用中的对象的数量。
  • clear:清理对象池。注意是清理不是清空,改方法要求的是,清理所有空闲对象,释放相关资源。
  • close:关闭对象池。这个方法可以达到清空的效果,清理所有对象以及相关资源。

在commons-pool2中,ObjectPool的核心实现类是GenericObjectPool。

在前面的文章中我已经解析过addObject和borrowObject方法的实现,对应链接地址:

  • addObject方法解析:https://blog.csdn.net/weixin_42340670/article/details/107749058
  • borrowObject方法解析:https://blog.csdn.net/weixin_42340670/article/details/106876879

本文接着解析returnObject方法在GenericObjectPool中的实现。

在讨论具体实现之前,我们还有必要看一下该方法在ObjectPool接口的具体定义是如何描述的。

ObjectPool接口中returnObject解析

/**
* Return an instance to the pool. By contract, <code>obj</code>
* <strong>must</strong> have been obtained using {@link #borrowObject()} or
* a related method as defined in an implementation or sub-interface.
*
* @param obj a {@link #borrowObject borrowed} instance to be returned.
*
* @throws IllegalStateException
*              if an attempt is made to return an object to the pool that
*              is in any state other than allocated (i.e. borrowed).
*              Attempting to return an object more than once or attempting
*              to return an object that was never borrowed from the pool
*              will trigger this exception.
*
* @throws Exception if an instance cannot be returned to the pool
*/

/**
 * 把一个实例(对象)归还给对象池。
 * 按照约定,obj也就是要归还的对象一定是通过borrowObject方法、
 * 或者是实现类或者子接口中的和borrowObject有一定逻辑关系的方法获得的(返回的)的对象。
 *
 * obj 通过 borrowObject借走的,现在要归还的对象。
 *
 * 如果要归还的对象的状态不是allocated状态、或者要归还的对象是一个从来没有被借走的对象。
 * 那么就会触发IllegalStateException异常,标识非法状态。
 *
 * 如果是其他情况导致的不能归还,抛出Exception异常。
 */
void returnObject(T obj) throws Exception;

接口的注释,往往对于逻辑实现都给出了一些明确的指示和要求,比如说:

  • returnObject和borrowObject应该成对的互斥操作。
  • 所谓的borrowObject不是从池子里真的移除。
  • 所谓的returnObject也不是真的往池子里添加。
  • 对象一直都在大池子里,是用状态来区分是被借走了、还是已经归还了(空闲了)

接下来我们再详细的看一下GenericObjectPool对于returnObject的具体实现,是否满足注释中的说法或者要求。

GenericObjectPool类中returnObject解析

public class GenericObjectPool<T> extends BaseGenericObjectPool<T>
        implements ObjectPool<T>, GenericObjectPoolMXBean, UsageTracking<T> {
   
     /*
     * All of the objects currently associated with this pool in any state. It
     * excludes objects that have been destroyed. The size of
     * {@link #allObjects} will always be less than or equal to {@link
     * #_maxActive}. Map keys are pooled objects, values are the PooledObject
     * wrappers used internally by the pool.
     */
    /*
     * allObjects 是一个ConcurrentHashMap,里面存储的就是对象池中所有的对象,ConcurrentHashMap能够保证并发读写安全。
     * 这里面不包括已经被销毁的对象,也就是说被销毁的对象会从allObjects中被移除掉。allObjects的数量应该小于等于maxActive(最大活跃对象数量)的值。
     * ConcurrentHashMap的key是IdentityWrapper类型的对象,他可以包装任何对象T,在这里他包装的T的实例就是业务上产生的最直接的对象(数据库连接对象、线程对象等)。
     * ConcurrentHashMap的value是PooledObject类型的对象,他持有的T的实例也是业务上产生的最直接的对象(数据库连接对象、线程对象等)。
     * 关于IdentityWrapper和PooledObject这两个类(接口)的作用,我们在之前的《addObject方法解析中》有详细解释。
     * addObject方法解析链接:https://blog.csdn.net/weixin_42340670/article/details/107749058
     */ 
    private final Map<IdentityWrapper<T>, PooledObject<T>> allObjects =
        new ConcurrentHashMap<IdentityWrapper<T>, PooledObject<T>>();

    /*
     * 用一个链表结构的阻塞队列来存储空闲对象
     */
    private final LinkedBlockingDeque<PooledObject<T>> idleObjects;

    /*
     * 对象工厂,GenericObjectPool的实现依赖了PooledObjectFactory来生产对象
     */
    private final PooledObjectFactory<T> factory;


    /**
     * {@inheritDoc}
     * <p>
     * If {@link #getMaxIdle() maxIdle} is set to a positive value and the
     * number of idle instances has reached this value, the returning instance
     * is destroyed.
     * <p>
     * If {@link #getTestOnReturn() testOnReturn} == true, the returning
     * instance is validated before being returned to the idle instance pool. In
     * this case, if validation fails, the instance is destroyed.
     * <p>
     * Exceptions encountered destroying objects for any reason are swallowed
     * but notified via a {@link SwallowedExceptionListener}.
     */

     /**
      * 虽然这个方法的作用是把一个不再使用的对象交还给对象池。(对象一直在池子里,所谓的还,就是变更对象的状态而已)
      * 但是,如果maxIdle(最大空闲数量)设置为一个有效的正数值,并且对象池里空闲的对象数量已经达到这个数值了,那么这个要还给对象池的对象可以直接被销毁。
      * 可以正常返还到对象池的情况下,如果testOnReturn设置为true,那么这个要交还的对象在重新入池之前需要校验有效性,如果校验失败,则直接销毁。
      * 在销毁对象时产生的任何异常都会被吞下,并交给SwallowedExceptionListener去处理。
      */
    @Override
    public void returnObject(T obj) {
        // 对象池allObjects是一个Map,他需要一个IdentityWrapper类型的key来获取出包装了原始对象的PooledObject对象。
        // IdentityWrapper的核心作用,是为了消除原始对象(业务对象)T的可能重写的hashcode和equals方法带来的影响。
        PooledObject<T> p = allObjects.get(new IdentityWrapper<T>(obj));
        
        if (p == null) { // 如果p为空,说明这个要还的对象,已经不在池子中了。
            // 不在池子中了,说明有可能被回收了,一个被使用的对象,是如何被回收的呢?
            // 虽然对象状态一直是被使用,如果超过一定时间,也是可能被强制回收的。这就取决于abandonedConfig是如何配置的了。
            // 如果没有配置abandonedConfig,说明就不是被强制回收的,那可能出了什么其他问题,说不清道不明的话,就抛个异常吧。
            if (!isAbandonedConfig()) { 
                throw new IllegalStateException(
                        "Returned object not currently part of this pool");
            } else { // 如果配置了abandonedConfig,那说明应该就是被强制回收了,也算是在计划范围内,所以直接return。
                return; // Object was abandoned and removed
            }
            // 关于abandonedConfig希望能够参考我的另一篇解析,详细了解。
            // abandonedConfig解析:https://blog.csdn.net/weixin_42340670/article/details/107136994
        }

        // 如果p不为空,这里加上了同步锁,保证内部逻辑处理的原子性。因为涉及到最重要的状态判断和变更。
        synchronized(p) { 
            final PooledObjectState state = p.getState(); // 首先获取对象状态
            // 要归还的对象状态就一定应该是ALLOCATED,标识使用中,如果状态不是ALLOCATED,就说明状态不符合预期,抛出个异常。
            if (state != PooledObjectState.ALLOCATED) { 
                throw new IllegalStateException(
                        "Object has already been returned to this pool or is invalid");
            } else {  // 如果状态符合预期
                // 调用PooledObject对象的markReturning方法进行对象状态变更
                // PooledObject的默认实现类DefaultPooledObject的该方法只有一行代码:
                //    state = PooledObjectState.RETURNING; //就是改变一下状态。
                p.markReturning(); 
            }
        }

        long activeTime = p.getActiveTimeMillis(); // 获取对象被使用了多长时间

        if (getTestOnReturn()) { // 如果testOnReturn配置为true,说明还需要校验下有效性

            /*
            调用PooledObjectFactory工厂方法validateObject进行校验对象的有效性,校验失败则返回false
            这个方法,不同的factory有不同的实现方式
            1、大多数据库连接池(比如dbcp的工厂实现PoolableConnectionFactory),默认会发一个select 1语句,校验连接的有效性。
            2、jedis的工厂实现类是JedisFactory,对于该方法的实现是发一条redis的ping命令来校验连接的有效性。
            有的工厂实现捕捉到异常,会直接抛出;有的实现捕捉到异常会忽略,但是最终还是会返回false。
            */     
            if (!factory.validateObject(p)) { 
                try {
                    destroy(p); // 如果校验不通过,则销毁该对象
                } catch (Exception e) {
                    // 如果销毁产生异常,调用swallowException方法,交给SwallowedExceptionListener去处理(如果存在的话)
                    swallowException(e); 
                }
                try {
                    // 如果当前正存在着调用方正在等待获取空闲对象,那么就需要创建一个空闲对象。
                    ensureIdle(1, false); // 这个方法下面会单独解析
                } catch (Exception e) {
                    swallowException(e); // 如果创建对象产生异常,也一样交给SwallowedExceptionListener处理
                }

                // 这个方法主要用来更新一些统计信息:对象池的归还操作一共执行了多少次,最近100个被归还对象的使用时长
                updateStatsReturn(activeTime); 
                return; 
            }
        }

        // 如果能够执行到这里,说明该对象是有效的
        try {
            /*
             passivateObject方法按照PooledObjectFactory接口中对其职责的描述应该叫做:对一个对象进行反初始化,什么意思?
             一个对象从使用中变为空闲,这个时候他本身除了持有很多核心资源外,可能还挂载了其他一些附加轻量的资源或者属性。
             对象池的目的就是为了避免重复的创建这种持有昂贵资源的对象,所以即使是对象状态变为空闲,但是本身的核心资源也不能被释放。
             但是那些外围的轻量的资源和属性是可以被卸载和重置的。所以可以这么理解,passivateObject方法是在对象变为空闲时,重置或者卸载那些非核心的资源和属性的。
             那怎么区分核心和非核心呢? 这个事情是使用方要操心的事情,你如果觉得你的对象持有的都是核心资源,passivateObject你还必须实现,所以你可以再方法内部啥逻辑都不写。其实JedisFactory就是这么干的。

             和passivateObject对应的是activateObject方法,activateObject意思是一个对象在被调用方使用前重新初始化一些附加信息。
             JedisFactory对于activateObject提供了实现,内部逻辑做了redis dbindex的修正动作。
            */
            factory.passivateObject(p);
        } catch (Exception e1) {  // 如果反初始化异常,和上面的解析近乎相同的套路:异常甩锅 + 销毁对象 + 创建空闲对象(基于判定情况) + 更新统计信息
            swallowException(e1); // 异常甩锅
            try {
                destroy(p); // 销毁对象
            } catch (Exception e) {
                swallowException(e); // 异常甩锅
            }
            try {
                ensureIdle(1, false); // 创建空闲对象(基于判定情况)
            } catch (Exception e) {
                swallowException(e); // 异常甩锅
            }
            updateStatsReturn(activeTime); // 更新统计信息
            return;
        }

        // 如果能执行到这里,说明反初始化(轻量级资源卸载)也成功了。

        /*
         deallocate 意思是 de-allocate.
         再回顾一下上面的逻辑,能走到这里,说明p的当前状态为:RETURNING
         调用deallocate目的是把状态更改为:IDLE
         在<RETURNING>和<IDLE>状态过渡的中间,执行了validate、passivate等校验和清理动作。
         如果deallocate返回false,说明状态变更失败,抛出个异常:说明一下对象可能已经是空闲或者失效状态了,这两种状态不支持变更为空闲状态
        */ 
        if (!p.deallocate()) { 
            throw new IllegalStateException(
                    "Object has already been returned to this pool or is invalid");
        }

        // 能执行到这里,说明状态也成功变为IDLE了

        int maxIdleSave = getMaxIdle(); // 获取对象池配置的最大空闲对象数量
        // 这个判断的意思就是,一个正常状态的对象池,目前空闲对象数量已经达到规定的最大值(正数)了,也就没必要再加多余的空闲了,可以直接把还回来的销毁掉。
        if (isClosed() || maxIdleSave > -1 && maxIdleSave <= idleObjects.size()) {
            try {
                destroy(p);  // 销毁
            } catch (Exception e) {
                swallowException(e); // 异常甩锅
            }
        } else { // 如果对象可以被正常归还,那么把对象添加到空闲队列
            if (getLifo()) { // 获取对象池的优先级配置
                idleObjects.addFirst(p); // 如果是后进先出,那么把空闲对象添加到队列开头
            } else {
                idleObjects.addLast(p); // 如果是先进先出,那么把空闲对象添加到队列末尾
            }
            if (isClosed()) { // 判断一下对象池状态,如果是关闭状态,那么调用clear方法,清空对象池
                // Pool closed while object was being added to idle objects.
                // Make sure the returned object is destroyed rather than left
                // in the idle object pool (which would effectively be a leak)
                clear();
            }
        }
        updateStatsReturn(activeTime); // 更新统计信息
    }
}

在上面的方法解析中,提到了如果被归还的对象基于某些原因(有效性校验失败、资源卸载失败)被销毁了,那么需要检查确保是否需要创建空闲对象来满足对象池的要求。当时调用了ensureIdle(1, false)方法,接下来我们就针对ensureIdle方法来做详细的源码解析。

GenericObjectPool类中ensureIdle方法解析

/**
* Tries to ensure that {@code idleCount} idle instances exist in the pool.
* <p>
* Creates and adds idle instances until either {@link #getNumIdle()} reaches {@code idleCount}
* or the total number of objects (idle, checked out, or being created) reaches
* {@link #getMaxTotal()}. If {@code always} is false, no instances are created unless
* there are threads waiting to check out instances from the pool.
*
* @param idleCount the number of idle instances desired
* @param always true means create instances even if the pool has no threads waiting
* @throws Exception if the factory's makeObject throws
*
* 尝试确保对象池中有足够的空闲对象
* 创建新的然后添加到对象池中,直到空闲对象数量达到指定的idleCount。
* 当然前提也是对象池中所有对象的数量不能大于对象池的maxTotal配置。
* idleCount参数:指定要创建多少个空闲对象
* always参数:如果为true,意味着不会关注到底有没有调用方在等待获取对象。如果为false,意味着只有存在着等待获取对象的调用方的时候才会创建对象。

* ensureIdle(1, false) 就意思着:如果当前有调用方在排队等待获取空闲对象,那么我就创建1个空闲对象。
*/
private void ensureIdle(int idleCount, boolean always) throws Exception {
    // 如果idleCount小于1,或者对象池已经关闭,或者always为false但是没有处于等待的调用方。 这不会创建对象。
    if (idleCount < 1 || isClosed() || (!always && !idleObjects.hasTakeWaiters())) {
        return;
    }

    while (idleObjects.size() < idleCount) { // 如果空闲对象数量小于指定的idleCount
        PooledObject<T> p = create(); // 创建一个新对象,“关于create方法可以参见:addObject方法的解析”
        if (p == null) { // 如果p为空,说明创建失败,跳出循环
            // Can't create objects, no reason to think another call to
            // create will work. Give up.
            break;
        }
        if (getLifo()) { // 判断回收策略,然后决定是往队列的开头加还是末尾加
            idleObjects.addFirst(p);
        } else {
            idleObjects.addLast(p);
        }
    }
    if (isClosed()) { // 如果对象池已经处于关闭状态
        // Pool closed while object was being added to idle objects.
        // Make sure the returned object is destroyed rather than left
        // in the idle object pool (which would effectively be a leak)
        clear(); // 清理所有空闲对象,包括刚刚创建的对象,防止内存泄漏
    }
}     
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

对象池(连接池):commons-pool2源码解析:GenericObjectPool的returnObject方法解析 的相关文章

  • java源码解析JavaParser

    package com bootdo jparser import java io File import java io FileNotFoundException import com github javaparser JavaPar
  • pomelo源码解析--新建项目(cli工具: pomelo)

    pomelo怎么新建项目 官方文档 1 安装pomelo 2 新建项目HelloWorld 我简单整理了下创建新项目关键步骤 xff1a 安装pomelo 方式一 xff1a npm install pomelo g 方式二 xff1a g
  • RTKlib源码解析:ppp和rtkpost中的周跳检测函数

    文章目录 前言detslp mwdetslp gfdetslp lldetslp dop 欢迎关注个人公众号 xff1a 导航员学习札记 前言 本文解析了RTKlib ppp c中两个周跳检测函数detslp mw和detslp gf xf
  • Pytorch学习(3) —— nn.Parameter nn.ParameterList nn.ParameterDict 源码解析

    为了更好理解Pytorch基本类的实现方法 xff0c 我这里给出了关于参数方面的3个类的源码详解 此部分可以更好的了解实现逻辑结构 xff0c 有助于后续代码理解 xff0c 学pytorch的话这个不是必须掌握的 xff0c 看不懂也没
  • java使用libreOffice预览word,ppt,txt等文档

    首先电脑上需要下载libreOffice 中文官网下载地址 https zh cn libreoffice org download libreoffice 安装过程较为简单 安装完需要重启完成配置 不要安装在中文目录下 验证libreOf
  • linux服务器安装jdk,maven详细步骤

    linux服务器安装jdk maven以及nginx详细步骤 一 安装jdk 使用命令安装 二 安装maven 1 将下载好的maven包放入linux下自定义文件夹 2 配置maven环境 打开profile文件 命令如下 一 安装jdk
  • 关于Git的一点思考

    GIT历史 很多人都知道 Linus在1991年创建了开源的Linux 从此 Linux系统不断发展 已经成为最大的服务器系统软件了 Linus虽然创建了Linux 但Linux的壮大是靠全世界热心的志愿者参与的 这么多人在世界各地为Lin
  • jsp 实现在线人数统计

    首先写个类 import javax servlet import javax servlet http public class SessionCounter implements HttpSessionListener private
  • Docker基本概念、linux

    Docker的基本概念 一 认识容器 1 1 容器是什么 1 2 对比容器和虚拟机 1 2 1 虚拟化的差异 1 2 2 资源利用总结 二 Docker基本概念 2 1 Docker是什么 2 2 Docker 的思想与核心 2 3 Doc
  • java与C#的比较

    一 C 和java哪个更好 几天前 我的北理工研究生面试 老师问了我这样一个问题 你认为C 和java哪个更好 那么 作为读者的你 会怎么回答这道题呢 其实 在我看来 这道题无非是想问你c 和java有什么异同 同为开发语言 并不能说哪个更
  • 对象池(连接池):commons-pool2源码解析:GenericObjectPool的returnObject方法解析

    为什么会有对象池 在实际的应用工程当中 存在一些被频繁使用的 创建或者销毁比较耗时 持有的资源也比较昂贵的一些对象 比如 数据库连接对象 线程对象 所以如果能够通过一种方式 把这类对象统一管理 让这类对象可以被循环利用的话 就可以减少很多系
  • java内存结构

    一 Java内存分配 1 Java有几种存储区域 寄存器 在CPU内部 开发人员不能通过代码来控制寄存器的分配 由编译器来管理 栈 在Windows下 栈是向低地址扩展的数据结构 是一块连续的内存的区域 即栈顶的地址和栈的最大容量是系统预先
  • JSer做的模式悬浮窗体与Jquery做的分页

    今天在做web开发 上网搜索资料时 无意间发现了JSer 现简单介绍一下JSer JSer是一款类似于jQuery的开源脚本框架 使用JSer 将极大的简化您的javascript开发 使程序代码更加简洁和高效 使用JSer 您几乎无需再考
  • xxl-job的使用及简述原理

    文章目录 前言 1 介绍 2 部署篇 2 1 初始化数据库 2 2 部署调度中心 2 2 1 集群部署 2 3 部署执行器 2 3 1 集群部署 3 使用篇 3 1 设置执行器 3 2 新建任务 3 3 启动任务 3 4 查看日志 4 原理
  • LevelDB源码解析(2) SkipList(跳跃表)

    你也可以通过我的独立博客 www huliujia com 获取本篇文章 背景 SkipList是LevelDB的MemTable使用的底层存储结构 LevelDB实现了一个支持泛型的跳跃表 本文不会具体介绍跳跃表的数据结构 如果读者不了解
  • 快速了解SDK和API的区别

    快速了解SDK和API的区别 SDK 是 Software Development Kit 的缩写 软件开发的工具包 辅助开发某一类软件的相关文档 范例和工具的集合都能叫做SDK SDK被开发出来是为了减少程序员工作量的 如果有公司开发出了
  • 常用DataSource配置

    先介绍配置dataSource的几种方式 1 org springframework jdbc datasource SimpleDriverDataSource 说明 SimpleDriverDataSource使用池化技术 推荐
  • 泛型是实体类的集合,根据某一字段排序

    举例 List
  • 【Unity Optimize】使用对象池(Object Pooling)优化项目

    目录 1 对象池 Object Pooling 介绍 2 实现对象池脚本 3 使用对象池生成Cube 4 效果展示 5 Unity资源商店的对象池插件 1 对象池 Object Pooling 介绍 Unity中的对象池 Object Po
  • Android 相机库CameraView源码解析 (三) : 滤镜相关类说明

    1 前言 这段时间 在使用 natario1 CameraView 来实现带滤镜的 预览 拍照 录像 功能 由于 CameraView 封装的比较到位 在项目前期 的确为我们节省了不少时间 但随着项目持续深入 对于 CameraView 的

随机推荐

  • RocketMQ Bug修复记录

    文章目录 1 Bug详情及解决 1 1 Bug 来龙去脉 1 2 验证这真的是一个BUG 1 2 1 BrokerFixedThreadPoolExecutor 1 2 2 FutureTaskExt 1 2 3 RegisterBroke
  • Ubuntu下通过CMake文件编译CUDA+OpenCV代码操作步骤

    在 CUDA Test 工程中 CUDA测试代码之前仅支持在Windows10 VS2013编译 今天在Ubuntu 14 04下写了一个CMakeLists txt文件 支持在Linux下也可以通过CMake编译CUDA Test工程 C
  • IELTS listening lesson from Simon

    IELTS listening lesson from Simon Video Lines Recorded by Marshal Zheng 文章目录 IELTS listening lesson from Simon overall s
  • Python实验及注意点总结

    第一次上机 实验一 随机生成两个小于100的整数 打印其中一个数的数据类型和存储地址 求这两个数的和 差 积 商 幂运算 import random x random randint 1 100 y random randint 1 100
  • L2TP或者PPTP拨号后只能访问远端内网而无法访问Internet的原因及解决办法

    原理 PPTP L2TP拨号成功后 由于修改了主机的缺省路由 导致访问外网的数据都通过VPN隧道转发 也就是访问远端局域网以及外网的数据都发送给了TL ER6110 6120 从而只能访问远端内网 而不能访问Internet 以 win11
  • Win10相机端无法启动解决方案

    Win10相机端无法启动解决方案 背景 设备驱动正常以及没有被其他应用占用相机端的情况下 偶然发现一个问题 利用第三方软件如微信 QQ以及VS结合Opencv库都可以打开系统摄像头 但是手动打开摄像头总是失败 返回错误码0xA00F4246
  • 前端系列——vue2+高德地图web端开发(使用和引入)

    vue2 高德地图web端开发 使用和引入 前言 基础 准备工作 高德地图的个人开发者注册 高德api网址 1 点击进行注册 2 注册完之后进入控制台 3 创建新应用 4 添加 高德 2 0 新增 创建vue2的项目 npm 引入高德 官方
  • pytorch BUG :.grad=NONE? pytorch参数的使用

    在实验中 输出发现网络的某个新增的参数不更新 在输出 tensor grad NONE 然后查找资料进行修改输出从 tensor 0 9925 device cuda 0 grad fn
  • 面试题目搜集(3)

    本博客的 面试题目搜集系列不错 1 面试题目搜集1 2 面试题目搜集2 3 面试题目搜集3 4 面试题目搜集4 5 面试题目搜集5 6 面试题目搜集6 1 有这样一个数组A 大小为n 相邻元素差的绝对值都是1 如 A 4 5 6 5 6 7
  • R-基础:数据框操作

    title dataframe author intro date 2022 1 20 output html document knitr opts chunk set echo TRUE R基础 在学习中分享 在分享中学习 R中数据框是
  • Java发送附件到邮箱

    1 配置 导入依赖以及在yml中写好邮箱的配置信息
  • Git 详细安装教程【图文讲解】

    目录 一 前言 二 Git 的安装 2 1 Git 的下载 2 2 Git 的安装 2 2 1 使用许可声明 2 2 2 选择安装目录 2 2 3 选择安装组件 2 2 4 选择开始菜单文件夹 2 2 5 选择 Git 默认编辑器 2 2
  • 性能测试常见指标分析

    压力测试 强调极端暴力 稳定性测试 在一定压力下 长时间运行的情况 基准测试 在特定条件下的性能测试 负载测试 不同负载下的表现 容量测试 最优容量 外部指标 从外部看 性能测试主要关注如下三个指标 吞吐量 每秒钟系统能够处理的请求数 任务
  • 技术管理到底管什么

    https mp weixin qq com s QN1OKEFT3DiA82 OAp858Q 前些天从湾区日报上看到美国一家叫 Gusto 的公司 CTO 的文章 他 6 年前开始创业 一开始他是唯一的工程师 几乎 100 时间都在写代码
  • MS Chart 控件学习(一)常见属性

    最常用的属性包括 ChartAreas 增加多个绘图区域 每个绘图区域包含独立的图表组 数据源 用于多个图表类型在一个绘图区不兼容时 AlignmentOrientation 图表区对齐方向 定义两个绘图区域间的对齐方式 Alignment
  • 个人计算机多核cpu好处,电脑cpu核数全开会怎样 对电脑有什么影响

    不少网友听说开启电脑cpu核数可以让电脑性能变高 不知道是不是真的 也有网友担心电脑cpu核数全开会怎样 会不会对电脑有影响 今天小编就跟大家聊下电脑cpu核数全开对电脑影响 首先 想要开启cpu核数 是可以在运行中输入msconfig打开
  • 全球排名前500的网站都是做什么的

    数据来自Alexa权威2016 3 3 Google com Enables users to search the world s information including webpages images and videos Offe
  • HttpClient的基本使用

    HttpClient4 510 API参考 点我进入 HTTP 协议可能是现在 Internet 上使用得最多 最重要的协议了 越来越多的 Java 应用程序需要直接通过 HTTP 协议来访问网络资源 虽然在 JDK 的 java net包
  • 【DirectX11学习02】绘制单个基本几何体的线框

    注 下面涉及的代码都基于这篇文章的内容 https blog csdn net Kurozaki Kun article details 86709050 绘制过程 要在DX上绘制一个基本图形 大体流程有以下几步 给出输入布局 主要是描述顶
  • 对象池(连接池):commons-pool2源码解析:GenericObjectPool的returnObject方法解析

    为什么会有对象池 在实际的应用工程当中 存在一些被频繁使用的 创建或者销毁比较耗时 持有的资源也比较昂贵的一些对象 比如 数据库连接对象 线程对象 所以如果能够通过一种方式 把这类对象统一管理 让这类对象可以被循环利用的话 就可以减少很多系