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

网站做百度口碑/百度seo排名点击器app

网站做百度口碑,百度seo排名点击器app,昆山智能网站建设,深圳注册公司注册资金要求文章目录 sds简介与特性(面试)sds结构模型数据结构苛刻的数据优化数据结构优化uintX_t对齐填充 sds优势O(1)时间复杂度获取字符串长度二进制安全杜绝缓冲区溢出自动扩容机制——sdsMakeRoomFor方法 内存重分配次数优化 sds最长是多少部分API源码解读创建sds释放sds sds简介与特…

文章目录

  • sds简介与特性(面试)
  • sds结构模型
    • 数据结构
    • 苛刻的数据优化
      • 数据结构优化
      • uintX_t
      • 对齐填充
    • sds优势
      • O(1)时间复杂度获取字符串长度
      • 二进制安全
      • 杜绝缓冲区溢出
          • 自动扩容机制——sdsMakeRoomFor方法
      • 内存重分配次数优化
  • sds最长是多少
  • 部分API源码解读
    • 创建sds
    • 释放sds

sds简介与特性(面试)

img

Redis 面试中大概率会提及相关的数据结构,SDS 的八股文大部分人倒背如流,可是没有读过源码,知其然不知其所以然,这可万万使不得呀!!

sds结构模型

本文阅读的Redis源码为最新的 Redis6.2.6 和 Redis3.0.0版本

相信各位看官在听到 Redis 中的字符串不是简简单单的C语言中的字符串,是 SDS(Simple Dynamic String,简单动态字符串)时以为是造出了啥新类型呢,其实 SDS 内容的源码底层就是typedef char *sds;

数据结构

Redis6.x 的 SDS 的数据结构定义与 Redis3.0.0 相差比较大,但是核心思想不变。先从简单版本(Redis3.x)开始吧~

struct sdshdr {//记录buf数组中已使用字节的数量//等于SDS所保存字符串的长度unsigned int len;//记录buf数组中未使用字节的数量unsigned int free;//char数组,用于保存字符串char buf[];
};

如下图所示为字符串"Aobing"在Redis中的存储形式:

在这里插入图片描述

  • len 为6,表示这个 SDS 保存了一个长度为6的字符串;
  • free 为0,表示这个 SDS 没有剩余空间;
  • buf 是个char类型的数组,注意末尾保存了一个空字符’\0’。

buf 尾部自动追加一个’\0’字符并不会计算在 SDS 的len中,这是为了遵循 C 字符串以空字符串结尾的惯例,使得 SDS 可以直接使用一部分string.h库中的函数,如strlen

#include <stdio.h>
#include <string.h>int main()
{char buf[] = {'A','o','b','i','n','g','\0'};printf("%s\n",buf);             // Aobingprintf("%lu\n",strlen(buf));    // 6return 0;
}

苛刻的数据优化

数据结构优化

目前我们似乎得到了一个结构不错的 SDS ,但是我们能否继续进行优化呢?

在 Redis3.x 版本中不同长度的字符串占用的头部是相同的,如果某一字符串很短但是头部却占用了更多的空间,这未免太浪费了。所以我们将 SDS 分为三种级别的字符串:

  • 短字符串(长度小于32),len和free的长度用1字节即可;
  • 长字符串,用2字节或者4字节;
  • 超长字符串,用8字节。

共有五种类型的SDS(长度小于1字节、1字节、2字节、4字节、8字节)

在这里插入图片描述

我们可以在 SDS 中新增一个 type 字段来标识类型,但是没必要使用一个 4 字节的int类型去做!可以使用 1 字节的char类型,通过位运算(3位即可标识2^3种类型)来获取类型。

如下所示为短字符串(长度小于32)的优化形式:

在这里插入图片描述

低三位存储类型,高5位存储长度,最多能标识的长度为32,所以短字符串的长度必定小于32。

无需free字段了,32-len即为free

接下来看看Redis6.x中是怎么做的吧!

