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

JavaScript的内存管理与垃圾回收

前言

JavaScript提供了高效的内存管理机制,它的垃圾回收功能是自动的。在我们创建新对象、函数、原始类型和变量时,所有这些编程元素都会占用内存。那么JavaScript是如何管理这些元素并在它们不再使用时清理它们的呢?

在本节中,我们将讨论JavaScript中的内存管理的所有内部过程。我们将关注以下主题:

  • JavaScript中的内存生命周期

  • JavaScript中的内存分配

  • 当内存不再使用时的释放

  • JavaScript的垃圾回收

  • JavaScript垃圾回收中的引用概念

  • 标记-清除算法

其他编程语言(例如C语言)支持手动内存管理方法,比如malloc()和free()。相比之下,JavaScript会自动为新对象分配内存,并在不再使用时释放它们。因此,JavaScript是web上速度最快的编程语言之一。

让我们了解JavaScript中的内存管理,深入探讨JavaScript垃圾回收的主题。

内存生命周期

每种编程语言的内存生命周期都是相似的。它按照以下方式工作:

  1. 为新实例分配所需的内存

  2. 在调用实例时使用已分配的内存(读取、写入)

  3. 当实例不再使用时,释放分配的内存

但是,第二部分可能在所有编程语言中有所不同,具体取决于它们的架构和用法。在低级别的语言(例如C语言)中,第一步和第三步是明确的,但在高级语言(例如JavaScript)中,它们通常是隐式的。

JavaScript中的内存分配

值的初始化

在JavaScript中,当值被声明时,它们会自动分配内存。我们不需要手动为创建的变量和对象分配内存。

让我们看下面的例子,解释不同类型的变量和对象的内存分配:


const a = 200; // 为一个数字类型的变量分配内存
const a = 'anystring'; // 为一个字符串类型的变量分配内存const new_obj = {a: 5,b: null,
}; // 为一个对象分配内存并包含值// 就像对象一样,它也会为数组和包含的值分配内存
const my_array = [5, null, 'anystring'];
// JavaScript也会为函数分配内存
function my_function(a) {return a + 5;
} // 它会分配一个函数(可调用对象)// JavaScript也会为函数表达式对象分配内存
anyHtmlElement.addEventListener('click', function() {htmlElement.style.backgroundColor = 'green';
}, true);

从上面的例子中,我们可以看到JavaScript为每个对象、变量和方法分配了值。

读取和写入值

当在JavaScript中使用值时,可以从分配的内存中读取和写入这些值。我们可以从变量或对象属性的分配内存中读取和写入值,甚至可以将参数传递给函数。

在Javascript中,基本数据类型存储在栈内存中,引用数据类型存储在堆内存中,但是引用数据类型会在栈内存中存储一个实际对象的引用。
示例如下:

当内存不再使用时的释放

当需要释放分配的内存时,大多数内存管理问题都发生在这个阶段。在这个阶段,最复杂的任务是确定特定变量、对象或函数何时不再需要。

低级别的编程语言要求开发人员手动指定他们希望释放的分配实例的内存的时间点。

但是,像JavaScript这样的高级语言使用自动内存管理,也就是垃圾回收。

垃圾回收的主要用途是自动监视并找到不再使用的特定块,并将其回收。

然而,内存管理的自动处理完全基于近似值。因此,确定特定内存块是否仍然需要是无法确定的通用问题。这就是JavaScript垃圾回收器登场并高效地管理内存的原因。

现在我们已经讨论了JavaScript中的内存管理,让我们继续我们的主题:垃圾回收。

JavaScript垃圾回收

如前所述,垃圾回收面临的主要问题是自动查找特定内存是否将被使用。在这种情况下,垃圾回收器为解决这个问题实施了一种限制。为了解决这个问题,JavaScript使用了引用的概念。让我们了解引用的概念:

JavaScript垃圾回收中的引用概念

对于自动内存管理,JavaScript依赖于引用的概念。在这个概念中,在内存管理上下文中,如果另一个对象在应用程序中的后续位置被访问(无论是隐式还是显式访问),则将一个对象视为对另一个对象的引用。例如,对象将具有对其原型的引用,这是一个隐式引用,以及对其属性值的引用,这是一个显式引用。

在这种情况下,对象的概念将扩展到比JavaScript中的常规对象更广泛的范围。它还包含了对象的作用域。

