本文主要研究一下tomcat jdbc pool的默认参数及poolSweeper

tomcat jdbc pool 参数默认值

  • initialSize = 10(默认值)
  • maxActive=100(默认值)
  • maxIdle=100(默认值)
  • minIdle=10(默认值)
  • maxWait=30000(默认值)
  • validationQueryTimeout=-1(默认值)
  • testOnBorrow=false(默认值)
  • testOnReturn=false(默认值)
  • testWhileIdel=false(默认值)
  • timeBetweenEvictionRunsMillis=5000(默认值)
  • minEvictableIdleTimeMillis=60000(默认值)
  • accessToUnderlyingConnectionAllowed=true(默认值)
  • removeAbandoned=false(默认值)
  • removeAbandonedTimeout=60(默认值)
  • logAbandoned=false(默认值)
  • validationInterval=3000(默认值)
  • testOnConnect=false(默认值)
  • fairQueue=true(默认值)
  • abandonWhenPercentageFull=0(默认值)
  • maxAge=0(默认值)
  • suspectTimeout=0(默认值)
  • alternateUsernameAllowed=false(默认值)
  • commitOnReturn=false(默认值)
  • rollbackOnReturn=false(默认值)
  • useDisposableConnectionFacade=true(默认值)
  • logValidationErrors=false(默认值)
  • propageInterruptState=false(默认值)
  • ignoreExceptionOnPreLoad=false(默认值)

判断是否开启poolSweeper

tomcat-jdbc-8.5.11-sources.jar!/org/apache/tomcat/jdbc/pool/PoolProperties.java

    @Overridepublic boolean isPoolSweeperEnabled() {boolean timer = getTimeBetweenEvictionRunsMillis()>0;boolean result = timer && (isRemoveAbandoned() && getRemoveAbandonedTimeout()>0);result = result || (timer && getSuspectTimeout()>0);result = result || (timer && isTestWhileIdle() && getValidationQuery()!=null);result = result || (timer && getMinEvictableIdleTimeMillis()>0);return result;}

如果timeBetweenEvictionRunsMillis不大于0,则肯定是关闭的,默认值为5000;即默认为true
之后是如下几个条件满足任意一个则开启

判断条件 默认值 结果
getTimeBetweenEvictionRunsMillis()>0 默认为5000 true
isRemoveAbandoned() && getRemoveAbandonedTimeout()>0 默认removeAbandoned为false,removeAbandonedTimeout为60 false
getSuspectTimeout()>0 默认为0 false
isTestWhileIdle() && getValidationQuery()!=null 默认testWhileIdle为false,常见的mysql,pg,oracle的validationQuery不为空 false
getMinEvictableIdleTimeMillis()>0 默认值为60000 true

默认为true,即会开启poolSweeper

DataSourceConfiguration

spring-boot-autoconfigure-1.4.5.RELEASE-sources.jar!/org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration.java

@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource", matchIfMissing = true)static class Tomcat extends DataSourceConfiguration {@Bean@ConfigurationProperties("spring.datasource.tomcat")public org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) {org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(properties, org.apache.tomcat.jdbc.pool.DataSource.class);DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl());String validationQuery = databaseDriver.getValidationQuery();if (validationQuery != null) {dataSource.setTestOnBorrow(true);dataSource.setValidationQuery(validationQuery);}return dataSource;}}

这里默认会根据连接url判断是哪类数据库,然后默认的常见数据库都有对应的validationQuery

如果有validationQuery,则testOnBorrow会被设置为true

注意,如果使用通用的spring.datasource直接来配置,通用的driver-class-name,url,username和password会被认,validationQuery根据url来自动判断,如果能识别出,则testOnBorrow也会被设置为true,其他的连接池的参数,就需要根据具体实现来具体指定,比如spring.datasource.tomcat.initial-size,否则不生效

validationQuery

  • DatabaseDriver