// 注意:sdshdr5从未被使用,Redis中只是访问flags。
struct __attribute__ ((__packed__)) sdshdr5 {unsigned char flags; /* 低3位存储类型, 高5位存储长度 */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr8 {uint8_t len; /* 已使用 */uint8_t alloc; /* 总长度,用1字节存储 */unsigned char flags; /* 低3位存储类型, 高5位预留 */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr16 {uint16_t len; /* 已使用 */uint16_t alloc; /* 总长度,用2字节存储 */unsigned char flags; /* 低3位存储类型, 高5位预留 */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr32 {uint32_t len; /* 已使用 */uint32_t alloc; /* 总长度,用4字节存储 */unsigned char flags; /* 低3位存储类型, 高5位预留 */char buf[];
};
struct __attribute__ ((__packed__)) sdshdr64 {uint64_t len; /* 已使用 */uint64_t alloc; /* 总长度,用8字节存储 */unsigned char flags; /* 低3位存储类型, 高5位预留 */char buf[];
};

数据结构和我们分析的差不多嘛!也是加一个标识字段而已,并且不是int类型,而是1字节的char类型,使用其中的3位表示具体的类型。

同时,Redis 中也声明了5个常量分别表示五种类型的 SDS ,与我们分析的也不谋而合。

#define SDS_TYPE_5  0
#define SDS_TYPE_8  1
#define SDS_TYPE_16 2
#define SDS_TYPE_32 3
#define SDS_TYPE_64 4

uintX_t

对比前后两版代码,不难发现在 Redis6.x 中 int 类型也多出了几种:uint8_t / uint16_t / uint32_t /uint64_t。乍一看以为是新增类型呢,毕竟 C语言里面可没有这些类型呀!查看相关源码,如下:

typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long long uint64_t;

对齐填充

在 Redis6.x 的源码中 SDS 的结构体为struct __attribute__ ((__packed__))struct有较大的差别,这其实和我们熟知的对齐填充有关。

(1)举个例子

考虑如下结构体:

typedef struct{char  c1;short s; char  c2; int   i;
} s;

若此结构体中的成员都是紧凑排列的,假设c1的起始地址为0,则s的地址为1,c2的地址为3,i的地址为4。下面用代码论证一下我们的假设。

#include <stdio.h>typedef struct
{char c1;short s;char c2;int i;
} s;int main()
{s a;printf("c1 -> %d, s -> %d, c2 -> %d, i -> %d\n",(unsigned int)(void *)&a.c1 - (unsigned int)(void *)&a,(unsigned int)(void *)&a.s - (unsigned int)(void *)&a,(unsigned int)(void *)&a.c2 - (unsigned int)(void *)&a,(unsigned int)(void *)&a.i - (unsigned int)(void *)&a);return 0;
}
// 结果为:c1 -> 0, s -> 2, c2 -> 4, i -> 8

尴尬😓了,和假设差的不是一星半点呀!这就是对齐填充搞的鬼,这啥啥啥呀~

(2)什么是字节对齐

现代计算机中,内存空间按照字节划分,理论上可以从任何起始地址访问任意类型的变量。但实际中在访问特定类型变量时经常在特定的内存地址访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序一个接一个地存放,这就是对齐。

(3)对齐原因

为什么需要对齐填充是由于各个硬件平台对存储空间的处理上有很大的不同。一些平台对某些特定类型的数据只能从某些特定地址开始存取。

最常见的是如果不按照适合其平台的要求对数据存放进行对齐,会在存取效率上带来损失。

比如有些平台每次读都是从偶地址开始,如果一个int型(假设为 32位)存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据,导致在读取效率上下降很多。

(4)更改对齐方式

注意:我们写程序的时候,不需要考虑对齐问题。编译器会替我们选择适合目标平台的对齐策略。

如果我们一定要手动更改对齐方式,一般可以通过下面的方法来改变缺省的对界条件:

  • 使用伪指令#pragma pack(n):C编译器将按照n个字节对齐;

  • 使用伪指令#pragma pack(): 取消自定义字节对齐方式。

另外,还有如下的一种方式(GCC特有语法):

  • __attribute((aligned (n))): 让所作用的结构成员对齐在n字节自然边界上。如果结构体中有成员的长度大于n,则按照最大成员的长度来对齐。