引用计数垃圾回收

JavaScript的垃圾回收器遵循计算引用的算法。该算法确定一个对象是否还需要,以确定一个对象是否仍然有其他对象引用它。如果一个对象没有引用指向它,它将被视为垃圾。

让我们通过下面的例子来理解它:


let a = {x: {y: 5}
};
// 这里创建了两个对象:x和y,其中x被另一个y作为其属性之一引用。
// 另一个对象是通过分配给'a'变量来引用的。
// 在这里,给定的对象没有一个可以垃圾回收的理由。let b = a; // 'b'变量是引用对象的第二个引用。
a = 5; // 现在,原来在'a'中的对象将有一个唯一的引用,即'b'变量。
let z = b.x; // 对象的'x'属性的引用。
// 现在,'z'对象将有两个引用:一个作为属性,另一个作为'z'变量。
b = 'chrome'; // 原来在'b'中的对象现在没有引用指向它。
// 然而它的'a'属性仍然被'z'变量引用,所以它不能被释放。
z = null; // 原来在'a'中的对象的'a'属性现在没有引用指向它。它可以被垃圾回收。

限制(循环引用)

JavaScript的垃圾回收器在发现循环引用时存在限制。例如,考虑以下例子:


function myFunction() {const a = {};const b = {};a.x = b;    // a引用了bb.y = a;    // b引用了areturn 'someString';
}
myFunction();

从上面的例子中,我们创建了两个相互引用的对象,形成了一个循环引用的情况。当函数myFunction()完成执行时,它将超出作用域。它们在那时不再需要,但是它们分配的内存将被回收。因此,垃圾收集器不会清理它们的分配。

然而,垃圾收集器的引用计数算法将不会将它们识别为可回收对象,但它们中的每一个都有一个引用,这就是为什么它们都不会被标记为垃圾回收的原因。循环引用问题是应用程序中内存泄漏的原因之一。

IE 6和IE 7维护引用计数垃圾回收器,但没有其他现代引擎使用引用计数垃圾回收器来防止由于循环引用而引发的内存泄漏。

垃圾回收的两种常用方法

标记-清除算法

标记-清除算法减少了“一个对象不再使用”算法的使用,而是专注于“一个对象不可达”方法。

这种方法专注于根(一组对象)。在JavaScript中,根是全局对象。在这种方法中,垃圾收集器从根(全局对象)开始,找到从这些根指向的对象。它引用了从这些根对象指向的所有对象。因此,它找到了所有可达和不可达的对象。

这个算法是引用计数算法的改进。它将一个对象视为不可达,如果它没有引用指向它。因此,在这个算法中,如果对象具有零引用,那么它就不可达。所以,在这个算法中,如果对象具有循环引用,该对象不再持有真实的零引用。

截至2012年,所有现代浏览器都遵循标记-清除算法进行垃圾回收。过去几年中,JavaScript的所有改进都采用了这种方法,而不是其本地方法“一个对象不再需要”。

在第一个例子中,在函数调用后没有两个对象引用任何资源。因此,垃圾收集器将它们视为不可达的,并且回收它们的内存。

这是目前浏览器大多基于标记清除法。我们可以分为两个阶段:

  • 标记:从根节点遍历为每个可以访问到的对象都打上一个标记,表示该对象可达。
  • 清除:在没有可用分块时,对堆内存遍历,若没有被标记为可达对象就将其回收。

优点:实现简单。
缺点:a 内存过于碎片化。
   b 内存分配速度慢。
解决方法:标记-整理法
标记整理法和标记清除法标记阶段一致,只是整理阶段是先将被引用的对象移动到一端,然后清理掉标记的内存。

视图如下:

2、引用计数法

引用计数法就是追踪每个变量被引用的次数,当引用数为0将可以被回收掉。
优点:当引用数为0时会被立即回收
缺点:a 计数器的增减处理频繁,会导致空间的使用效率降低。
   b 循环引用无法收回,导致内存泄漏。
  若有一函数Person中a引用了b,b引用了a。每次调用函数Person,它们的引用计数都不为0,则永远不能被回收。
示例如下:

function Person(){let a={};let b={};a.prop = b;b.prop = a;
}

限制:

在应用程序中可能会有这样的时刻,手动指定哪些内存将被释放会很方便。要释放对象的内存,我们需要手动将其显式设置为不可达。