spring-boot-1.4.5.RELEASE-sources.jar!/org/springframework/boot/jdbc/DatabaseDriver.java

    /*** Apache Derby.*/DERBY("Apache Derby", "org.apache.derby.jdbc.EmbeddedDriver", null,"SELECT 1 FROM SYSIBM.SYSDUMMY1"),/*** H2.*/H2("H2", "org.h2.Driver", "org.h2.jdbcx.JdbcDataSource", "SELECT 1"),/*** HyperSQL DataBase.*/HSQLDB("HSQL Database Engine", "org.hsqldb.jdbc.JDBCDriver","org.hsqldb.jdbc.pool.JDBCXADataSource","SELECT COUNT(*) FROM INFORMATION_SCHEMA.SYSTEM_USERS"),/*** SQL Lite.*/SQLITE("SQLite", "org.sqlite.JDBC"),/*** MySQL.*/MYSQL("MySQL", "com.mysql.jdbc.Driver","com.mysql.jdbc.jdbc2.optional.MysqlXADataSource", "SELECT 1"),/*** Maria DB.*/MARIADB("MySQL", "org.mariadb.jdbc.Driver", "org.mariadb.jdbc.MariaDbDataSource","SELECT 1"),/*** Oracle.*/ORACLE("Oracle", "oracle.jdbc.OracleDriver","oracle.jdbc.xa.client.OracleXADataSource", "SELECT 'Hello' from DUAL"),/*** Postgres.*/POSTGRESQL("PostgreSQL", "org.postgresql.Driver", "org.postgresql.xa.PGXADataSource","SELECT 1"),

关于poolCleaner

