【项目设计】高并发内存池(二)[高并发内存池整体框架设计|threadcache]
🎇C++学习历程:入门
- 博客主页:一起去看日落吗
- 持续分享博主的C++学习历程
博主的能力有限,出现错误希望大家不吝赐教
- 分享给大家一句我很喜欢的话: 也许你现在做的事情,暂时看不到成果,但不要忘记,树🌿成长之前也要扎根,也要在漫长的时光🌞中沉淀养分。静下来想一想,哪有这么多的天赋异禀,那些让你羡慕的优秀的人也都曾默默地翻山越岭🐾。
💐 🌸 🌷 🍀
目录
- 💐1. 高并发内存池整体框架设计
- 💐2. threadcache
- 🌷2.1 threadcache整体设计
- 🌷2.2 threadcache哈希桶映射对齐规则
- 🌷2.3 threadcacheTLS无锁访问
- 💐3. 完整代码
- 🌷3.1 Common.hpp
- 🌷3.2 ThreadCache.cpp
- 🌷3.3 ConcurrentAlloc.hpp
- 🌷3.4 ThreadCache.hpp
💐1. 高并发内存池整体框架设计
- 该项目解决的是什么问题?
现代很多的开发环境都是多核多线程,因此在申请内存的时,必然存在激烈的锁竞争问题。malloc本身其实已经很优秀了,但是在并发场景下可能会因为频繁的加锁和解锁导致效率有所降低,而该项目的原型tcmalloc实现的就是一种在多线程高并发场景下更胜一筹的内存池。
在实现内存池时我们一般需要考虑到效率问题和内存碎片的问题,但对于高并发内存池来说,我们还需要考虑在多线程环境下的锁竞争问题。
- 高并发内存池整体框架设计
高并发内存池主要由以下三个部分构成:
- thread cache: 线程缓存是每个线程独有的,用于小于等于256KB的内存分配,每个线程独享一个thread cache。
- central cache: 中心缓存是所有线程所共享的,当thread cache需要内存时会按需从central cache中获取内存,而当thread cache中的内存满足一定条件时,central cache也会在合适的时机对其进行回收。
- page cache: 页缓存中存储的内存是以页为单位进行存储及分配的,当central cache需要内存时,page cache会分配出一定数量的页分配给central cache,而当central cache中的内存满足一定条件时,page cache也会在合适的时机对其进行回收,并将回收的内存尽可能的进行合并,组成更大的连续内存块,缓解内存碎片的问题。
进一步说明:
-
每个线程都有一个属于自己的thread cache,也就意味着线程在thread cache申请内存时是不需要加锁的,而一次性申请大于256KB内存的情况是很少的,因此大部分情况下申请内存时都是无锁的,这也就是这个高并发内存池高效的地方。
-
每个线程的thread cache会根据自己的情况向central cache申请或归还内存,这就避免了出现单个线程的thread cache占用太多内存,而其余thread cache出现内存吃紧的问题。
-
多线程的thread cache可能会同时找central cache申请内存,此时就会涉及线程安全的问题,因此在访问central cache时是需要加锁的,但central cache实际上是一个哈希桶的结构,只有当多个线程同时访问同一个桶时才需要加锁,所以这里的锁竞争也不会很激烈。
各个部分的主要作用:
-
thread cache主要解决锁竞争的问题,每个线程独享自己的thread cache,当自己的thread cache中有内存时该线程不会去和其他线程进行竞争,每个线程只要在自己的thread cache申请内存就行了。
-
central cache主要起到一个居中调度的作用,每个线程的thread cache需要内存时从central cache获取,而当thread cache的内存多了就会将内存还给central cache,其作用类似于一个中枢,因此取名为中心缓存。
-
page cache就负责提供以页为单位的大块内存,当central cache需要内存时就会去向page cache申请,而当page cache没有内存了就会直接去找系统,也就是直接去堆上按页申请内存块。
💐2. threadcache
🌷2.1 threadcache整体设计
定长内存池只支持固定大小内存块的申请释放,因此定长内存池中只需要一个自由链表管理释放回来的内存块。现在我们要支持申请和释放不同大小的内存块,那么我们就需要多个自由链表来管理释放回来的内存块,因此thread cache实际上一个哈希桶结构,每个桶中存放的都是一个自由链表。
thread cache支持小于等于256KB内存的申请,如果我们将每种字节数的内存块都用一个自由链表进行管理的话,那么此时我们就需要20多万个自由链表,光是存储这些自由链表的头指针就需要消耗大量内存,这显然是得不偿失的。
这时我们可以选择做一些平衡的牺牲,让这些字节数按照某种规则进行对齐,例如我们让这些字节数都按照8字节进行向上对齐,那么thread cache的结构就是下面这样的,此时当线程申请18字节的内存时会直接给出8字节,而当线程申请916字节的内存时会直接给出16字节,以此类推。
因此当线程要申请某一大小的内存块时,就需要经过某种计算得到对齐后的字节数,进而找到对应的哈希桶,如果该哈希桶中的自由链表中有内存块,那就从自由链表中头删一个内存块进行返回;如果该自由链表已经为空了,那么就需要向下一层的central cache进行获取了。
但此时由于对齐的原因,就可能会产生一些碎片化的内存无法被利用,比如线程只申请了6字节的内存,而thread cache却直接给了8字节的内存,这多给出的2字节就无法被利用,导致了一定程度的空间浪费,这些因为某些对齐原因导致无法被利用的内存,就是内存碎片中的内部碎片。
我们首先先实现自由链表所需的三个必要功能:头插,头删,判断空
static void*& NextObj(void* obj)
{return *(void**)obj;
}//管理切分好的小对象的自由链表
class FreeList
{
public://将释放的头对象头插进自由链表void Push(void* obj){assert(obj);//头插NextObj(obj) = _freeList;_freeList = obj;}//从自由链表头部获取对象void* Pop(){assert(_freeList);//头删void* obj = _freeList;_freeList = NextObj(_freeList);return obj;}bool Empty(){return _freeList == nullptr;}
private:void* _freeList = nullptr;//自由链表
};
因此thread cache实际就是一个数组,数组中存储的就是一个个的自由链表,至于这个数组中到底存储了多少个自由链表,就需要看我们在进行字节数对齐时具体用的是什么映射对齐规则了。
🌷2.2 threadcache哈希桶映射对齐规则
- 如何进行对齐?
首先,这些内存块是会被链接到自由链表上的,因此一开始肯定是按8字节进行对齐是最合适的,因为我们必须保证这些内存块,无论是在32位平台下还是64位平台下,都至少能够存储得下一个指针。
但如果所有的字节数都按照8字节进行对齐的话,那么我们就需要建立 256 × 1024 ÷ 8 = 32768 256\times1024\div8=32768 256×1024÷8=32768个桶,这个数量还是比较多的,实际上我们可以让不同范围的字节数按照不同的对齐数进行对齐,具体对齐方式如下:
字节数 | 对齐数 | 哈希桶下标 |
---|---|---|
[1,128] | 8 | [0,16) |
[128+1,1024] | 16 | [16,72) |
[1024+1,8*1024] | 128 | [72,128) |
[81024+1,641024] | 1024 | [128,184) |
[641024+1,2561024] | 8*1024 | [184,208) |
- 空间浪费率
虽然对齐产生的内碎片会引起一定程度的空间浪费,但按照上面的对齐规则,我们可以将浪费率控制到百分之十左右。需要说明的是,1~128这个区间我们不做讨论,因为1字节就算是对齐到2字节也有百分之五十的浪费率,这里我们就从第二个区间开始进行计算。
- 对齐和映射相关函数的编写
此时有了字节数的对齐规则后,我们就需要提供两个对应的函数,分别用于获取某一字节数对齐后的字节数,以及该字节数对应的哈希桶下标。关于处理对齐和映射的函数,我们可以将其封装到一个类当中。
//管理对齐和映射等关系
class SizeClass
{
public://获取向上对齐后的字节数static inline size_t RoundUp(size_t bytes);//获取对应哈希桶的下标static inline size_t Index(size_t bytes);
};
需要注意的是,SizeClass类当中的成员函数最好设置为静态成员函数,否则我们在调用这些函数时就需要通过对象去调用,并且对于这些可能会频繁调用的函数,可以考虑将其设置为内联函数。
在获取某一字节数向上对齐后的字节数时,可以先判断该字节数属于哪一个区间,然后再通过调用一个子函数进行进一步处理。
//获取向上对齐后的字节数
static inline size_t RoundUp(size_t bytes)
{if (bytes <= 128){return _RoundUp(bytes, 8);}else if (bytes <= 1024){return _RoundUp(bytes, 16);}else if (bytes <= 8 * 1024){return _RoundUp(bytes, 128);}else if (bytes <= 64 * 1024){return _RoundUp(bytes, 1024);}else if (bytes <= 256 * 1024){return _RoundUp(bytes, 8 * 1024);}else{assert(false);return -1;}
}
此时我们就需要编写一个子函数,该子函数需要通过对齐数计算出某一字节数对齐后的字节数,最容易想到的就是下面这种写法。
//一般写法
static inline size_t _RoundUp(size_t bytes, size_t alignNum)
{size_t alignSize = 0;if (bytes%alignNum != 0){alignSize = (bytes / alignNum + 1)*alignNum;}else{alignSize = bytes;}return alignSize;
}
除了上述写法,我们还可以通过位运算的方式来进行计算,虽然位运算可能并没有上面的写法容易理解,但计算机执行位运算的速度是比执行乘法和除法更快的。
//位运算写法
static inline size_t _RoundUp(size_t bytes, size_t alignNum)
{return ((bytes + alignNum - 1)&~(alignNum - 1));
}
在获取某一字节数对应的哈希桶下标时,也是先判断该字节数属于哪一个区间,然后再通过调用一个子函数进行进一步处理。
//获取对应哈希桶的下标
static inline size_t Index(size_t bytes)
{//每个区间有多少个自由链表static size_t groupArray[4] = { 16, 56, 56, 56 };if (bytes <= 128){return _Index(bytes, 3);}else if (bytes <= 1024){return _Index(bytes - 128, 4) + groupArray[0];}else if (bytes <= 8 * 1024){return _Index(bytes - 1024, 7) + groupArray[0] + groupArray[1];}else if (bytes <= 64 * 1024){return _Index(bytes - 8 * 1024, 10) + groupArray[0] + groupArray[1] + groupArray[2];}else if (bytes <= 256 * 1024){return _Index(bytes - 64 * 1024, 13) + groupArray[0] + groupArray[1] + groupArray[2] + groupArray[3];}else{assert(false);return -1;}
}
此时我们需要编写一个子函数来继续进行处理,容易想到的就是根据对齐数来计算某一字节数对应的下标。
//一般写法
static inline size_t _Index(size_t bytes, size_t alignNum)
{size_t index = 0;if (bytes%alignNum != 0){index = bytes / alignNum;}else{index = bytes / alignNum - 1;}return index;
}
当然,为了提高效率下面也提供了一个用位运算来解决的方法,需要注意的是,此时我们并不是传入该字节数的对齐数,而是将对齐数写成2的n次方的形式后,将这个n值进行传入。比如对齐数是8,传入的就是3。
//位运算写法
static inline size_t _Index(size_t bytes, size_t alignNum)
{return ((bytes + (1 << alignShift) - 1) >> alignNum) - 1;
}
- ThreadCache类
按照上述的对齐规则,thread cache中桶的个数,也就是自由链表的个数是208,以及thread cache允许申请的最大内存大小256KB,我们可以将这些数据按照如下方式进行定义。
//小于等于MAX_BYTES,就找thread cache申请
//大于MAX_BYTES,就直接找page cache或者系统堆申请
static const size_t MAX_BYTES = 256 * 1024;
//thread cache和central cache自由链表哈希桶的表大小
static const size_t NFREELISTS = 208;
现在就可以对ThreadCache类进行定义了,thread cache就是一个存储208个自由链表的数组
class ThreadCache {
public://申请对象void* Allocate(size_t size);void* Deallocate(void* ptr,size_t size);void* FetchFromCentralCache(size_t index,size_t size);
private:FreeList _freeLists[NFREELISTS];//哈希桶
};
在thread cache申请对象时,通过所给字节数计算出对应的哈希桶下标,如果桶中自由链表不为空,则从该自由链表中取出一个对象进行返回即可;但如果此时自由链表为空,那么我们就需要从central cache进行获取了
//申请内存对象
void* ThreadCache::Allocate(size_t size)
{assert(size <= MAX_BYTES);size_t alignSize = SizeClass::RoundUp(size);size_t index = SizeClass::Index(size);if(!_freeLists[index].Empty()){return _freeLists[index].Pop();}else{return FetchFromCentralCache(index,alignSize);}
}
🌷2.3 threadcacheTLS无锁访问
每个线程都有一个自己独享的thread cache,那应该如何创建这个thread cache呢?我们不能将这个thread cache创建为全局的,因为全局变量是所有线程共享的,这样就不可避免的需要锁来控制,增加了控制成本和代码复杂度。
要实现每个线程无锁的访问属于自己的thread cache,我们需要用到线程局部存储TLS(Thread Local Storage),这是一种变量的存储方法,使用该存储方法的变量在它所在的线程是全局可访问的,但是不能被其他线程访问到,这样就保持了数据的线程独立性。
//TLS - Thread Local Storage
static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr;
但不是每个线程被创建时就立马有了属于自己的thread cache,而是当该线程调用相关申请内存的接口时才会创建自己的thread cache,因此在申请内存的函数中会包含以下逻辑。
//通过TLS,每个线程无锁的获取自己专属的ThreadCache对象
if (pTLSThreadCache == nullptr)
{pTLSThreadCache = new ThreadCache;
}
💐3. 完整代码
🌷3.1 Common.hpp
//
// Common.h
// ThreadCache
//
// Created by 卜绎皓 on 2023/1/31.
//#ifndef Common_h
#define Common_h#endif /* Common_h */#include <iostream>
#include <vector>
#include <assert.h>
#include <thread>
#include <time.h>//小于等于MAX_BYTES,就找thread cache申请
//大于MAX_BYTES,就直接找page cache或者系统堆申请
static const size_t MAX_BYTES = 256 * 1024;
//thread cache和central cache自由链表哈希桶的表大小
static const size_t NFREELISTS = 208;static void*& NextObj(void* obj)
{return *(void**)obj;
}class FreeList
{
public://将释放的头对象头插进自由链表void Push(void* obj){assert(obj);//头插NextObj(obj) = _freeList;_freeList = obj;}//从自由链表头部获取对象void* Pop(){assert(_freeList);//头删void* obj = _freeList;_freeList = NextObj(_freeList);return obj;}bool Empty(){return _freeList == nullptr;}
private:void* _freeList = nullptr;//自由链表
};//管理对齐和映射等关系
class SizeClass
{
public:static inline size_t _RoundUp(size_t bytes,size_t alignNum){
// //一般写法
// size_t alignSize = 0;
// if(bytes % alignNum != 0)
// {
// alignSize = (bytes / alignNum + 1)*alignNum;
// }
// else
// {
// alignSize = bytes;
// }
// return alignSize;//位运算写法return ((bytes + alignNum - 1)&~(alignNum-1));}//获取向上对齐后的字节数static inline size_t RoundUp(size_t bytes){if(bytes <= 128){return _RoundUp(bytes, 8);}else if(bytes <= 1024){return _RoundUp(bytes, 16);}else if(bytes <= 8*1024){return _RoundUp(bytes, 128);}else if(bytes <= 64*1024){return _RoundUp(bytes, 1024);}else if(bytes <= 256*1024){return _RoundUp(bytes, 8*1024);}else{assert(false);return -1;}}//获取对应哈希桶的下标static inline size_t _Index(size_t bytes,size_t alignNum){
// //一般写法
// size_t index = 0;
// if(bytes % alignNum != 0)
// {
// index = bytes / alignNum;
// }
// else
// {
// index = bytes / alignNum - 1;
// }
// return index;//位运算写法return ((bytes + (1 << alignNum) - 1) >> alignNum) - 1;}//获取对应哈希桶的下标static inline size_t Index(size_t bytes){assert(bytes <= MAX_BYTES);//每个区间有多少个自由链表static size_t grounpArray[4] = {16,56,56,56};if(bytes <= 128){return _Index(bytes, 3);}else if(bytes <= 1024){return _Index(bytes - 128, 4) + grounpArray[0];}else if(bytes <= 8*1024){return _Index(bytes - 128, 7) + grounpArray[0] + grounpArray[1];}else if(bytes <= 64*1024){return _Index(bytes - 128, 10) + grounpArray[0] + grounpArray[1] + grounpArray[2];}else if(bytes <= 256*1024){return _Index(bytes - 128, 13) + grounpArray[0] + grounpArray[1] + grounpArray[2] + grounpArray[3];}else{assert(false);return -1;}}
};
🌷3.2 ThreadCache.cpp
//
// ThreadCache.cpp
// ThreadCache
//
// Created by 卜绎皓 on 2023/1/31.
//#include "ThreadCache.hpp"//申请内存对象
void* ThreadCache::Allocate(size_t size)
{assert(size <= MAX_BYTES);size_t alignSize = SizeClass::RoundUp(size);size_t index = SizeClass::Index(size);if(!_freeLists[index].Empty()){return _freeLists[index].Pop();}else{return FetchFromCentralCache(index,alignSize);}
}
🌷3.3 ConcurrentAlloc.hpp
//
// ConcurrentAlloc.h
// ThreadCache
//
// Created by 卜绎皓 on 2023/1/31.
//#ifndef ConcurrentAlloc_h
#define ConcurrentAlloc_h#endif /* ConcurrentAlloc_h */#include "Common.hpp"
#include "ThreadCache.hpp"static void* ConcurrentAlloc(size_t size)
{//通过TLS,每个线程无锁的获取自己专属的ThreadCache对象if (pTLSThreadCache == nullptr){pTLSThreadCache = new ThreadCache;}cout << std::this_thread::get_id() << ":"<<pTLSThreadCache<<endl;return pTLSThreadCache->Allocate(size);
}static void ConcurrentFree(void* ptr, size_t size)
{assert(pTLSThreadCache);pTLSThreadCache->Deallocate(ptr, size);
//}
🌷3.4 ThreadCache.hpp
//
// ThreadCache.hpp
// ThreadCache
//
// Created by 卜绎皓 on 2023/1/31.
//#ifndef ThreadCache_hpp
#define ThreadCache_hpp#endif /* ThreadCache_hpp */#include "Common.hpp"class ThreadCache {
public://申请对象void* Allocate(size_t size);void* Deallocate(void* ptr,size_t size);void* FetchFromCentralCache(size_t index,size_t size);
private:FreeList _freeLists[NFREELISTS];//哈希桶
};//TLS - Thread Local Storage
static _declspec(thread) ThreadCache* pTLSThreadCache = nullptr;
相关文章:
【项目设计】高并发内存池(二)[高并发内存池整体框架设计|threadcache]
🎇C学习历程:入门 博客主页:一起去看日落吗持续分享博主的C学习历程博主的能力有限,出现错误希望大家不吝赐教分享给大家一句我很喜欢的话: 也许你现在做的事情,暂时看不到成果,但不要忘记&…...
西电编译原理期末核心考点汇总(期末真题+相关知识点)
文章目录前言一、正规式1.1 相关知识点1.1.1 正规式定义1.1.2 辅助定义1.2 历年真题二、二义文法2.1 相关知识点2.1.1 二义性概念2.2 历年考题三、全部短语、直接短语和句柄3.1 相关知识点3.1.1 短语,直接短语和句柄定义3.1.2 短语,直接短语和句柄例题3.…...
追梦之旅【数据结构篇】——详解C语言实现二叉树
详解C语言实现二叉树~😎前言🙌什么是二叉树?二叉树的性质总结:整体实现内容分析💞1.头文件的编写:🙌2.功能文件的编写:🙌1)前序遍历的数值来创建树——递归函…...
独家 | Gen-1——可以改变视频风格的AI模型
翻译:吴振东校对:张睿毅本文约1000字,建议阅读3分钟 本文简单介绍了Runway公司的发展史,以及他们新推出的生成式AI模型Gen-1,可用于通过应用文本提示或者参考图像所指定的任意风格,将现有视频转换为新视频。…...
戴尔dell inspiron-5598电脑 Hackintosh 黑苹果efi引导文件
原文来源于黑果魏叔官网,转载需注明出处。硬件型号驱动情况主板X99 K9 v2 Machinist处理器i5-10210U / *i7-10510U已驱动内存20GB已驱动硬盘1000GB SAMSUNG 860 QVO SATA已驱动显卡Intel UHD 620已驱动声卡Realtek ALC3204/236已驱动网卡RTL8168H Gigabit Ethernet已…...
3.2 网站图的爬取路径
深度优先与广度优先方法都是遍历树的一种方法,但是网站的各个网页 之间的关系未必是树的结构,它们可能组成一个复杂的图形结构,即有回路。如果在前面的网站中每个网页都加一条Home的语句,让每个网页都能回到主界面,那么…...
《SQL基础》12. SQL优化
SQL优化SQL优化数据插入insert优化大批量插入数据主键优化order by优化group by优化limit优化count优化count用法update优化SQL优化 数据插入 insert优化 如果我们需要一次性往数据库表中插入多条记录,可以从以下三个方面进行优化。 批量插入手动控制事务主键顺…...
fork之后是子进程先执行还是父进程先执行
CFS(完全公平调度器)是Linux内核2.6.23版本开始采用的进程调度器,它的基本原理是这样的:设定一个调度周期(sched_latency_ns),目标是让每个进程在这个周期内至少有机会运行一次,换一种说法就是每个进程等待CPU的时间最长不超过这个…...
2023年java初级面试题(5道)
一、两个对象值相同(x.equals(y) true),但却可有不同的hash code,这句话对不对?答:不对,如果两个对象x和y满足x.equals(y) true,它们的哈希码(hash code)应当相同。Java对于eqauls…...
【内网安全】——Linux权限维持
作者名:白昼安全主页面链接: 主页传送门创作初心: 以后赚大钱座右铭: 不要让时代的悲哀成为你的悲哀专研方向: web安全,后渗透技术每日鸡汤: 钱至少对于现在的我来说,的确是万能的在…...
Linux 真实使用内存计算
获取Linux内存信息,可通过cat /proc/meminfo查看,比如,Ubuntu 20.04.5 LTS上会显示以下信息: leoyaDESKTOP-LMR:~$ cat /proc/meminfo MemTotal: 16017572 kB MemFree: 15637472 kB MemAvailable: 15533140 kB Bu…...
Unity Jobsystem ECS
简介随着ECS的加入,Unity基本上改变了软件开发方面的大部分方法。ECS的加入预示着OOP方法的结束。随着实体组件系统ECS的到来,我们在Unity开发中曾使用的大量实践方法都必须进行改变以适应ECS,也许不少人需要些时间适应ECS的使用,…...
Java中创建线程有哪几种方式
1.继承Thread类 总结:通过继承 Thread 类,重写 run() 方法,而不是 start() 方法 Thread 类底层实现 Runnable 接口类只能单继承 接口可以多继承2.实现Runnable接口 总结:通过实现 Runnable 接口,实现 run() 方法,依然…...
C++【string类用法详细介绍string类模拟实现解析】
文章目录string 类用法介绍及模拟实现一、string介绍二、string类常用接口1. string类对象的常见构造接口2.string类对象的常见容量接口3.string类对象的常见修改接口4. string类对象的常见访问及遍历接口5.string其他接口1.不常用查找接口2.字符替换3.字符串拼接4.字符串排序5…...
常见的开发模型和测试模型
软件的生命周期软件开发阶段的生命周期需求分析->计划->设计->编码->测试->运维软件测试阶段的生命周期需求分期->测试计划->测试设计与开发->执行测试->测试评估开发模型瀑布模型可以看到,这个模型和我们上面的软件开发生命周期很相似采用的是线性…...
印度和印度尼西亚有什么关系吗?
印度和印度尼西亚,这两个国家很多人都比较熟悉。因为两国都是人口大国,而且经济总量也比较高,在全球还是有很大影响的。不过很多人刚看到这两个国家的时候,都会觉得这两个国家肯定有什么关系,要不然国名也不会这么像。…...
单调栈(C/C++)
目录 1. 单调栈的定义 2. 单调栈的常见用途 3. 案例分析 3.1 暴力解法 3.2 单调栈 4. 单调栈总结 1. 单调栈的定义 单调栈顾名思义,就是栈内的元素是单调的。根据栈内元素的单调性的不同,可以分为: 单调递增栈:栈内元素是单…...
算法设计与智能计算 || 专题一: 算法基础
专题一: 算法基础 文章目录专题一: 算法基础1. 算法的定义及特点1.1 算法的基本特征1.2 算法的基本要素1.3 算法的评定2 算法常见执行方法2.1 判断语句2.2 循环语句2.3 综合运用3. 计算复杂度4. 代码的重用5. 类函数的定义与使用5.1 定义类5.2 调用类函数1. 算法的定义及特点 …...
用javascript分类刷leetcode13.单调栈(图文视频讲解)
239. 滑动窗口最大值 (hard) 给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回 滑动窗口中的最大值 。 示例 1: 输入:nums [1,…...
英语基础语法学习(B站英语电力公司)
1. 句子结构 五大基本句型: 主谓主谓宾主谓宾宾主谓宾宾补主系表 谓语: 一般来说,谓语是指主语发出的动作。(动词)但是很多句子是没有动作的,但是还是必须要有谓语。(此时需要be动词&#x…...
【计算机网络】网络层IP协议
文章目录一、认识IP协议二、IP协议头部格式三、IP地址划分1. IP地址分类2. 子网划分四、IP地址数量危机1. IP地址的数量限制2. NAT技术五、私网IP和公网IP六、路由1. 认识路由2. 路由表生成算法一、认识IP协议 IP协议是Internet Protocol(互联网协议)的…...
Eclipse快捷键大全
编辑类快捷键Ctrl1: 快速修复(最经典的快捷键, 可以解决很多问题, 比如import类、try catch包围等)CtrlShiftF: 格式化当前代码CtrlShiftM: 添加类的import导入CtrlShiftO: 组织类的导入(既有CtrlShiftM的作用,又可以去除没用的导入, 一般用这个导入包)CtrlY: 重做(与CtrlZ相反…...
JavaScript 高级2 :构造函数和原型 d331702016e84f54b3594ae05e0eeac
JavaScript 高级2 :构造函数和原型 Date: January 16, 2023 Text: 构造函数和原型、继承、ES5中的新增方法 目标 能够使用构造函数创建对象 能够说出原型的作用 能够说出访问对象成员的规则 能够使用 ES5新增的一些方法 构造函数和原型 概述 在典型的 OOP 的…...
maven-war-plugin插件 overlays maven-war-plugin翻译
说明 翻译maven-war-plugin插件的部分内容 官方地址为:https://maven.apache.org/plugins/maven-war-plugin/index.html Overview 概述 Introduction 介绍 Apache Maven WAR Plugin apache maven war 插件 The WAR Plugin is responsible for collecting all artifa…...
【数据结构】初识二叉树(二叉树的入门知识)
初识二叉树一、树概念及结构1、树的概念2、树的相关概念3、树的表示4、树在实际中的运用(表示文件系统的目录树结构)二、二叉树概念及结构1、概念2、特殊的二叉树3、二叉树的性质4、二叉树的存储结构三、结语一、树概念及结构 1、树的概念 树是一种非线…...
RV1126笔记三十二:基于 FastDeploy 在 RV1126 上的部署示例(RV1126 上部署 YOLOv5 检测模型测试)
若该文为原创文章,转载请注明原文出处。 FastDeploy是一款全场景、易用灵活、极致高效的AI推理部署工具, 支持云边端部署。提供超过 🔥160+ Text,Vision, Speech和跨模态模型📦开箱即用的部署体验,并实现🔚端到端的推理性能优化。包括 物体检测、字符识别(OCR)、…...
JVM垃圾回收——G1垃圾收集器
目录 一、什么是G1垃圾收集器 二、G1垃圾收集器的内存划分 三、G1垃圾收集器的收集过程 四、G1收集器的优缺点 五、G1收集器的JVM参数配置 一、什么是G1垃圾收集器 Garbage First(简称G1)收集器是垃圾收集器技术发展史上里程碑式的成果,它摒弃了传统垃圾收集器的…...
C语言深度剖析:关键字
C语言深度剖析:关键字C语言深度剖析:关键字前言定义与声明(补充内容)最宏大的关键字-auto最快的关键字-register关键字static被冤枉的关键字-sizeof整型在内存中的存储原码、反码、补码大小端补充理解变量内容的存储和取出为什么都是补码整型取值范围关于…...
聊一聊过度设计!
文章目录什么是过度设计?过度设计的坏处如何避免过度设计充分理解问题本身保持简单小步快跑征求其他人的意见总结新手程序员在做设计时,因为缺乏经验,很容易写出欠设计的代码,但有一些经验的程序员,尤其是在刚学习过设…...
程序员在小公司(没有大牛,人少)怎么成长?
大多数小公司都是创业公司,所以它们有着非常独特的“创业心态”。所谓创业心态通常表现为关注快速增长,竭尽所能让公司盈利,或者达成其他一些迫切目标。 在这样一家公司工作的软件开发人员,你极有可能要身兼多职,不能…...
东台建设企业网站/如何优化搜索引擎的搜索功能
数据结构是以某种形式将数据组织在一起的集合,它不仅存储数据,还支持访问和处理数据的操作。算法是为求解一个问题需要遵循的、被清楚指定的简单指令的集合。下面是自己整理的常用数据结构与算法相关内容,如有错误,欢迎指出。 为了…...
全国电子网站建设/今日头条最新新闻消息
从 1 到完美,用 js 和 react-native 写一个 APP facebook 在 2013 年开源了 react 后,紧接着在 2015 年就又开源了 react-native,就此打开了用 js 和前端技术写原生 android&ios APP 之路。尽管到目前为止 react-native 最新版本是 0.56.…...
南通网站建设知识/临沂森佳木业有限公司
重装MAC系统后,要恢复.ssh等文件夹内容,而其在“Finder”中又是默认隐藏的,这时我们可以先在“Finder”中使用“前往文件夹功能…”进入指定文件夹,然后再进入“Time Machine”进行恢复即可。 转载于:https://www.cnblogs.com/eri…...
专业建设方案/seo网站诊断分析报告
switch case的进化历程🚐简介区别总结因为长情是古董,所以假货特别多 觉得太长可以直接看总结就可以了 简介 Switch case语句在Java中是一种流程控制结构,用于将一个值与一系列可能的情况进行比较,并根据匹配的情况执行相应的代…...
如何承接网站建设外包/爱情链接
文章目录如何制作GIF?网页版GIF工具PC端GIF工具手机端GIF工具GIF压缩工具GIF现在被用得十分广泛了,各种表情包、小动画、甚至有些小教学说明动图也是GIF做的。在现在这个信息爆炸的年代,GIF已不再神秘,很多工具都可以制作GIF了,如…...
正版视频素材网站/茶叶网络推广方案
为了方便的处理集合中的元素,Java中出现了一个对象,该对象提供了一些方法专门处理集合中的元素.例如删除和获取集合中的元素.该对象就叫做迭代器(Iterator).对 Collection 进行迭代的类,称其为迭代器。还是面向对象的思想,专业对象做专业的事情ÿ…...