我们不能手动触发JavaScript垃圾收集器。

分代式垃圾回收机制

把分代式垃圾回收机制单独拎出来是因为涉及内容较多,容易混淆。v8中将内存分成了两个区,新生代和老生代。新生代对象存活时间较短,内存通常支持1~8MB。而老生代存储存活时间较长或常驻内存的对象。对于新老两块内存区域的垃圾回收频率不同,所以V8 采用了两个垃圾回收器来管控。
视图如下:


1、 新生代垃圾回收

新生代垃圾回收通过Scavenge策略进行垃圾回收,在具体实现中,主要采用了一种复制式的方法Cheney算法。Cheney算法将堆内存也分为两个区,一个使用状态的空间我们称为使用区。一个处于闲置状态的空间称为空闲区。新加入的对象都会被存放到使用区,当使用区快被写满时,就执行一次垃圾回收操作。

(1)垃圾回收流程

先对使用区中的活动做标记
标记完成后,将使用区的活动对象复制进空闲区并进行排序
将原先使用区对象占用的空间释放
最后进行角色互换,把空闲区变为使用区,使用区变为空闲区

(2)新生代对象何时会到老生代?

第一种情况:经过多次复制后依然存活的对象会被认为是生命周期较强的对象,会被移到老生代管理。
第二种情况:如果复制一个对象到空闲区时,空闲区空间占用超过25%,那么这个对象将被移到老生代区域。原因是,当完成Scavenge回收后,空闲区转变成使用区,需继续进行内存分配,若占比过大,将会影响后续内存的分配。

(3)并行回收

Javascript是一门单线程语言,它是运行在主线程上的,而在进行垃圾回收的时候就会阻塞Javascript脚本的执行,需等待垃圾回收完毕后再恢复脚本执行,这种行为叫全停顿。那当GC时间过长就会造成页面卡顿问题。那一个人干活慢,n个人一起速度便会是一个人的n倍。程序也一样,我们可以通过引入多个辅助线程来同时处理。因此V8引入了并行回收机制。
新生代对象空间就采用并行策略。在垃圾回收过程中,启动多个线程来负责新生代中的垃圾清理,这些线程同时将对象空间中的数据移到空闲区。由于这个过程中数据地址会发生改变,所以还需要同步更新引用这些对象的指针。

2、老生代垃圾回收

老生代数据大多是存活的对象,不需要时常清除更新,所以采用上面提到的标记清除法来进行垃圾回收。因为上面也提到标记清除后会产生大量内存碎片,所以V8就采用了上文所说的标记整理法来解决这个问题。

(1)增量标记

并行策略虽然可以增加垃圾回收的效率,对于新生代这样存放较小对象的回收器能有很好的优化,但其实还是全停顿式的。对于存放较大对象的老生代来说,这些较大对象GC时哪怕使用并行策略依旧会消耗大量时间。所以V8对老生代进行了优化,从全停顿标记切换到了增量标记。
增量标记:就是将一次GC分成多步小GC标记,让JS和小GC标记交替执行,直到标记完成。
问题来了,小GC标记执行完后是如何暂停执行JS任务,而后又是如何进行下一次小GC 标记?如果执行JS任务时刚被标记好的对象引用又被修改了该当如何?V8解决这两个问题的方法分别是三色标记法和写屏障。

1)解决问题一:三色标记法

标记清理法区分是通过非黑即白的策略,但这样便会出现在增量标记时,内存中黑白都有,我们无法区分下一步是什么?所以采用了三色标记法,使用每个对象的两个标记位和一个标记工作表来实现标记,两个标记位编码三种颜色:黑(11),白(00),灰(10)。

黑色表示对象自身及对象的引用都被标记(已检查状态)
白色表示未被标记的对象(初始状态)
灰色表示自身被标记,自身的引用未被标记(待检查状态)
执行流程如下:

初始将所有对象都是白色
从root对象开始,先将root对象标记为灰色并推入标记工作表中
当收集器从标记工作表中弹出对象并访问他的所有引用对象时,自身灰色就会变成黑色。
将自身的下一个引用对象标记为灰色
一直这样执行下去,直到没有可以被标记为灰色的对象时,剩下的白色对象都是不可达的,进入清理阶段。恢复时从灰色标记对象开始执行。

2)解决问题二:写屏障