tomcat-jdbc-8.5.11-sources.jar!/org/apache/tomcat/jdbc/pool/ConnectionPool.java

    /*** Instantiate a connection pool. This will create connections if initialSize is larger than 0.* The {@link PoolProperties} should not be reused for another connection pool.* @param prop PoolProperties - all the properties for this connection pool* @throws SQLException Pool initialization error*/public ConnectionPool(PoolConfiguration prop) throws SQLException {//setup quick access variables and poolsinit(prop);}public void initializePoolCleaner(PoolConfiguration properties) {//if the evictor thread is supposed to run, start it nowif (properties.isPoolSweeperEnabled()) {poolCleaner = new PoolCleaner(this, properties.getTimeBetweenEvictionRunsMillis());poolCleaner.start();} //end if}

ConnectionPool构造器初始化会调用initializePoolCleaner,判断是否开启poolCleaner,默认配置为true,即会开启poolCleaner

poolCleaner

protected static class PoolCleaner extends TimerTask {protected WeakReference<ConnectionPool> pool;protected long sleepTime;PoolCleaner(ConnectionPool pool, long sleepTime) {this.pool = new WeakReference<>(pool);this.sleepTime = sleepTime;if (sleepTime <= 0) {log.warn("Database connection pool evicter thread interval is set to 0, defaulting to 30 seconds");this.sleepTime = 1000 * 30;} else if (sleepTime < 1000) {log.warn("Database connection pool evicter thread interval is set to lower than 1 second.");}}@Overridepublic void run() {ConnectionPool pool = this.pool.get();if (pool == null) {stopRunning();} else if (!pool.isClosed()) {try {if (pool.getPoolProperties().isRemoveAbandoned()|| pool.getPoolProperties().getSuspectTimeout() > 0)pool.checkAbandoned();if (pool.getPoolProperties().getMinIdle() < pool.idle.size())pool.checkIdle();if (pool.getPoolProperties().isTestWhileIdle())pool.testAllIdle();} catch (Exception x) {log.error("", x);}}}public void start() {registerCleaner(this);}public void stopRunning() {unregisterCleaner(this);}}

这个timer主要的任务如下

任务 执行条件 默认值 结果
checkAbandoned removeAbandoned为true或suspectTimeout大于0 removeAbandoned为false,suspectTimeout为0 false
checkIdle pool.idel.size() > minIdle 默认minIdle为10 --
testAllIdle testWhileIdle为true 默认为false false

由于这些任务是依次往下执行的,默认参数配置可以执行的是checkIdle()
只要removeAbandoned=true或者suspectTimeout大于0,就会执行checkAbandoned()
只要testWhileIdle为true,就会执行testAllIdle()

checkAbandoned

    /*** Iterates through all the busy connections and checks for connections that have timed out*/public void checkAbandoned() {try {if (busy.size()==0) return;Iterator<PooledConnection> locked = busy.iterator();int sto = getPoolProperties().getSuspectTimeout();while (locked.hasNext()) {PooledConnection con = locked.next();boolean setToNull = false;try {con.lock();//the con has been returned to the pool or released//ignore itif (idle.contains(con) || con.isReleased())continue;long time = con.getTimestamp();long now = System.currentTimeMillis();if (shouldAbandon() && (now - time) > con.getAbandonTimeout()) {busy.remove(con);abandon(con);setToNull = true;} else if (sto > 0 && (now - time) > (sto * 1000L)) {suspect(con);} else {//do nothing} //end if} finally {con.unlock();if (setToNull)con = null;}} //while} catch (ConcurrentModificationException e) {log.debug("checkAbandoned failed." ,e);} catch (Exception e) {log.warn("checkAbandoned failed, it will be retried.",e);}}

suspectTimeout大于0,removeAbandoned=true两个条件一个成立就会执行checkAbandoned()
如果removeAbandoned为false,则只会进行suspect判断
如果开启removeAbandoned,那么在连接超过abandonTimeout时执行abandon,否则进入suspect判断
abandon会释放连接,即disconnect/close连接

abandon实例

连接池配置

spring:datasource:driver-class-name: org.postgresql.Driverurl: jdbc:postgresql://localhost:5432/postgres?connectTimeout=60&socketTimeout=60username: postgrespassword: postgresjmx-enabled: truetomcat:initial-size: 1max-active: 5## when pool sweeper is enabled, extra idle connection will be closedmax-idle: 5## when idle connection > min-idle, poolSweeper will start to closemin-idle: 1# PoolSweeper run interval abandon及suspect检测的执行间隔time-between-eviction-runs-millis: 30000remove-abandoned: true# how long a connection should return,if not return regard as leak connectionremove-abandoned-timeout: 10# how long a connection should return, or regard as probably leak connectionsuspect-timeout: 10log-abandoned: trueabandon-when-percentage-full: 0 ## (used/max-active*100f)>=perc -->shouldAbandon, if set 0 always abandon# idle connection idle time before closemin-evictable-idle-time-millis: 60000validation-query: select 1validation-interval: 30000

实例代码

    @Testpublic void testConnAbandon() throws SQLException {Connection connection = dataSource.getConnection();connection.setAutoCommit(false); //NOTE pg 为了设置fetchSize,必须设置为falseString sql = "select * from demo_table";PreparedStatement pstmt;try {pstmt = (PreparedStatement)connection.prepareStatement(sql);pstmt.setFetchSize(10);ResultSet rs = pstmt.executeQuery(); //NOTE 设置Statement执行完成的超时时间,前提是socket的timeout比这个大//NOTE 这里返回了就代表statement执行完成,pg会顺带返回fetchSize大小的第一批数据,mysql不会返回第一批数据int col = rs.getMetaData().getColumnCount();System.out.println("============================");while (rs.next()) { //NOTE 这个的timeout由socket的超时时间设置,oracle.jdbc.ReadTimeout=60000for (int i = 1; i <= col; i++) {System.out.print(rs.getObject(i));}System.out.println("");TimeUnit.SECONDS.sleep(1); //NOTE 这里模拟连接被abandon}System.out.println("============================");} catch (Exception e) {e.printStackTrace();} finally {//close resources}}

报错

2018-01-27 11:48:59.891  WARN 1004 --- [:1517024909680]] o.a.tomcat.jdbc.pool.ConnectionPool      : Connection has been abandoned PooledConnection[org.postgresql.jdbc.PgConnection@6c6bdce1]:java.lang.Exceptionat org.apache.tomcat.jdbc.pool.ConnectionPool.getThreadDump(ConnectionPool.java:1102)at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:807)at org.apache.tomcat.jdbc.pool.ConnectionPool.borrowConnection(ConnectionPool.java:651)at org.apache.tomcat.jdbc.pool.ConnectionPool.getConnection(ConnectionPool.java:198)at org.apache.tomcat.jdbc.pool.DataSourceProxy.getConnection(DataSourceProxy.java:132)at com.demo.JpaDemoApplicationTests.testConnAbandon(JpaDemoApplicationTests.java:59)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:497)at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)at org.junit.runners.ParentRunner.run(ParentRunner.java:363)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)at org.junit.runner.JUnitCore.run(JUnitCore.java:137)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:497)at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)org.postgresql.util.PSQLException: An I/O error occurred while sending to the backend.at org.postgresql.core.v3.QueryExecutorImpl.fetch(QueryExecutorImpl.java:2389)at org.postgresql.jdbc.PgResultSet.next(PgResultSet.java:1841)at com.demo.JpaDemoApplicationTests.testConnAbandon(JpaDemoApplicationTests.java:70)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:497)at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)at org.junit.runners.ParentRunner.run(ParentRunner.java:363)at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)at org.junit.runner.JUnitCore.run(JUnitCore.java:137)at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)at java.lang.reflect.Method.invoke(Method.java:497)at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)
Caused by: java.io.IOException: Stream closedat sun.nio.cs.StreamEncoder.ensureOpen(StreamEncoder.java:45)at sun.nio.cs.StreamEncoder.flush(StreamEncoder.java:140)at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:229)at org.postgresql.core.PGStream.flush(PGStream.java:549)at org.postgresql.core.v3.QueryExecutorImpl.sendSync(QueryExecutorImpl.java:1333)at org.postgresql.core.v3.QueryExecutorImpl.fetch(QueryExecutorImpl.java:2383)... 34 more