  • attribute ((packed)): 取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。

将上述示例代码的结构体更改如下(取消对齐),再次执行,可以发现取消对齐后和我们的假设就一致了。

typedef struct __attribute__ ((__packed__))
{char c1;short s;char c2;int i;
} s;
// 再次执行:c1 -> 0, s -> 1, c2 -> 3, i -> 4

(5)redis为什么不对齐呢?

综上所述我们知道了对齐填充可以提高 CPU 的数据读取效率,作为 IO 频繁的 Redis 为什么选择不对齐呢?

我们再次回顾 Redis6.x 中的 SDS 结构:

在这里插入图片描述

有个细节各位需要知道,即 SDS 的指针并不是指向 SDS 的起始位置(len位置),而是直接指向buf[],使得 SDS 可以直接使用 C 语言string.h库中的某些函数,做到了兼容,十分nice~。

如果不进行对齐填充,那么在获取当前 SDS 的类型时则只需要后退一步即可flagsPointer = ((unsigned char*)s)-1

相反,若进行对齐填充,由于 Padding 的存在,我们在不同的系统中不知道退多少才能获得flags,并且我们也不能将 sds 的指针指向flags,这样就无法兼容 C 语言的函数了,也不知道前进多少才能得到 buf[]。

sds优势

O(1)时间复杂度获取字符串长度

由于C字符串不记录自身的长度,所以为了获取一个字符串的长度程序必须遍历这个字符串,直至遇到’0’为止,整个操作的时间复杂度为O(N)。而我们使用SDS封装字符串则直接获取len属性值即可,时间复杂度为O(1)。

在这里插入图片描述

二进制安全

什么是二进制安全?

通俗地讲,C语言中,用’0’表示字符串的结束,如果字符串本身就有’0’字符,字符串就会被截断,即非二进制安全;若通过某种机制,保证读写字符串时不损害其内容,则是二进制安全。

C字符串中的字符除了末尾字符为’\0’外其他字符不能为空字符,否则会被认为是字符串结尾(即使实际上不是)。这限制了C字符串只能保存文本数据,而不能保存二进制数据。而SDS使用len属性的值判断字符串是否结束,所以不会受’\0’的影响。

杜绝缓冲区溢出

字符串的拼接操作是使用十分频繁的,在C语言开发中使用char *strcat(char *dest,const char *src)方法将src字符串中的内容拼接到dest字符串的末尾。

由于C字符串不记录自身的长度,所有strcat方法已经认为用户在执行此函数时已经为dest分配了足够多的内存,足以容纳src字符串中的所有内容,而一旦这个条件不成立就会产生缓冲区溢出,会把其他数据覆盖掉,Dangerous~。

// strcat 源码
char * __cdecl strcat (char * dst, const char * src)
{char * cp = dst;while( *cp )cp++; /* 找到 dst 的结尾 */while( *cp++ = *src++ ) ; /* 无脑将 src 复制到 dst 中 */return( dst ); /* 返回 dst */
}    

如下图所示为一次缓冲区溢出:

在这里插入图片描述

与C字符串不同,SDS 的自动扩容机制完全杜绝了发生缓冲区溢出的可能性:当SDS API需要对SDS进行修改时,API会先检查 SDS 的空间是否满足修改所需的要求,如果不满足,API会自动将SDS的空间扩展至执行修改所需的大小,然后才执行实际的修改操作,所以使用 SDS 既不需要手动修改SDS的空间大小,也不会出现缓冲区溢出问题。

SDS 的sds sdscat(sds s, const char *t)方法在字符串拼接时会进行扩容相关操作。

sds sdscatsds(sds s, const sds t) {return sdscatlen(s, t, sdslen(t));
}/* s: 源字符串* t: 待拼接字符串* len: 待拼接字符串长度*/
sds sdscatlen(sds s, const void *t, size_t len) {// 获取源字符串长度size_t curlen = sdslen(s);// SDS 分配空间(自动扩容机制)s = sdsMakeRoomFor(s,len);if (s == NULL) return NULL;// 将目标字符串拷贝至源字符串末尾memcpy(s+curlen, t, len);// 更新 SDS 长度sdssetlen(s, curlen+len);// 追加结束符s[curlen+len] = '\0';return s;
}
自动扩容机制——sdsMakeRoomFor方法

strcatlen中调用sdsMakeRoomFor完成字符串的容量检查及扩容操作,重点分析此方法:

/* s: 源字符串* addlen: 新增长度*/
sds sdsMakeRoomFor(sds s, size_t addlen) {void *sh, *newsh;// sdsavail: s->alloc - s->len, 获取 SDS 的剩余长度size_t avail = sdsavail(s);size_t len, newlen, reqlen;// 根据 flags 获取 SDS 的类型 oldtypechar type, oldtype = s[-1] & SDS_TYPE_MASK;int hdrlen;size_t usable;/* Return ASAP if there is enough space left. */// 剩余空间大于等于新增空间,无需扩容,直接返回源字符串if (avail >= addlen) return s;// 获取当前长度len = sdslen(s);// sh = (char*)s-sdsHdrSize(oldtype);// 新长度reqlen = newlen = (len+addlen);// 断言新长度比原长度长,否则终止执行assert(newlen > len);   /* 防止数据溢出 */// SDS_MAX_PREALLOC = 1024*1024, 即1MBif (newlen < SDS_MAX_PREALLOC)// 新增后长度小于 1MB ,则按新长度的两倍扩容newlen *= 2;else// 新增后长度大于 1MB ,则按新长度加上 1MB 扩容newlen += SDS_MAX_PREALLOC;// 重新计算 SDS 的类型type = sdsReqType(newlen);/* Don't use type 5: the user is appending to the string and type 5 is* not able to remember empty space, so sdsMakeRoomFor() must be called* at every appending operation. */// 不使用 sdshdr5 if (type == SDS_TYPE_5) type = SDS_TYPE_8;// 获取新的 header 大小hdrlen = sdsHdrSize(type);assert(hdrlen + newlen + 1 > reqlen);  /* Catch size_t overflow */if (oldtype==type) {// 类型没变// 调用 s_realloc_usable 重新分配可用内存,返回新 SDS 的头部指针// usable 会被设置为当前分配的大小newsh = s_realloc_usable(sh, hdrlen+newlen+1, &usable);if (newsh == NULL) return NULL; // 分配失败直接返回NULL// 获取指向 buf 的指针s = (char*)newsh+hdrlen;} else {// 类型变化导致 header 的大小也变化,需要向前移动字符串,不能使用 reallocnewsh = s_malloc_usable(hdrlen+newlen+1, &usable);if (newsh == NULL) return NULL;// 将原字符串copy至新空间中memcpy((char*)newsh+hdrlen, s, len+1);// 释放原字符串内存s_free(sh);s = (char*)newsh+hdrlen;// 更新 SDS 类型s[-1] = type;// 设置长度sdssetlen(s, len);}// 获取 buf 总长度(待定)usable = usable-hdrlen-1;if (usable > sdsTypeMaxSize(type))// 若可用空间大于当前类型支持的最大长度则截断usable = sdsTypeMaxSize(type);// 设置 buf 总长度sdssetalloc(s, usable);return s;
}

自动扩容机制总结:

扩容阶段:

  • 若 SDS 中剩余空闲空间 avail 大于新增内容的长度 addlen,则无需扩容;
  • 若 SDS 中剩余空闲空间 avail 小于或等于新增内容的长度 addlen:
    • 若新增后总长度 len+addlen < 1MB,则按新长度的两倍扩容;
    • 若新增后总长度 len+addlen > 1MB,则按新长度加上 1MB 扩容。

内存分配阶段:

  • 根据扩容后的长度选择对应的 SDS 类型:
    • 若类型不变,则只需通过 s_realloc_usable(即realloc函数)扩大 buf 数组即可;
    • 若类型变化,则需要为整个 SDS 重新分配内存,并将原来的 SDS 内容拷贝至新位置。

自动扩容流程图如下所示:

img

扩容后的 SDS 不会恰好容纳下新增的字符,而是多分配了一些空间(预分配策略),这减少了修改字符串时带来的内存重分配次数

内存重分配次数优化

(1)空间预分配策略

因为 SDS 的空间预分配策略, SDS 字符串在增长过程中不会频繁的进行空间分配。通过这种分配策略,SDS 将连续增长N次字符串所需的内存重分配次数从必定N次降低为最多N次。

(2)惰性空间释放机制

空间预分配策略用于优化 SDS 增长时频繁进行空间分配,而惰性空间释放机制则用于优化 SDS 字符串缩短时并不立即使用内存重分配来回收缩短后多出来的空间,而仅仅更新 SDS 的len属性,多出来的空间供将来使用。

SDS 中调用sdstrim方法来缩短字符串:

/* sdstrim 方法删除字符串首尾中在 cset 中出现过的字符* 比如:* s = sdsnew("AA...AA.a.aa.aHelloWorld     :::");* s = sdstrim(s,"Aa. :");* printf("%s\n", s);** SDS 变成了 "HelloWorld"*/
sds sdstrim(sds s, const char *cset) {char *start, *end, *sp, *ep;size_t len;sp = start = s;ep = end = s+sdslen(s)-1;// strchr()函数用于查找给定字符串中某一个特定字符while(sp <= end && strchr(cset, *sp)) sp++;while(ep > sp && strchr(cset, *ep)) ep--;len = (sp > ep) ? 0 : ((ep-sp)+1);if (s != sp) memmove(s, sp, len);s[len] = '\0';// 仅仅更新了lensdssetlen(s,len);return s;
}

勘误

在《Redis的设计与实现》一书中针对 sdstrim方法的讲解为:删除字符串中 cset 出现的所有字符,而不是首尾。

比如:调用sdstrim(“XYXaYYbcXyY”,“XY”),后移除了所有的’X’和’Y’。这是错误❌的~

SDS 并没有释放多出来的5字节空间,仅仅将 len 设置成了7,剩余空间为5。如果后续字符串增长时则可以派上用场(可能不需要再分配内存)。

也许各位看官又会有疑问了,这没真正释放空间,是否会导致内存泄漏呢?放心,SDS为我们提供了真正释放SDS未使用空间的方法sdsRemoveFreeSpace

sds sdsRemoveFreeSpace(sds s) {void *sh, *newsh;// 获取类型char type, oldtype = s[-1] & SDS_TYPE_MASK;// 获取 header 大小int hdrlen, oldhdrlen = sdsHdrSize(oldtype);// 获取原字符串长度size_t len = sdslen(s);// 获取可用长度size_t avail = sdsavail(s);// 获取指向头部的指针sh = (char*)s-oldhdrlen;/* Return ASAP if there is no space left. */if (avail == 0) return s;// 查找适合这个字符串长度的最优 SDS 类型type = sdsReqType(len);hdrlen = sdsHdrSize(type);/* 如果类型相同,或者至少仍然需要一个足够大的类型,我们只需 realloc buf即可;* 否则,说明变化很大,则手动重新分配字符串以使用不同的头文件类型。*/if (oldtype==type || type > SDS_TYPE_8) {newsh = s_realloc(sh, oldhdrlen+len+1);if (newsh == NULL) return NULL;s = (char*)newsh+oldhdrlen;} else {newsh = s_malloc(hdrlen+len+1);if (newsh == NULL) return NULL;memcpy((char*)newsh+hdrlen, s, len+1);// 释放内存s_free(sh);s = (char*)newsh+hdrlen;s[-1] = type;sdssetlen(s, len);}// 重新设置总长度为lensdssetalloc(s, len);return s;
}

sds最长是多少

Redis 官方给出了最大的字符串容量为 512MB。这是为什么呢?

在这里插入图片描述

在 Reids3.x 版本中len是使用int修饰的,这就会导致 buf 最长就是2147483647,无形中限制了字符串的最大长度。

任何细节在源码中都能发现,在_sdsnewlen方法创建 SDS 中都会调用sdsTypeMaxSize方法获取每种类型所能创建的最大buf长度,不难发现此方法最大的返回值为2147483647,即512MB。

static inline size_t sdsTypeMaxSize(char type) {if (type == SDS_TYPE_5)return (1<<5) - 1;if (type == SDS_TYPE_8)return (1<<8) - 1;if (type == SDS_TYPE_16)return (1<<16) - 1;
#if (LONG_MAX == LLONG_MAX)if (type == SDS_TYPE_32)return (1ll<<32) - 1; // 不管方法啥意思,最大返回2147483647。OVER~
#endifreturn -1; /* this is equivalent to the max SDS_TYPE_64 or SDS_TYPE_32 */
}

此方法在 Redis3.0.0中是不存在的

部分API源码解读

创建sds

Redis 通过sdsnewlen方法创建 SDS。在方法中会根据字符串初始化长度选择合适的类型。

sds _sdsnewlen(const void *init, size_t initlen, int trymalloc) {void *sh;sds s;// 根据初始化长度判断 SDS 的类型char type = sdsReqType(initlen);// SDS_TYPE_5 强制转换为 SDS_TYPE_8// 这样侧面验证了 sdshdr5 从未被使用,创建这一步就GG了 ੯ੁૂ‧̀͡u\if (type == SDS_TYPE_5 && initlen == 0) type = SDS_TYPE_8;// 获取头部大学int hdrlen = sdsHdrSize(type);// 指向 flags 的指针unsigned char *fp; /* flags pointer. */// 分配的空间size_t usable;// 防止溢出assert(initlen + hdrlen + 1 > initlen); /* Catch size_t overflow */// 分配空间// s_trymalloc_usable: 尝试分配内存,失败则返回NULL// s_malloc_usable: 分配内存或者抛异常[不友好]sh = trymalloc?s_trymalloc_usable(hdrlen+initlen+1, &usable) :s_malloc_usable(hdrlen+initlen+1, &usable);if (sh == NULL) return NULL;if (init==SDS_NOINIT)init = NULL;else if (!init)memset(sh, 0, hdrlen+initlen+1);// s 此时指向bufs = (char*)sh+hdrlen;// 指向flagsfp = ((unsigned char*)s)-1;usable = usable-hdrlen-1;// 对不同类型的 SDS 可分配空间进行截断if (usable > sdsTypeMaxSize(type))usable = sdsTypeMaxSize(type);switch(type) {case SDS_TYPE_5: {*fp = type | (initlen << SDS_TYPE_BITS);break;}case SDS_TYPE_8: {SDS_HDR_VAR(8,s);sh->len = initlen;sh->alloc = usable;*fp = type;break;}// ... 省略}if (initlen && init)memcpy(s, init, initlen);// 末尾添加\0s[initlen] = '\0';return s;
}

通过sdsnewlen方法我们可以获得以下信息:

  • SDS_TYPE_5 会被强制转换为 SDS_TYPE_8 类型;
  • 创建时默认会在末尾加'\0'
  • 返回值是指向 SDS 结构中 buf 的指针;
  • 返回值是char *sds类型,可以兼容部分C函数。

释放sds

为了优化性能,SDS 提供了不直接释放内存,而是通过重置len达到清空 SDS 目的的方法——sdsclear。改方法仅仅将 SDS 的len归零,而buf的空间并为真正被清空,新的数据可以复写,而不用重新申请内存。

void sdsclear(sds s) {sdssetlen(s, 0);// 设置len为0s[0] = '\0';//“清空”buf
}

若真正想清空 SDS 则可以调用sdsfree方法,底层通过调用s_free释放内存。

void sdsfree(sds s) {if (s == NULL) return;s_free((char*)s-sdsHdrSize(s[-1]));
}

相关文章:

redis6.0源码分析:简单动态字符串sds

文章目录 sds简介与特性(面试)sds结构模型数据结构苛刻的数据优化数据结构优化uintX_t对齐填充 sds优势O(1)时间复杂度获取字符串长度二进制安全杜绝缓冲区溢出自动扩容机制——sdsMakeRoomFor方法 内存重分配次数优化 sds最长是多少部分API源码解读创建sds释放sds sds简介与特…...

1.7 攻击面和攻击树

思维导图&#xff1a; 1.7 攻击面与攻击树 攻击面: 描述计算机和网络系统面对的安全威胁和攻击。 定义: 攻击面是由系统中可访问和可利用的漏洞所组成。常见攻击面: 向外部Web及其他服务器开放的端口和相应代码。防火墙内部的服务。处理入站数据、电子邮件、XML文件、Office文档…...

解决input在谷歌浏览器自动填充问题

解决input在谷歌浏览器自动填充问题 <input typepassword readonly onfocus"this.removeAttribute(readonly);" />...

Java字节码技术

Java 字节码简介 Java 中的字节码&#xff0c;英文名为 bytecode, 是 Java 代码编译后的中间代码格式。JVM 需要读取并解析字节码才能执行相应的任务。 从技术人员的角度看&#xff0c;Java 字节码是 JVM 的指令集。JVM 加载字节码格式的 class 文件&#xff0c;校验之后通过 J…...

Java SE 学习笔记(十八)—— 注解、动态代理

目录 1 注解1.1 注解概述1.2 自定义注解1.3 元注解1.4 注解解析1.5 注解应用于 junit 框架 2 动态代理2.1 问题引入2.2 动态代理实现 1 注解 1.1 注解概述 Java 注解&#xff08;Annotation&#xff09;又称Java标注&#xff0c;是JDK 5.0引入的一种注释机制&#xff0c;Java语…...

虚拟内存之请求分页管理

一、与基本分页存储管理的区别 程序执行过程中&#xff0c;访问信息不在内存时&#xff0c;OS需要从外存调入内存。——>调页功能 内存空间不够时&#xff0c;OS需要将内存中暂时用不到的信息换出到外存。——>页面置换功能 二、页表机制 1.页表&#xff1a;需要知道页面…...

lazarus开发:提升sqlite数据插入速度

目录 1 前言 2 优化数据容器 3 开启事务插入数据 4 其他方面优化 1 前言 近期有一个需求是向数据库中插入excel文件中的10万多条数据&#xff0c;接近70个字段。最初整个插入数据时间是大约40分钟&#xff0c;经过优化调整后&#xff0c;大幅优化为大约5分钟。这里简单介绍…...

瑞萨RH850-P1X ECM和英飞凌TC3xx SMU对比

1.1 基本结构 P1X ECM(Error Control Module)收集从不同的错误源和监控电路发来的错误信号&#xff0c;并通过error pin(ERROROUTZ)对外输出、产生中断并发出ECM reset信号。 P1x-C系列根据产品型号不同&#xff0c;ECM个数也不相同&#xff0c;如下&#xff1a; 对应寄存器基地…...

Ajax学习笔记第三天

做决定之前仔细考虑&#xff0c;一旦作了决定就要勇往直前、坚持到底&#xff01; 【1 ikunGG邮箱注册】 整个流程展示&#xff1a; 1.文件目录 2.页面效果展示及代码 mysql数据库中的初始表 2.1 主页 09.html:里面代码部分解释 display: inline-block; 让块元素h1变成行内…...

ESP32-C3 低功耗懒人开关:传统开关轻松上云和本地控制

项目背景 随着科技的快速发展&#xff0c;智能家居已经成为我们日常生活的一部分。而对于基础设施已经配备完毕的家庭而言&#xff0c;对家居设备的智能化改造是一项相对困难的工作。本文将分享一款基于 Wi-Fi 的低功耗懒人开关—— “ESP32-C3 管灯熊猫”。将智能的 “ESP32-…...

前端学习路线指南:从入门到精通【①】

前言 作为一个前端开发者&#xff0c;学习前端技术是必不可少的。然而&#xff0c;由于前端领域的广阔和不断演进的技术栈&#xff0c;对于初学者来说可能会感到困惑。本篇文章将为你提供一个清晰的前端学习路线&#xff0c;帮助你系统地掌握前端开发技能&#xff0c;并成为一名…...

Flash模拟EEPROM原理浅析

根据ST的手册&#xff0c;我们可以看到&#xff0c;外挂EEPROM和Dflash模拟EEPROM&#xff0c;区别如下&#xff1a; 很明显&#xff0c;模拟EEprom的写入速度要远远快于外挂eeprom(有数据传输机制)&#xff1b; 其次&#xff0c;外挂EEPROM不需要擦除即可实现写入数据&#xf…...

Typora 最新激活方法

Markdown是一种可以使用普通文本编辑器编写的标记语言&#xff0c;通过简单的标记语法&#xff0c;它可以使普通文本内容具有一定的格式&#xff0c;其目标是实现易读易写。而Typora则是一个非常不错的Markdown编辑器&#xff0c;它的界面非常的简洁直观&#xff0c;并且功能各…...

jenkins如何安装?

docker pull jenkins/jenkins:lts-centos7-jdk8 2.docker-compose.yml version: 3 services:jenkins:image: jenkins/jenkins:lts-centos7-jdk8container_name: my-jenkinsports:- "8080:8080" # 映射 Jenkins Web 界面端口volumes:- jenkins_home:/var/jenkins_h…...

从零开始的LINUX(三)

bc&#xff1a;进行浮点数运算 uname&#xff1a;查看当前的操作系统 ctrlc&#xff1a;中止当前正在执行的程序 ctrld&#xff1a;退出xshell shutdown&#xff1a;关机 reboot&#xff1a;重启 shell外壳&#xff1a; 作用&#xff1a;1、命令解释&#xff08;将输入的程序…...

CleanMyMac2024永久免费版Mac系统磁盘清理工具

Cleanmymac对很多用户来说已经非常熟悉了&#xff0c;因为在网上如果你搜寻有关清理mac系统方面的软件时&#xff0c;占比非常多的会是cleanmymac的相关消息。许多刚从Windows系统转向Mac系统怀抱的用户&#xff0c;一开始难免不习惯&#xff0c;因为Mac系统没有像Windows一样的…...

HashSet 元素不重复

HashSet通过底层使用HashMap来保证元素不重复。具体来说&#xff0c;HashSet内部维护一个HashMap&#xff0c;其中元素存储在HashMap的key上&#xff0c;而所有的value都指向同一个共享的内部对象。在存储元素时&#xff0c;HashSet会根据元素的hashCode值来确定其在HashMap中的…...

基于SpringBoot的二手车交易系统的设计与实现

目录 前言 一、技术栈 二、系统功能介绍 管理员功能实现 商家管理 公告信息管理 论坛管理 商家功能实现 汽车管理 汽车留言管理 论坛管理 用户功能实现 汽车信息 在线论坛 公告信息 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 如今社会上各行…...

最短路径:迪杰斯特拉算法

简介 英文名Dijkstra 作用&#xff1a;找到路中指定起点到指定终点的带权最短路径 核心步骤 1&#xff09;确定起点&#xff0c;终点 2&#xff09;从未走过的点中选取从起点到权值最小点作为中心点 3&#xff09;如果满足 起点到中心点权值 中心点到指定其他点的权值 < 起…...

基于UDP/TCP的网络通信编程实现

小王学习录 今日鸡汤Socket套接字基于UDP来实现一个网络通信程序DatagramSocket类DatagramPacket类基于UDP的服务器端代码基于UDP的客户端代码基于TCP来实现一个网络通信程序ServerSocket类Socket类基于TCP的服务器端代码基于TCP的客户端代码优化之后的服务器端代码补充TCP长短…...

springboot启动报错

...

Python中的split()函数

函数&#xff1a;split() Python中有split()和os.path.split()两个函数&#xff0c;具体作用如下&#xff1a; split()&#xff1a;拆分字符串。通过指定分隔符对字符串进行切片&#xff0c;并返回分割后的字符串列表&#xff08;list&#xff09; os.path.split()&#xff1a…...

大数据-玩转数据-Python Sftp Mysql 数据

一、需求描述 1、从Mysql数据库表下载数据到服务器&#xff1b; 2、将数据已csv文件格式存储并对数据格式进行处理&#xff08;添加表头&#xff0c;表头和数据均用竖线分隔符隔开&#xff0c;末尾也加分割符&#xff09;&#xff1b; 3、文件路径文件夹以天为单位&#xff0c…...

Selenium3-当元素通过@FindBy获取时,返回元素为null

报错: 在获取元素的js属性时一直获取不到&#xff0c;报空指针&#xff0c;定位到元素时&#xff0c;发现是FindBy的元素没有找到 解决方法: 在page类的构造函数中加上了 界面初始化&#xff0c;让元素先隐式加载&#xff0c;这样就不会出现返回元素为空的情况辣 PageFactory…...

JWT详解解读读

&#x1f4d1;前言 本文主要是jwt解读文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是青衿&#x1f947; ☁️博客首页&#xff1a;CSDN主页放风讲故事 &#x1f304;每日一句&#xff1a;努力一点&#…...

一文详解如何从 Oracle 迁移数据到 DolphinDB

Oracle 是一个广泛使用的关系型数据库管理系统&#xff0c;它支持 ACID 事务处理&#xff0c;具有强大的安全性和可靠性&#xff0c;因此被广泛应用于各种企业级应用程序。但是&#xff0c;随着数据规模的增加和业务需求的变化&#xff0c;Oracle 的一些限制和缺点也逐渐暴露出…...

负载均衡--Haproxy

haproxy 他也是常用的负载均衡软件 nginx 支持四层转发&#xff0c;七层转发 haproxy也可以四层和七层转发 haproxy&#xff1a;法国人开发的威利塔罗在2000年基于C语言开发的一个开源软件 可以支持一万以上的并发请求 高性能的tcp和http负载均衡2.4 1.5.9 haproxy&#…...

股票价格预测 | 融合CNN和Transformer以提升股票趋势预测准确度

一 本文摘要 股票价格往往很难预测,因为我们很难准确建模数据点之间的短期和长期时间关系。卷积神经网络(CNN)擅长找出用于建模短期关系的局部模式。然而,由于其有限的观察范围,CNN无法捕捉到长期关系。相比之下,Transformer可以学习全局上下文和长期关系。本文提出了一…...

QMI8658A_QMC5883L(9轴)-EVB 评估板

1. 描述 QMI8658A_QMC5883L(9轴)-EVB 评估板是一款功能强大的9轴IMU传感器&#xff0c;它利用了QMA8658A 内置的3轴加速度计和3轴陀螺仪&#xff0c;同时结合QMC5883L的3轴地磁数据&#xff0c;来测量物体在三维空间中的角速度和加速度&#xff08;严格意义上的IMU只为用户提供…...

vue2+antd——实现动态菜单路由功能——基础积累

vue2antd——实现动态菜单路由功能——基础积累 实现的需求&#xff1a;效果图&#xff1a;登录接口处添加以下代码loadRoutes方法内容如下&#xff1a; 最近在写后台管理系统&#xff0c;遇到一个需求就是要将之前的静态路由改为动态路由&#xff0c;使用的后台框架是&#xf…...