为了解决黑色对象在程序执行中被新添加引用或已经标记黑色的被引用对象不再被引用了。写屏障就有了以下两个变化:

不对已标记的黑色对象做处理,因为在之后的GC中也会被清理。
Write-barrier 机制强制不变黑的对象指向白色对象。这个也被称作强三色不变性。所以一旦有黑色对象引用白色对象,该机制会强制将引用的白色对象改为灰色,从而保证下一次增量 GC 标记阶段可以正确标记。

(2)懒性清理

增量标记完后,如果当前内存足以支持代码的快速运行,也没必要立即清理,可让程序先运行,也无需一次性清理完所有垃圾对象,可以按需清理直到所有垃圾对象清理完后再继续增量标记。

并发回收

并发主要发生在工作线程上。当在工作线程(辅助线程)执行GC是,应用程序可以继续在主线程运行并不会被挂起。
这也是有问题的,因为GC也在进行,应用程序也在执行,此时对象的引用关系随时都有可能变化,所以之前做的一些标记就需要改变,所以需要读写锁机制来控制这一点。

总结:

JavaScript提供了高效的内存管理机制。JavaScript自动支持内存管理,对我们来说是透明的。我们不能手动触发JavaScript垃圾收集器。

相关文章:

JavaScript的内存管理与垃圾回收

前言 JavaScript提供了高效的内存管理机制,它的垃圾回收功能是自动的。在我们创建新对象、函数、原始类型和变量时,所有这些编程元素都会占用内存。那么JavaScript是如何管理这些元素并在它们不再使用时清理它们的呢? 在本节中,…...

Neo4j导入数据之JAVA JDBC

目录结构 前言设置neo4j外部访问代码整理maven 依赖java 代码 参考链接 前言 公司需要获取neo4j数据库内容进行数据筛查,neo4j数据库咱也是头一次基础,辛辛苦苦安装好整理了安装neo4j的步骤,如今又遇到数据不知道怎么创建,关关难…...

LeetCode 2878.获取DataFrame的大小

DataFrame players: ------------------- | Column Name | Type | ------------------- | player_id | int | | name | object | | age | int | | position | object | | … | … | ------------------- 编写一个解决方案,计算并显示 players 的 行数和列数。 将结…...

索引失效的 12 种情况

目录 一、未使用索引字段进行查询 二、索引列使用了函数或表达式 三、使用了不等于&#xff08;! 或 <>&#xff09;操作符 四、LIKE 操作符的模糊查询 五、对索引列进行了数据类型转换 六、使用 OR 连接多个条件 七、表中数据量较少 八、索引列上存在大量重复值…...

Spring及工厂模式概述

文章目录 Spring 身世什么是 Spring什么是设计模式工厂设计模式什么是工厂设计模式简单的工厂设计模式通用的工厂设计 总结 在 Spring 框架出现之前&#xff0c;Java 开发者使用的主要是传统的 Java EE&#xff08;Java Enterprise Edition&#xff09;平台。Java EE 是一套用于…...

运维SRE-19 网站Web中间件服务-http-nginx

Ans自动化流程 1.网站集群核心协议&#xff1a;HTTP 1.1概述 web服务&#xff1a;网站服务&#xff0c;网站协议即可. 协议&#xff1a;http协议,https协议 服务&#xff1a;Nginx服务&#xff0c;Tengine服务....1.2 HTTP协议 http超文本传输协议&#xff0c;负责数据在网站…...

C语言—自定义(构造)类型

2.20&#xff0c;17.56 1.只有当我们使用结构体类型定义变量/结构体数组,系统才会为结构体的成员分配内存空间,用于存储对应类型的数据 2.strct 结构体 一起作为结构体类型标识符 嘿嘿暂时先这样&#xff0c;我会回来改的1、定义一个表示公交线路的结构体&#xff0c;要…...

ArcgisForJS如何实现添加含图片样式的点要素?

文章目录 0.引言1.加载底图2.获取点要素的坐标3.添加含图片样式的几何要素4.完整实现 0.引言 ArcGIS API for JavaScript 是一个用于在Web和移动应用程序中创建交互式地图和地理空间分析应用的库。本文在ArcGIS For JavaScript中使用Graphic对象来创建包含图片样式的点要素。 …...

C#之WPF学习之路(2)