小结

  • 对于不同连接池的参数配置,需要额外注意.
  • 关于开启abandon,则会把连接强制关闭掉,这个是全局的

对于在同一个连接执行多个statement的情况,可以使用ResetAbandonedTimer来避免被错误abandon掉连接

  • 在springboot中会根据spring.datasource.url自动识别数据库,同时拿到默认的validationQuery,如果该值不为空,则testOnBorrow会被自动设置为true
  • 由于poolSweeper是每隔time-between-eviction-runs-millis时间执行一次,而且是checkAbandoned,checkIdle,testAllIdle依次往下执行,由于它是采用timer实现的,因此一旦某个时间点的任务延时了,后续也一并延时,并不能保证每隔time-between-eviction-runs-millis时间执行一次,这点需要额外注意。

doc

  • tomcat jdbc pool高级配置
  • tomcat jdbc连接池的suspect、abandon操作解析
  • 浅析tomcat jdbc的ResetAbandonedTimer
  • Java Timer和TimerTask实例教程

聊聊tomcat jdbc pool的默认参数及poolSweeper相关推荐

  1. 聊聊hikari与tomcat jdbc pool的fail fast

    序 本文主要研究在中途数据库挂的情况下,hikari与tomcat jdbc pool的fail fast情况. 实验代码 @Testpublic void testDatabaseDownAndUp ...

  2. Tomcat JDBC Pool使用说明

    Maven依赖 <dependency><groupId>org.apache.tomcat</groupId><artifactId>tomcat-j ...

  3. tomcat7 mysql 连接池_Tomcat7 新的数据库连接池Tomcat jdbc pool介绍和配置

    Tomcat 在 7.0 以前的版本都是使用commons-dbcp做为连接池的实现,但是 dbcp存在一些问题: (1)dbcp 是单线程的,为了保证线程安全会锁整个连接池 (2)dbcp 性能不佳 ...

  4. java的maxrow_聊聊pg jdbc statement的maxRows参数

    序 本文主要解析一下pg jdbc statement的maxRows参数 Statement.setMaxRows void setMaxRows(int max) throws SQLExcept ...

  5. C3P0数据源和Tomcat jdbc数据源的基本配置

    在项目中我们经常需要使用数据源,数据源存储所有建立数据库连接的信息.就象通过指定文件名你可以在文件系统中找到文件一样,通过提供正确的数据源名称,你可以找到相应的数据库连接.下面分别对C3P0数据源和T ...

  6. tomcat jdbc连接池配置属性详解之参数说明

    driverClassName 数据库驱动类,针对mysql填com.mysql.jdbc.Driver username 用户名 password 密码 maxActive 最大允许的连接数 max ...

  7. Tomcat JDBC池–连接泄漏–捕获罪魁祸首

    数据库连接泄漏是可以隐藏的东西,除非特别注意,否则将在系统高峰期最关键的阶段暴露出来. 我们将手动检查所有打开的连接是否已正确关闭. 然后,我们将提供各种代码质量插件来进行扫描和检查. 当连接通过复杂 ...

  8. tomcat jdbc SlowQueryReport的实现解读

    为什么80%的码农都做不了架构师?>>>    ##序 tomcat提供了JdbcInterceptor可以用来监控jdbc的执行情况,默认提供了好几个现成的interceptor可 ...

  9. 在独立Java应用程序中使用Tomcat JDBC连接池

    这是从我们的客人文章W4G伙伴克拉伦斯豪的作者临春3从A按. 您可能会在文章结尾找到本书的折扣券代码,仅适用于Java Code Geeks的读者! 请享用! 在需要数据访问权限的独立Java应用程序 ...

