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

SGI 空间配置器

前言

空间配置器是 STL 六大组件之一,它总是隐藏在容器的背后,默默工作,默默付出。本文为《STL 源码剖析》读书笔记,主要讨论 SGI 版本空间的配置和释放,对代码进行解读时会改变一些写法,使其更易于阅读。

对象构造前的空间配置和对象析构后的空间释放,由 <stl_alloc.h> 负责,SGI 对此的设计哲学如下:

  • 向系统堆申请空间
  • 考虑多线程状态(本文不考虑多线程情况)
  • 考虑内存不足时的应变措施
  • 考虑小块内存过多造成的内存碎片问题

一级配置器

一级配置器并没有什么特殊的地方,就是调用 malloc() 和 free() 申请和释放内存。

__malloc_alloc_template 源码:

#if 0
#   include <new>
#   define __THROW_BAD_ALLOC throw bad_alloc
#elif !defined(__THROW_BAD_ALLOC)
#   include <iostream.h>
#   define __THROW_BAD_ALLOC cerr << "out of memory" << endl; exit(1)
#endiftemplate <int inst>
class __malloc_alloc_template {private:static void *oom_malloc(size_t);static void *oom_realloc(void *, size_t);#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUGstatic void (* __malloc_alloc_oom_handler)();
#endifpublic:static void * allocate(size_t n)
{void *result = malloc(n);if (0 == result) result = oom_malloc(n);return result;
}static void deallocate(void *p, size_t /* n */)
{free(p);
}static void * reallocate(void *p, size_t /* old_sz */, size_t new_sz)
{void * result = realloc(p, new_sz);if (0 == result) result = oom_realloc(p, new_sz);return result;
}static void (* set_malloc_handler(void (*f)()))()
{void (* old)() = __malloc_alloc_oom_handler;__malloc_alloc_oom_handler = f;return(old);
}};// malloc_alloc out-of-memory handling#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
template <int inst>
void (* __malloc_alloc_template<inst>::__malloc_alloc_oom_handler)() = 0;
#endiftemplate <int inst>
void * __malloc_alloc_template<inst>::oom_malloc(size_t n)
{void (* my_malloc_handler)();void *result;for (;;) {my_malloc_handler = __malloc_alloc_oom_handler;if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }(*my_malloc_handler)();result = malloc(n);if (result) return(result);}
}template <int inst>
void * __malloc_alloc_template<inst>::oom_realloc(void *p, size_t n)
{void (* my_malloc_handler)();void *result;for (;;) {my_malloc_handler = __malloc_alloc_oom_handler;if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; }(*my_malloc_handler)();result = realloc(p, n);if (result) return(result);}
}typedef __malloc_alloc_template<0> malloc_alloc;

oom

什么是 oom(out of member)?

申请内存时,如果没有空闲的物理内存,那么内核就会开始进行回收内存的工作。如果内存回收后,空闲的物理内存仍然无法满足此次物理内存的申请,那么内核就会触发 OOM (Out of Memory)机制

OOM 机制会根据算法选择一个占用物理内存较高的进程,然后将其杀死,以便释放内存资源,如果物理内存依然不足,OOM 会继续杀死占用物理内存较高的进程,直到释放足够的内存。

SGI 中的 __malloc_alloc_oom_handler 操作又是什么?

__malloc_alloc_oom_handler 指向内存不足时的处理函数,是 SGI 模仿 C++ 的 set_new_handler,因为没有使用 ::operator new 来分配内存,所以不能直接使用 set_new_handler。

一个设计良好的 new_handler 函数做以下事情:

  • 让更多内存可以被使用
  • 安装另一个 new_handler
  • 卸载 new_handler
  • 抛出 bad_alloc
  • 不返回

SGI 中内存不足时调用 oom_malloc() 和 oom_realloc(),在它们内部不断调用「内存不足处理函数」,期望在某次调用后,就得到了足够的内存然后返回。但如果「内存不足处理函数」并没有被客户端设定,便会调用 __THROW_BAD_ALLOC,丢出异常信息并终止进程。

内存不足时的处理操作:

// 此处的条件编译一定会执行 elif 部分
// 最后尽力了也申请不到内存时,就打印错误语句,结束程序
#if 0
#   include <new>
#   define __THROW_BAD_ALLOC throw bad_alloc
#elif !defined(__THROW_BAD_ALLOC)
#   include <iostream.h>
#   define __THROW_BAD_ALLOC cerr << "out of memory" << endl; exit(1)
#endif// 成员变量,指向内存不足时的处理函数,初始值为空
static void (* __malloc_alloc_oom_handler)();// 参数为新设置的内存不足处理函数
// 返回值为旧的内存不足处理函数
static auto set_malloc_handler(void (*f)()) -> void (*)() {void (* old)() = __malloc_alloc_oom_handler;__malloc_alloc_oom_handler = f;return(old);
}// 该非类型模板参数没有用处
template <int inst>
void * __malloc_alloc_template<inst>::oom_malloc(size_t n) {void (* my_malloc_handler)();void *result;		// 指向申请到的内存while(true) {// 获取内存不足时的处理函数my_malloc_handler = __malloc_alloc_oom_handler;if (0 == my_malloc_handler) {	// 如果没有设置处理函数,终止进程__THROW_BAD_ALLOC; }(*my_malloc_handler)();			// 调用内存不足处理函数result = malloc(n);				// 再次尝试申请if (result != nullptr) {return(result);				// 申请成功返回}}
}template <int inst>
void * __malloc_alloc_template<inst>::oom_realloc(void *p, size_t n) {void (* my_malloc_handler)();void *result;while(true) {// 获取内存不足时的处理函数my_malloc_handler = __malloc_alloc_oom_handler;if (0 == my_malloc_handler) {	// 如果没有设置处理函数,终止进程__THROW_BAD_ALLOC; }(*my_malloc_handler)();			// 调用内存不足处理函数result = realloc(p, n);			// 再次尝试申请if (result != nullptr) {return(result);				// 申请成功返回}}
}

申请内存

一级配置器申请内存直接调用 malloc() 和 realloc() 函数。

static void * allocate(size_t n) {void *result = malloc(n);if (0 == result) {result = oom_malloc(n);				// 申请失败时调用 oom_malloc}return result;
}static void * reallocate(void *p, size_t /* old_sz */, size_t new_sz) {void * result = realloc(p, new_sz);if (0 == result) {result = oom_realloc(p, new_sz);	// 申请失败时调用 oom_realloc}return result;
}

释放内存

一级配置器释放内存直接调用 free() 函数。

// 第二个参数没有作用
static void deallocate(void *p, size_t /* n */) {free(p);
}

二级配置器

二级配置器就比一级配置器复杂的多,大于 128 字节的申请调用一级配置器,小于 128 字节的内存使用自由链表数组分配。整个二级配置器共享一个内存池,内存不足时从内存池获取。提供函数从下层获取内存,并向自由链表中填充内存。

自由链表如下,提供以 8 为倍数的小块内存,小块内存的头部 4/8 字节指向下一空闲节点。

自由链表

__default_alloc_template 源码:

enum {__ALIGN = 8};
enum {__MAX_BYTES = 128};
enum {__NFREELISTS = __MAX_BYTES/__ALIGN};template <bool threads, int inst>
class __default_alloc_template {private:static size_t ROUND_UP(size_t bytes) {return (((bytes) + __ALIGN - 1) & ~(__ALIGN - 1));}
private:union obj {union obj * free_list_link;char client_data[1];    /* The client sees this.        */};
private:static obj * volatile free_list[__NFREELISTS]; static  size_t FREELIST_INDEX(size_t bytes) {return (((bytes) + __ALIGN-1)/__ALIGN - 1);}static void *refill(size_t n);static char *chunk_alloc(size_t size, int &nobjs);// Chunk allocation state.static char *start_free;static char *end_free;static size_t heap_size;public:/* n must be > 0      */static void * allocate(size_t n){obj * volatile * my_free_list;obj * result;if (n > (size_t) __MAX_BYTES) {return(malloc_alloc::allocate(n));}my_free_list = free_list + FREELIST_INDEX(n);result = *my_free_list;if (result == 0) {void *r = refill(ROUND_UP(n));return r;}*my_free_list = result -> free_list_link;return (result);}/* p may not be 0 */static void deallocate(void *p, size_t n){obj *q = (obj *)p;obj * volatile * my_free_list;if (n > (size_t) __MAX_BYTES) {malloc_alloc::deallocate(p, n);return;}my_free_list = free_list + FREELIST_INDEX(n);q -> free_list_link = *my_free_list;*my_free_list = q;}static void* reallocate(void *p, size_t old_sz, size_t new_sz){void * result;size_t copy_sz;if (old_sz > (size_t) __MAX_BYTES && new_sz > (size_t) __MAX_BYTES) {return(realloc(p, new_sz));}if (ROUND_UP(old_sz) == ROUND_UP(new_sz)) return(p);result = allocate(new_sz);copy_sz = new_sz > old_sz? old_sz : new_sz;memcpy(result, p, copy_sz);deallocate(p, old_sz);return(result);}} ;template <bool threads, int inst>
char*
__default_alloc_template<threads, inst>::chunk_alloc(size_t size, int& nobjs)
{char * result;size_t total_bytes = size * nobjs;size_t bytes_left = end_free - start_free;if (bytes_left >= total_bytes) {result = start_free;start_free += total_bytes;return(result);} else if (bytes_left >= size) {nobjs = bytes_left/size;total_bytes = size * nobjs;result = start_free;start_free += total_bytes;return(result);} else {size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);// Try to make use of the left-over piece.if (bytes_left > 0) {obj * __VOLATILE * my_free_list =free_list + FREELIST_INDEX(bytes_left);((obj *)start_free) -> free_list_link = *my_free_list;*my_free_list = (obj *)start_free;}start_free = (char *)malloc(bytes_to_get);if (0 == start_free) {int i;obj * __VOLATILE * my_free_list, *p;// Try to make do with what we have.  That can't// hurt.  We do not try smaller requests, since that tends// to result in disaster on multi-process machines.for (i = size; i <= __MAX_BYTES; i += __ALIGN) {my_free_list = free_list + FREELIST_INDEX(i);p = *my_free_list;if (0 != p) {*my_free_list = p -> free_list_link;start_free = (char *)p;end_free = start_free + i;return(chunk_alloc(size, nobjs));// Any leftover piece will eventually make it to the// right free list.}}end_free = 0;	// In case of exception.start_free = (char *)malloc_alloc::allocate(bytes_to_get);// This should either throw an// exception or remedy the situation.  Thus we assume it// succeeded.}heap_size += bytes_to_get;end_free = start_free + bytes_to_get;return(chunk_alloc(size, nobjs));}
}template <bool threads, int inst>
void* __default_alloc_template<threads, inst>::refill(size_t n)
{int nobjs = 20;char * chunk = chunk_alloc(n, nobjs);obj * volatile * my_free_list;obj * result;obj * current_obj, * next_obj;int i;if (1 == nobjs) return(chunk);my_free_list = free_list + FREELIST_INDEX(n);/* Build free list in chunk */result = (obj *)chunk;*my_free_list = next_obj = (obj *)(chunk + n);for (i = 1; ; i++) {current_obj = next_obj;next_obj = (obj *)((char *)next_obj + n);if (nobjs - 1 == i) {current_obj -> free_list_link = 0;break;} else {current_obj -> free_list_link = next_obj;}}return(result);
}template <bool threads, int inst>
char *__default_alloc_template<threads, inst>::start_free = 0;template <bool threads, int inst>
char *__default_alloc_template<threads, inst>::end_free = 0;template <bool threads, int inst>
size_t __default_alloc_template<threads, inst>::heap_size = 0;template <bool threads, int inst>
__default_alloc_template<threads, inst>::obj * __VOLATILE
__default_alloc_template<threads, inst> ::free_list[__NFREELISTS] = 
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };

成员变量

enum {__ALIGN = 8};								// 对齐数
enum {__MAX_BYTES = 128};						// 可以申请的最大字节数
enum {__NFREELISTS = __MAX_BYTES/__ALIGN};		// 自由链表个数static obj * volatile free_list[__NFREELISTS];	// 自由链表数组
static char *start_free;						// 内存池空间起始位置
static char *end_free;							// 内存池空间结束位置
static size_t heap_size;						// 多开辟的堆大小

还有一个比较特殊的成员,自由链表的节点结构。

该联合体从第一个字段看:它可以被视为一个指针,指向下一节点。从第二个字段看:它可以被视为一个指针,指向实际的数据空间。

实际上该联合体并没有实际的作用,只是为了便于理解。申请的一块内存,在没被使用的时候,可以用其头部 4/8 字节指向下一空闲节点,不用维护另外的指针。

union obj {union obj * free_list_link;char client_data[1];    /* The client sees this.        */
};

工具部分

这部分提供内存对齐,获取在自由链表数组中下标的函数。

因为自由链表中提供以 8 为倍数的小块内存,因此需要将申请的内存对齐为 8 的倍数。

static size_t ROUND_UP(size_t bytes) {// (bytes) + __ALIGN - 1 保证向对齐数进一位// ~(__ALIGN - 1) 去掉低位的 1return (((bytes) + __ALIGN - 1) & ~(__ALIGN - 1));
}

同时也需要知道对应大小在自由链表数组中的下标,以边获取内存和归还内存。

static  size_t FREELIST_INDEX(size_t bytes) {// - 1 因为数组的下标从零开始// 等于 ROUND_UP(bytes) / _ALIGN - 1// 因为低位的数在除对齐数后没有影响return (((bytes) + __ALIGN - 1) / __ALIGN - 1);
}

申请内存

申请内存时首先判断大小,大于 128 字节就调用一级配置器,小于 128 字节就去自由链表中获取,自由链表中没有内存就调用 refill() 填充内存。

static void * allocate(size_t n) {obj * volatile * my_free_list;	obj * result;					// 带回申请的内存// 大于 128 字节调用一级配置器if (n > (size_t) __MAX_BYTES) {return(malloc_alloc::allocate(n));}// 找到对应大小的自由链表my_free_list = free_list + FREELIST_INDEX(n);result = *my_free_list;if (result == 0) {// 自由链表中没有内存可用,为其填充内存void *r = refill(ROUND_UP(n));return r;}// 取出一个节点,调整 free_list 指向下一节点*my_free_list = result -> free_list_link;return (result);
}

自由链表2

释放内存

释放时同样需要先判断内存大小,大于 128 字节调用一级配置器释放,小于 128 字节还给自由链表。

// p 为要释放的首地址,n 为对象的大小
static void deallocate(void *p, size_t n) {obj *q = (obj *)p;obj * volatile * my_free_list;// 大于 128 字节调用一级配置器if (n > (size_t) __MAX_BYTES) {malloc_alloc::deallocate(p, n);return;}// 找到要插入的位置my_free_list = free_list + FREELIST_INDEX(n);// 将节点头插进去q -> free_list_link = *my_free_list;*my_free_list = q;
}

自由链表3

填充自由链表

当申请内存时发现自由链表中没有可用内存后,就调用 refill()。refill() 的作用是为指定自由链表填充内存,新的内存从内存池中获取,默认情况下是填充 20 个节点,但万一内存池中内存不足,获取的节点可能小于 20 个。

template <bool threads, int inst>
void* __default_alloc_template<threads, inst>::refill(size_t n) {int nobjs = 20;							// 默认填充的个数// 从内存池获取内存,nobjs 为引用传参,带回实际申请到的个数char * chunk = chunk_alloc(n, nobjs);obj * volatile * my_free_list;obj * result;							// 返回使用的节点obj * current_obj, * next_obj;int i;									// 只申请到一个节点,将该节点直接返回,不用向 free_list 中新增节点if (1 == nobjs) {return(chunk);}// 调整 my_free_list 指向,指向要添加节点的自由链表my_free_list = free_list + FREELIST_INDEX(n);// 需要将多余的节点插入到自由链表中// 获取第一个节点,后续返回使用result = (obj *)chunk;// + n 指向第二个节点*my_free_list = (obj *)(chunk + n);next_obj = (obj *)(chunk + n);for (i = 1; ; ++i) {// 分别指向当前节点、下一节点current_obj = next_obj;// (char *)next_obj + n 取一个节点的大小next_obj = (obj *)((char *)next_obj + n);// 一共申请了 nobjs 个节点,需要插入 n - 1 个if (nobjs - 1 == i) {// 将最后一个插入的节点置空current_obj -> free_list_link = 0;break;} else {// 采用尾插的方式current_obj -> free_list_link = next_obj;}}return(result);
}

内存池

chunk_alloc() 首先检查内存池中是否有内存可用,如果没有就尝试调用 malloc() 申请内存,申请失败就去更大节点的自由链表中寻找内存。如果经过上述艰难的过程,还是没有获取到内存的话,就会调用一级配置器,祈祷「内存不足处理函数」有所作用。

template <bool threads, int inst>
char* __default_alloc_template<threads, inst>::chunk_alloc(size_t size, int& nobjs) {char * result;								// 结果指针size_t total_bytes = size * nobjs;			// 要申请的总字节数size_t bytes_left = end_free - start_free;  // 内存池剩余空间if (bytes_left >= total_bytes) {// 内存池剩余空间满足需求result = start_free;start_free += total_bytes;	// 将 start 向后移,表示内存已被使用return(result);} else if (bytes_left >= size) {// 内存池剩余空间不能满足要求,但足够一个以上的节点nobjs = bytes_left/size;	// 能带回的节点个数total_bytes = size * nobjs;	// 申请到的字节数result = start_free;start_free += total_bytes;	// 调整 startreturn(result);} else {// 内存池剩余空间连一个节点也不能满足// 申请成功后后取走 total_bytes 字节,剩余部分留在内存池size_t bytes_to_get = 2 * total_bytes + ROUND_UP(heap_size >> 4);if (bytes_left > 0) {// 内存池中还有剩余内存,把它分配到合适的自由链表中// 申请是 8 的倍数,使用也是 8 的倍数,因此可以找到合适的位置obj * volatile * my_free_list = free_list + FREELIST_INDEX(bytes_left);((obj *)start_free) -> free_list_link = *my_free_list;*my_free_list = (obj *)start_free;}// 调用 malloc 向堆申请内存start_free = (char *)malloc(bytes_to_get);if (0 == start_free) {// 申请内存失败int i;obj * volatile * my_free_list, *p;for (i = size; i <= __MAX_BYTES; i += __ALIGN) {// 检查配有更大节点的自由链表是否有空间可用// 例如 32 字节自由链表没有内存可用,可以去 40、48 等自由链表查看my_free_list = free_list + FREELIST_INDEX(i);p = *my_free_list;if (0 != p) {// 更大的自由链表中有内存可用*my_free_list = p -> free_list_link;	// 弹出一个节点start_free = (char *)p;					// 将弹出的节点放入内存池中end_free = start_free + i;				// 调整内存池大小return(chunk_alloc(size, nobjs));		// 此时已经有内存了,递归调用自己获取}}// 此时内存池没有内存,malloc 失败,也没有更大的可用节点// 尝试调用一级配置器,看内存不足处理函数是否有办法end_free = 0;start_free = (char *)malloc_alloc::allocate(bytes_to_get);} // end of if (0 == start_free)heap_size += bytes_to_get;				// 随着次数而增大,不太理解含义end_free = start_free + bytes_to_get;	// 调整内存池大小return(chunk_alloc(size, nobjs));		// 此时已经有内存了,递归调用自己获取} // end of if (bytes_left >= total_bytes)
}

自由链表4

相关文章:

SGI 空间配置器

前言 空间配置器是 STL 六大组件之一&#xff0c;它总是隐藏在容器的背后&#xff0c;默默工作&#xff0c;默默付出。本文为《STL 源码剖析》读书笔记&#xff0c;主要讨论 SGI 版本空间的配置和释放&#xff0c;对代码进行解读时会改变一些写法&#xff0c;使其更易于阅读。…...

2023年白酒行业研究报告

第一章 行业概况 白酒是中国传统的酿酒业之一&#xff0c;历史悠久&#xff0c;源远流长。白酒指以高粱等粮谷为主要原料&#xff0c;以大曲、小曲或麸曲及酒母等为糖化发酵剂&#xff0c;经蒸煮、糖化、发酵、蒸馏、陈酿、勾兑而制成的&#xff0c;酒精度(体积分数)在18%-68%…...

华为OD机试 -合规数组(Java) | 机试题+算法思路+考点+代码解析 【2023】

合规数组 题目 给定一个正整数数组 检查数组中是否存在满足规则的数组组合 规则: A = B + 2C 输入 第一行输出数组的元素个数 接下来一行输出所有数组元素,用空格隔开 输出 如果存在满足要求的数 在同一行里依次输出规则里A B C的取值,用空格隔开 如果不存在输出0 示…...

华为OD机试真题Python实现【英文输入法】真题+解题思路+代码(20222023)

英文输入法 主管期望你来实现英文输入法单词联想功能,需求如下: 依据用户输入的单词前缀,从已输入的英文语句中联想出用户想输入的单词。按字典序输出联想到的单词序列,如果联想不到,请输出用户输入的单词前缀。注意 英文单词联想时区分大小写缩略形式如"don’t&quo…...

改进YOLO系列 | 添加轻量化Decouple_Head 和 ASFF_Head

绿色为ASFF_Head,浅蓝色Decoupled_Head,深蓝色是第三步加的_initialize_dh_biases方法后的效果。 参数量与计算量对比 模型参数量 parameters计算量GFLOPsyolov5s_Head723538916.5ASFF_Head1267484725.0Decoupled_Head892869722.0结构图 本篇介绍的这个Decouple_Head和YOLOX…...

LLFlow沦为和代码解读

LLFlow沦为和代码解读 1.测试时代码的运行位置 sr&#xff1a;什么意思 sr 和 z 这里又将模型设置为了训练模式 所以下面这部分代码应该是测试时运行的所有代码 这个就是测试时使用的网络模型框架 下面应该就是self.netG的网络模型框架 但是这个z的网络模型框架代码还不…...

C语言学习及复习笔记-【9】数组

目录9. 数组9.1 数组的定义9.2 数组与指针的区别9.3 数组大小计算9.4 数组的赋值9.5多维数组寻址方式9.6 函数数组9. 数组 9.1 数组的定义 C 语言支持数组数据结构&#xff0c;它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据&#xff0c;但它往往…...

Kubernetes入门教程 --- 使用kubeadm进行集群安装

Kubernetes入门教程 --- 使用kubeadm和二进制安装1. Introduction1.1 架构图1.2 关键字介绍1.3 简述2. 使用Kubeadm Install2.1 申请三个虚拟环境2.2 准备安装环境2.3 配置yum源2.4 安装Docker2.5 时间同步2.6 安装组件2.7 部署集群2.8 Master安装网络插卡3. 查询状态3.1 查询n…...

华为OD机试真题Python实现【相对开音节】真题+解题思路+代码(20222023)

相对开音节 题目 相对开音节构成的结构为辅音+元音(aeiou)+辅音(r除外) 常见的单词有bike cake 给定一个字符串,以空格为分隔符 反转每个单词的字母 若单词中包含如数字等其他非字母时不进行反转 反转后计算其中含有相对开音节结构的子串个数 (连续子串中部分字符可以重复) …...

海思SD3403/SS928V100开发(5)MIPI_YUV相机vio sample开发----修改思路

1. 前言 sensor输出格式: YUV422 8bit 硬件连接: MIPI_YUV相机(4lane MIPI) -> SS928V100 MIPI0(4lane) 框图: 2. 几个问题 基于SS928 SDK中的 vio sample修改; 但是sample里面都是基于RAW RGB sensor开发的sample, 没有现成的MIPI_YUV sensor的参考,需要自己…...

javaee之node.js与es6

问题1&#xff1a;在IDEA控制台为什么node显示不会出来命令 修改完之后记得重新启动电脑 问题2&#xff1a;response.end()作用 在Web开发中&#xff0c;浏览器端的请求到达服务器进行处理的时候&#xff0c;Response.End的作用就是让request执行到此结束&#xff0c;输出到客户…...

11 nacos源码开篇

nacos核心功能点&#xff1a; 服务注册&#xff1a;Nacos Client会通过发送REST请求的方式向Nacos Server注册自己的服务&#xff0c;提供自身的元数据&#xff0c;比如ip地址、端口等信息。Nacos Server接收到注册请求后&#xff0c;就会把这些元数据信息存储在一个双层的内存…...

13.Jenkins集成sonarqube

Jenkins集成sonarqube1.jenkins集成sonarqube2.jenkins以maven的方式集成sonarqube scanner报错1报错21.jenkins集成sonarqube 1.在jenkins插件管理中安装sonarqube scanner 2.jenkins要知道sonarqube服务端是谁&#xff0c;需要配置&#xff08;系统管理–系统配置–sonarq…...

JavaWeb13-线程休眠和指定唤醒:LockSupport

目录 1.LockSupport.park()&#xff1a;休眠当前线程 2.LockSupport.unpark(线程对象)&#xff1a;唤醒某一个指定的线程 3.扩展&#xff1a;LockSupport.parkUntil(long)等待最大时间是一个固定时间 4.LockSupport和Interrupt 5.LockSupport VS wait 相同点&#xff1a;…...

【第一章:Spring概述、特点、IOC容器、IOC操作bean管理(基于xml方式创建对象,注入属性)】

第一章&#xff1a;Spring概述、特点、IOC容器、IOC操作bean管理&#xff08;基于xml方式创建对象&#xff0c;注入属性&#xff09; 1.Spring是什么&#xff1f; ①Spring是一款主流的java EE 轻量级开源框架。 ②广义的Spring&#xff1a;Spring技术栈&#xff0c;Spring不再…...

前端react面试题(边面边更)

展示组件(Presentational component)和容器组件(Container component)之间有何不同 展示组件关心组件看起来是什么。展示专门通过 props 接受数据和回调&#xff0c;并且几乎不会有自身的状态&#xff0c;但当展示组件拥有自身的状态时&#xff0c;通常也只关心 UI 状态而不是…...

产品3C认证在哪里办理

​一、什么是CCC认证 3C认证的全称为“中国强制性产品认证”&#xff0c;英文名称China Compulsory Certification&#xff0c;英文缩写CCC。它是中国政府为保护消费者人身安全和国家安全、加强产品质量管理、依照法律法规实施的一种产品合格评定制度。年12月&#xff0c;国家…...

UA-DETRAC数据集转YOLO格式

一&#xff1a; 数据集下载 原官方数据集 链接&#xff1a;https://pan.baidu.com/s/1P_CeSIpJIYSA1dykmFhgYw 提取码&#xff1a; 7f4g 处理完成数据集&#xff08;每10帧取一张&#xff09; 链接&#xff1a;后续添加 提取码&#xff1a;后续添加 二&#xff1a; 处理标…...

代码随想录---二叉树的总结和二叉树的定义

二叉树的种类&#xff1a; 满二叉树&#xff1a;树的所有节点都是满&#xff0c;即都有左右孩子。 这棵二叉树为满二叉树&#xff0c;也可以说深度为k&#xff0c;有2^k-1个节点的二叉树。 完全二叉树&#xff1a;完全二叉树的定义如下&#xff1a;在完全二叉树中&#xff0c…...

Hive SQL 执行计划

我们在写Hive SQL的时候&#xff0c;难免会在运行的时候有报错&#xff0c;所以知道Hive SQL的执行计划具体是什么&#xff0c;然后假如在之后的运行过程中有报错&#xff0c;可以根据执行计划定位问题&#xff0c;调试自己的SQL开发脚本。 一、含义 Hive SQL的执行计划描述S…...

XCTF-web-easyupload

试了试php&#xff0c;php7&#xff0c;pht&#xff0c;phtml等&#xff0c;都没有用 尝试.user.ini 抓包修改将.user.ini修改为jpg图片 在上传一个123.jpg 用蚁剑连接&#xff0c;得到flag...

【Linux】shell脚本忽略错误继续执行

在 shell 脚本中&#xff0c;可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行&#xff0c;可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令&#xff0c;并忽略错误 rm somefile…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

MVC 数据库

MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

Python爬虫(二):爬虫完整流程

爬虫完整流程详解&#xff08;7大核心步骤实战技巧&#xff09; 一、爬虫完整工作流程 以下是爬虫开发的完整流程&#xff0c;我将结合具体技术点和实战经验展开说明&#xff1a; 1. 目标分析与前期准备 网站技术分析&#xff1a; 使用浏览器开发者工具&#xff08;F12&…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1

每日一言 生活的美好&#xff0c;总是藏在那些你咬牙坚持的日子里。 硬件&#xff1a;OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写&#xff0c;"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...

【Web 进阶篇】优雅的接口设计:统一响应、全局异常处理与参数校验

系列回顾&#xff1a; 在上一篇中&#xff0c;我们成功地为应用集成了数据库&#xff0c;并使用 Spring Data JPA 实现了基本的 CRUD API。我们的应用现在能“记忆”数据了&#xff01;但是&#xff0c;如果你仔细审视那些 API&#xff0c;会发现它们还很“粗糙”&#xff1a;有…...

今日科技热点速览

&#x1f525; 今日科技热点速览 &#x1f3ae; 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售&#xff0c;主打更强图形性能与沉浸式体验&#xff0c;支持多模态交互&#xff0c;受到全球玩家热捧 。 &#x1f916; 人工智能持续突破 DeepSeek-R1&…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...