目录 控件的父类 DispatcherObject类 DependencyObject类 DependencyObject 类的关键成员和方法 Visual类 Visual 类的主要成员和方法 UIElement类 UIElement 类的主要成员和功能 FrameworkElement类 FrameworkElement 类的主要成员和功能 控件的父类 在 WPF (Windo…...

胶原抗体诱导小鼠关节炎模型

胶原诱导性关节炎小鼠(CIA)作为人类类风湿关节炎模型应用广泛,但CIA引起的关节炎起病比较缓慢&#xff0c;造模周期较长&#xff0c;一般为6-8周(1-12)。Chondrex公司已开发出单一种单克隆抗体合剂诱导的小鼠关节炎模型&#xff08;CAIA&#xff09;&#xff0c;明显缩短了造模…...

集百家所长的开放世界游戏,艾尔莎H311-PRO带你玩转《幻兽帕鲁》

随着近几年开放世界游戏热潮的兴起&#xff0c;如今这类游戏可以说是像雨后春笋般不断推出&#xff0c;比如《幻兽帕鲁》就是近期非常火热的一个代表&#xff0c;它不仅集合了生存、建造、宠物养成等多种元素&#xff0c;而且可爱的卡通画风格更是老少皆宜。那么&#xff0c;这…...

机器人内部传感器阅读笔记及心得-位置传感器-旋转变压器、激光干涉式编码器

旋转变压器 旋转变压器是一种输出电压随转角变化的检测装置&#xff0c;是用来检测角位移的&#xff0c;其基本结构与交流绕线式异步电动机相似&#xff0c;由定子和转子组成。 旋转变压器的原理如图1所示&#xff0c;定子相当于变压器的一次侧&#xff0c;有两组在空间位置上…...

深度学习的学习笔记帖子2

人脸数据集的介绍&#xff1a; https://zhuanlan.zhihu.com/p/362356480 https://blog.csdn.net/bjbz_cxy/article/details/122210641 CASIAWebFace人脸数据集等的github&#xff1a; https://github.com/deepinsight/insightface/blob/master/recognition/datasets/README.md…...

【机器学习学习脉络】

机器学习学习脉络 基础知识 数学基础 线性代数概率论与数理统计微积分最优化理论 编程基础 Python编程语言数据结构与算法软件工程原则 计算机科学基础 操作系统网络通信数据库系统 机器学习概论 定义与发展历程机器学习的主要任务和应用领域基本术语和概念 监督学习 线…...

golang命令行工具gtcli,实现了完美集成与结构化的gin脚手架,gin-restful-api开箱即用

关于gtools golang非常奈斯&#xff0c;gin作为web框架也非常奈斯&#xff0c;但我们在开发过程中&#xff0c;前期搭建会花费大量的时间&#xff0c;且还不尽人意。 为此我集成了gin-restful-api的模板gin-layout&#xff0c;还有脚手架一键生成项目。 集成相关 ginviperz…...

Qt 事件

1. 事件 事件是对各种应用程序需要知道的由应用程序内部或者外部产生的事情或者动作的通称。在Qt中使用一个对象来表示一个事件&#xff0c;它继承自QEvent类。 2. 事件和信号 事件与信号并不相同&#xff0c;比如我们使用鼠标点击了一下界面上的按钮&#xff0c;那么就会产生…...

JAVA高并发——并行算法

文章目录 1、并行流水线2、并行搜索3、并行排序3.1、分离数据相关性&#xff1a;奇偶交换排序3.2、改进的插入排序&#xff1a;希尔排序 4、并行算法&#xff1a;矩阵乘法 1、并行流水线 并行算法虽然可以充分发挥多核CPU的性能&#xff0c;但并非所有的运算都可以改造成并行的…...

HTTP 与 HTTPS-HTTP 解决了 HTTP 哪些问题?

资料来源 : 小林coding 小林官方网站 : 小林coding (xiaolincoding.com) HTTP 解决了 HTTP 哪些问题? HTTP 由于是明文传输&#xff0c;所以安全上存在以下三个风险: 窃听风险&#xff0c;比如通信链路上可以获取通信内容&#xff0c;用户号容易没。篡改风险&#xff0c;比如…...

S32 Design Studio PE工具配置TMR

配置步骤 配置内容 生成的配置结构体如下&#xff0c;在Generated_Code路径下的lpTmr.c文件和lpTmr.h文件。 /*! lpTmr1 configuration structure */ const lptmr_config_t lpTmr1_config0 {.workMode LPTMR_WORKMODE_PULSECOUNTER,.dmaRequest false,.interruptEnable tr…...

Typescript中常用的数据类型

文章目录 概要TS的数据类型1.基础类型-- 简单的类型-- Array类型-- Object类型- 可选类型 -- Function类型- 函数的参数类型- 函数的返回值类型- 匿名函数的参数- 函数参数为对象类型- 函数的调用签名- 函数的构造签名(了解)- 剩余参数- 函数的重载(了解)- 函数的this(了解) 2.…...

【推荐】渗透测试面试(问题+答案)

1、介绍一下自认为有趣的挖洞经历 2、你平时用的比较多的漏洞是哪些?相关漏洞的原理?以及对应漏洞的修复方案? 3、php/java反序列化漏洞的原理?解决方案? 4、如果一台服务器被入侵后,你会如何做应急响应? 5、你平时使用哪些工具?以及对应工具的特点? 6、如果遇到waf的情…...

基于java+springboot+vue实现的美食信息推荐系统(文末源码+Lw)23-170

1 摘 要 使用旧方法对美食信息推荐系统的信息进行系统化管理已经不再让人们信赖了&#xff0c;把现在的网络信息技术运用在美食信息推荐系统的管理上面可以解决许多信息管理上面的难题&#xff0c;比如处理数据时间很长&#xff0c;数据存在错误不能及时纠正等问题。这次开发…...

HGAME week2 web

1.What the cow say? 测试发现可以反引号命令执行 ls /f* tac /f*/f* 2.myflask import pickle import base64 from flask import Flask, session, request, send_file from datetime import datetime from pytz import timezonecurrentDateAndTime datetime.now(timezone(…...

SQL注入:网鼎杯2018-unfinish

目录 使用dirmap扫描 使用dirsearch扫描 使用acunetix扫描 爆破后端过滤的字符 绕过限制获取数据 这次的进行SQL注入的靶机是&#xff1a;BUUCTF在线评测 进入到主页面后发现是可以进行登录的&#xff0c;那么我们作为一个安全人员&#xff0c;那肯定不会按照常规的方式来…...

C 标准库 - <limits.h>

在C语言编程中&#xff0c;<limits.h> 头文件扮演着关键角色&#xff0c;它为各种基本数据类型定义了最小和最大限制。通过使用这些预定义的宏&#xff0c;程序员可以确保程序代码不会尝试存储超出特定类型范围的值。 简介 <limits.h> 头文件包含了关于不同类型&…...

《游戏引擎架构》--学习3

内存管理 优化动态内存分配 维持最低限度的堆分配&#xff0c;并且永不在紧凑循环中使用堆分配 容器 迭代器 Unicode...

c语言中的大小写字母转换怎么转?

在C语言中&#xff0c;大小写字母转换是基于ASCII码表的特性实现的。ASCII码中&#xff0c;小写字母从’a’到’z’的ASCII码值是连续的&#xff08;97到122&#xff09;&#xff0c;而大写字母从’A’到’Z’的ASCII码值也是连续的&#xff08;65到90&#xff09;。它们之间有…...

java面试题之SpringMVC篇

Spring MVC的工作原理 Spring MVC的工作原理如下&#xff1a; DispatcherServlet 接收用户的请求找到用于处理request的 handler 和 Interceptors&#xff0c;构造成 HandlerExecutionChain 执行链找到 handler 相对应的 HandlerAdapter执行所有注册拦截器的preHandler方法调…...

基于FPGA的I2C接口控制器(包含单字节和多字节读写)

1、概括 前文对IIC的时序做了详细的讲解&#xff0c;还有不懂的可以获取TI的IIC数据手册查看原理。通过手册需要知道的是IIC读、写数据都是以字节为单位&#xff0c;每次操作后接收方都需要进行应答。主机向从机写入数据后&#xff0c;从机接收数据&#xff0c;需要把总线拉低来…...

使用sql判断两段时间是否重叠

使用sql判断两段时间是否重叠 1. 时间点重叠判断a)时间重叠有以下4种情况a)时间不重叠只有以下2种情况 判断条件, 不重叠的判断判断条件, 重叠的判断 假设现在有时间 [startTime, endTime], 数据库存在字段 sql_start_time 和 sql_end_time, 分别表示要判断的时间段和数据库的时…...