最新文章

  1. ajax传递参数与controller接收参数映射关系
  2. 在python中表白_Python 告白:除了你还是你
  3. vs2013缺少Mvc 怎么办?
  4. WP7应用《OOK随心系列》字体和电子书上传方法说明
  5. LeetCode题组:第322题-零钱兑换
  6. 6000字思考!一篇看懂促销系统的底层逻辑
  7. Android开发者指南(4) —— Application Fundamentals(二)
  8. 太惨了!卖一个月不如小米卖一天,手机一哥仍不甘心
  9. tfhpple解析html中的图片,图文详解使用TFHpple解析html方法
  10. PHP使用echo输出标签设置CSS样式问题
  11. mysql mssql 性能对比_详解mysql分区实验测试--非分区表与分区表的性能对比
  12. dbf转成excel_DBF文件转换成excel工具(DbfToExcel)
  13. 【机器学习】GBDT+LR算法进行特征扩增
  14. 前端度分秒与经纬度互转
  15. cl.nd77.pw index.php,Loop in template
  16. 吐纳六字气法的形成与发展
  17. nginx: [error] CreateFile() “D:\nginx-1.20.1/logs/nginx.pid“ failed (2: The system cannot find the
  18. apk编辑器找Android,教你用安卓神器APK编辑器去除程序广告
  19. 【Devops】【docker】【CI/CD】jenkins 清除工作空间报错Error: Wipe Out Workspace blocked by SCM...
  20. retina屏 适配问题

热门文章

  1. systemverilog硬件设计及建模_Chisel引领敏捷硬件开发浪潮
  2. 怎么修改linux用户名密码忘记,linux passwd命令设置或修改用户忘记密码
  3. 查询已有链表的hashmap_源码分析系列1:HashMap源码分析(基于JDK1.8)
  4. linux内存管理简介,Linux内存管理机制简介
  5. c盘扩展卷是灰色的_技术丨电脑C盘装太满?这几招轻松释放空间
  6. vim trick之 vimrc更改立即生效
  7. php中类的构造函数是,php类与构造函数解析
  8. 7 兼容 因特尔十代_年终抄底十代酷睿 请务必看看它……- ——快科技(驱动之家旗下媒体)-...
  9. 【合并区间】排序 + 双指针
  10. QT如何让窗口放置在屏幕正中间