Windows管理内存的3种方式——堆、虚拟内存、共享内存

news2024/11/28 6:27:35

一、操作系统管理内存概述

在 Windows 操作系统中,每个进程都被分配了 4GB 的虚拟地址空间,这被称为进程的虚拟地址空间。虚拟地址空间提供了一个抽象的地址空间,使得每个进程都可以认为它拥有自己的独立内存空间。这个虚拟地址空间被分为两个部分,即用户空间和内核空间。这个虚拟地址空间的前 2GB 是给操作系统内核使用的,这个部分被称为内核空间。内核空间是由操作系统内核所控制和管理的。它包含了操作系统所需的内存,例如内核代码、驱动程序和系统数据结构等。在 Windows 操作系统中,内核空间的大小是固定的,且不可更改。

现在我来讲一下重点内容:

有很多同学搞不懂虚拟内存是个什么东西,首先我声明一下,我们这里讲的是每个进程拥有的独立的4GB虚拟内存空间,这4GB虚拟内存空间是操作系统给每个进程许诺的一张空头支票,平时这4GB空间不会真的分配给你,而是当你程序的一部分真正需要运行的时候,操作系统才会为你分配物理页,也就是真正的物理内存。再说通俗一点:虚拟内存就是你代码编译的时候,编译器给你写死的一个地址,你的变量存放的位置(比如数组变量,int类型的变量之类的)在编译的时候已经写死了,包括你通过malloc动态申请的内存,这些虚拟地址写死在exe里面,真正执行的时候操作系统需要将虚拟内存给物理内存之间建立一一映射关系,这种映射关系就记录在操作系统的PCB里面,然后上处理机执行的时候会通过MMU自动将虚拟地址转换为物理地址,这里不再赘述,可以简单提示一下,MMU无非就是从PCB里面取出来当前进程的基地址,然后从虚拟地址里获取偏移就可以正确映射物理地址了,至于怎么获取偏移感兴趣的同学可以自行查找资料。这里顺便说一下,你64位的程序也可以把虚拟地址设置为4GB,你只要映射的时候物理地址的基地址选取好,他的寻址空间同样可以扩充到8G,16G,32G等等,这里的4GB虚拟地址空间和你的寻址范围没有什么必然联系!

 

而虚拟地址空间的后 2GB 则是给用户进程使用的,这个部分被称为用户空间。用户空间中存放着进程所需的代码、数据和堆栈等。用户空间的大小是由操作系统所分配的内存大小决定的,它是可以动态调整的,也就是说当进程需要更多的内存时,操作系统会自动为进程分配更多的空间。

在 Windows 操作系统中,虚拟地址空间中的每一个地址都是虚拟地址,每一个虚拟内存空间都是和操作系统的物理页一一对应的。即虚拟内存通过操作系统内核来将其转换为物理地址,从而能够真正访问对应的内存空间。这个地址转换过程是透明的,进程并不需要关心物理地址是什么。这样的虚拟地址转换机制,保证了每个进程在使用内存时的独立性,也避免了进程之间相互干扰的情况发生。为什么能够保证内存物理空间上的隔离?这是因为操作系统在为进程分配内存的时候首先选择一块空闲的真实物理内存页,然后将分配页的基址以及分配空间的大小写在进程的PCB当中,这样每个程序加上偏移所得到的虚拟地址与物理内存一一映射,从而保证了进程之间的相互隔离。

二、堆内存

我们都知道:栈是操作系统给运行中的程序的分的一块确定大小的内存,这个内存大小是在编译的过程中就已经写死了,由编译器提交给OS来分配确定大小的内存空间,而堆是当我们在程序运行的过程当中想使用一块儿不确定大小内存时动态申请的,通常是在堆里面进行分配内存,即从虚拟内存中割出来一小块作为堆内存来使用。

我们真正想要申请内存只有两个办法,一是通过VirtualAlloc,二是通过CreateFileMapping这两个函数,我们用的HeapAlloc其实也是一种假申请,我们平常用的new(new=malloc+构造函数)和malloc他俩也和内存申请没有关系,他们名义上是指申请内存,实际上根本就不是,malloc是从已经申请好的虚拟内存中割出来一小块供自己使用,VirtualAlloc则可以看成是向操作系统批发物理页的一个过程,在这个过程中的内存申请是真实存在的,而malloc是假申请,它是在这块虚拟内存本身就已经申请过了,归你了的基础上,自己从中割出一小块内存拿来使用。堆内存和栈内存都是在进程启动时,操作系统就已经替我们申请好了。换句话说,就是在你HeapAlloc一小块内存之前,这块内存就已经存在了。

待补充。。。(有时间会更新)

在 Windows 操作系统中,多个进程可能会需要访问同一份数据,例如共享的 DLL 库、共享内存、共享文件等等。为了节约内存,Windows 采用了一种称为“共享页面”的机制,尽量保证同一份数据在物理内存中只有一份,并分别映射到多个进程中。

当多个进程需要访问同一份数据时,Windows 会将这份数据对应的物理内存页面标记为“可共享”的状态,这个过程可以通过一些系统调用(如CreateFileMapping、MapViewOfFile等)来实现。然后,为每个进程分配一个虚拟地址空间,并将这些虚拟地址空间映射到同一个物理内存页面上。这样,每个进程在访问这份数据时,实际上是访问同一个物理内存页面,而不是各自拥有一份独立的拷贝。

三、虚拟内存

1、内存分页的概念:

内存分页是一种将物理内存划分成固定大小的块,并将虚拟内存映射到这些物理内存块的技术。在内存分页的实现中,每个物理内存块被称为一个页框,每个虚拟内存块被称为一个页面。操作系统将虚拟地址空间划分为固定大小的页面,并将其映射到物理地址空间中的页面框中。

内存分页的主要目的是实现虚拟内存,以提高系统的内存利用率。由于虚拟内存允许将页面换入换出到磁盘上,因此在物理内存不足时,可以将不常用的页面换出到磁盘上,从而释放物理内存,并为正在执行的进程提供足够的内存空间。同时,内存分页还可以使操作系统更加灵活地管理内存,以便实现内存保护和共享等功能。

在内存分页的实现中,操作系统通常会将每个页面映射到一个页表项中。页表项包含了虚拟页面的地址、页面的状态信息、页面的访问权限、页面所属的进程等信息。当CPU访问一个虚拟地址时,操作系统会根据该地址对应的页表项,将虚拟地址映射到物理地址,并检查对应的页面是否已经在物理内存中。如果页面不在物理内存中,操作系统就会触发一个缺页异常,将页面从磁盘上读取到物理内存中,并更新页表项。

内存分页是一种将虚拟内存映射到物理内存的技术,它实现了虚拟内存和内存保护等功能,提高了系统的内存利用率,并为操作系统提供了更加灵活的内存管理手段。

2、操作系统分页概述:

操作系统如何管理内存?

》操作系统管理内存是将内存分成一页一页来管理的,每一页的大小是4K也就是0x1000

4G的内存共有1M个页

使用了分页机制之后,4G的地址空间被分成了固定大小的页,每一页或者被映射到物理内存,或者被映射到硬盘上的交换文件中,或者没有映射任何东西。对于一般程序来说,4G的地址空间,只有一小部分映射了物理内存,大片大片的部分是没有映射任何东西。CPU用来把虚拟地址转换成物理地址的信息存放在叫做页目录和页表的结构里。

3、虚拟内存状态:

状态

空闲(FREE)

内存页不可用

保留(Reserve)

内存页被预定了,但为与物理内存做映射,还是不可用

提交(Commit)

内存被分配,并且与物理内存进行了映射,进程可以使用了

当虚拟内存映射到物理内存时,有三种映射方式

4、内存映射方式:

映射方式

描述

Private

进程私有内存,不被其他进程所共享,一般是堆,栈

Mapped

从别的内存映射而来

Image

从程序的PE映像映射而来。

虚拟内存管理-内存属性:

Windows中,内存管理的最小单元是一个内存页 通常是0x1000=4kb

5、内存分页属性:

ReadOnly

只读

READ_WRITE

读写

EXECUTE

执行

EXECUTE_READ_WRITE

可读可写可执行

WRITE_COPY

写时拷贝

6、页交换文件逻辑:

程序访问虚拟内存地址,操作系统判断数据是否在内存中,如果在就从虚拟地址映射到的物理地址,如果不在就判断是否在页交换文件当中,如果在就查看物理内存是否有闲置空间,有的话,就将页交换文件载入到物理内存,如果没有闲置内存,就从物理内存中找到一个可以释放的页,然后将页保存到页交换文件中。

7、虚拟内存相关API

VirtualAlloc

分配或者预定一块虚拟内存

VirtualAllocEx

可以在其他进程分配或者预定一块内存

VirtualFree

释放内存

VirtualFreeEx

可以释放其他进程内存

VirtualLock

锁定内存不能交换到硬盘

VirtualUnLock

解锁

VirtualProtect

修改内存读写执行属性

VirtualProtectEx

可以修改其他进程内存属性

ReadProcessMemory

读取远程进程内存

WriteProcessMemory

写入数据到远程进程内存

VirtualQuery

查询内存状态

VirtualQueryEx

8、虚拟内存小案例

下面我们利用虚拟内存的特点来修改C++当中的常量:没错!就是那些你以为不可能被改动的const char*那些玩意🙂🙂

事情是这样的:浮沉最近经常摆烂,他感觉这样不好,于是偷偷黑入了我的电脑,把我虚拟内存常量区的《浮沉学习记录表》偷偷改了,从此,浮沉变成了一个爱学习的乖宝宝😂😂😂

以下是完整的代码部分:(他的代码忘记删了,我复制了一份)

#include <iostream>
#include<Windows.h>
#include<cstring>
int main()
{
    const char* str = "浮沉今天在好好摆烂!\n";
    std::cout << "字符串常量修改前:" << str << std::endl;

    // 获取字符串所在内存区域的地址
    const void* addr = static_cast<const void*>(str);

    // 修改内存保护属性为可写属性
    //记得保存一下内存区域的原有访问属性,以便后续恢复他的属性,避免报错
    DWORD old_protect;
    VirtualProtect((char*)addr, strlen(str), PAGE_EXECUTE_READWRITE, &old_protect);
    // 修改字符串内容
    char* writable_str = const_cast<char*>(str);
    char* index = std::strstr(writable_str, "摆烂");
    if (index != nullptr) {
        std::memcpy(index, "学习!\0", 7);
    }
    // 输出修改后的字符串内容
    std::cout << "字符串常量修改后:" << str << std::endl;

    // 恢复内存保护属性为只读属性
    VirtualProtect((char*)addr, strlen(str), old_protect, &old_protect);

    return 0;
}

四、文件映射

1、文件映射的概念

文件映射(File Mapping)是Windows系统中一种高效的文件访问方式。它通过将文件数据与进程的虚拟地址空间相关联,这样访问虚拟内存就是直接访问文件了,让进程可以直接读写文件数据,避免了传统的文件I/O操作带来的频繁的磁盘I/O,提高了文件访问的效率。

注:这里的所谓和虚拟地址建立联系,本质就是和物理内存建立联系!表面上来看修改虚拟内存就直接修改磁盘上的文件了,本质上还是要通过物理内存来连接滴😐

2、文件映射的原理

1)文件映射首先通过系统调用创建一个FileMapping映射对象(内核对象)

2)然后将映射对象关联到一个文件或其他I/O设备(关联我们准备操作的文件)

3)最后将映射对象的内容映射到进程的虚拟地址空间中

通过文件映射的方式使得进程可以通过访问内存的方式直接读写文件或设备的数据,修改这个内容之后由操作系统来负责自动同步磁盘上文件内容的更新,所以有时候你在内存修改文件的时候,磁盘上的源文件可能并没有同步更新,这时候我们可以使用Windows提供的另外一个接口FlushViewOfFile来刷新缓冲区,实现将映射在内存当中的文件立即写回到磁盘当中。

这个过程中,操作系统会将映射对象分页,每一页映射到虚拟地址空间的一个页面上,并且会对访问的数据进行合适的缓存,从而提高了文件访问的效率。

3、下面我来详细解释一下为什么通过文件映射可以提高访问文件的访问效率:

首先我们需要明确两点:一是在读取大文件的时候通过文件映射才可以提高访问效率,其实原因很简单,你文件太小的话,两种方式都会直接把整个文件全部读到内存当中去,启动一次磁盘,没什么效率上的区别。二是所谓提高文件访问效率本质就是减少启动磁盘的次数!

因此,我们不难发现,采用传统的方式操作文件,属于一种颗粒度比较大的读取,而且没有通过操作系统进行统一集中管理,比如在操作文件的时候,发现文件的内容没有被加载进内存,通过传统的方式就是要把相关的块一股脑地全部加载进来,但是你很有可能只需要使用其中很少的一部分内容,而且长时间不用还有读回磁盘,这就又需要额外启动磁盘,我们都知道IO设备的访问速度远远慢于内存,你直接按照块去加载会启动不止一次磁盘,而且每一层加载很多内容速率也慢,同时会造成额外的空间开销,而使用文件映射之后,就相当于是把磁盘当中内存来使用了,一样是按页来进行装载,一个页大小4KB大小,十分轻量,就算读取IO也不会浪费太多时间,而且文件映射对象是通过内核进行管理的,可以十分精确地把没有加载的内容装载进来,而不会造成额外的开销!

4、文件映射的应用场景

  1. 需要频繁地读写大文件或大数据的场景,比如视频编辑、图像处理等,使用文件映射可以提高访问效率,减少磁盘I/O带来的性能瓶颈。

  2. 需要多个进程共享同一个数据源的场景,比如IPC(进程间通信),使用文件映射可以让多个进程共享同一个数据源,避免了复制数据和进程间通信的开销。

  3. 实现内存映射文件(Memory-Mapped Files),可以将一个文件映射到内存中,通过读写内存的方式来读写文件数据。内存映射文件可以实现对文件的共享访问,使得多个进程可以共享同一个文件数据。

我们以前操作文件,都是将文件从磁盘当中先读入内存当中,然后在内存当中对文件的内容进行操作,操作完成之后再将文件内容从内存写回磁盘当中,这样的操作方式的效率无疑十分低下。

因此Windows当中提供了一种文件映射内存的方式操作文件,也就是建立虚拟内存和磁盘文件的映射,在这里这个磁盘文件我们就相当于当成是物理内存来用,从虚拟内存的角度来看,磁盘文件和物理内存本质就是一个东西,只不过物理内存的读写速度更快,仅此而已。

5、文件映射常用API:

CreateFileMapping

创建一个Mapping对象

OpenFileMapping

打开一个Mapping对象

MapViewOfFile

将maping对象的文件映射到内存中

UnmapViewOfFile

取消文件映射

FlushViewOfFile

刷新缓存区,将映射在内存中的文件写回到硬盘中

6、下面我们来梳理一下:

正常操作文件:

CreateFile--》文件句柄---》通过文件句柄将文件内容读到虚拟内存,修改内容--》重写写入到文件。

文件映射:

CreateFile打开文件---》创建文件映射对象---》将文件映射到内存---》对内存的操作直接映射到文件当中。

文件映射还有一个重要的特点:可以在不同的进程间共享数据。(跨进程)!!!

下面我们来重点介绍一下文件映射用于共享内存的功能

7、共享内存小案例:

我们可以通过一个小案例来说明一下如何利用共享内存来实现限制程序多开!

事情是这样的,我有一个朋友叫浮沉,他很喜欢摆烂,他经常在电脑上使用一款名叫《只因你太美.exe》的舞蹈软件来欣赏哥哥的舞姿,现在我要在物理上让他实现科学上网:我们都知道”事不过三“这个Old Wisdom,当他打开《只因你太美.exe》软件超过三次,软件就会打开一个弹窗,提醒他停止摆烂,赶紧去学习,从而达到科学上网的目的。

先说思路:我们利用映射内核对象跨进程共享的属性,每次打开一个进程,就在共享内存的这块区域把计数器+1(计数器就存放在共享内存当中),这样新打开的进程就知道在他之前以及打开多少个进程了。注意!这个操作要执行在main函数的最前面,即在开始我们的业务之前进行执行,这样一旦多开数量超过我们设置的阈值,后面的业务程序就不会被执行了。

具体步骤就是:
1)首先通过OpenFileMappingA函数,根据共享内存对象的名称打开我们创建的共享内存对象,如果返回NULL,我们就通过CreateFileMappingA创建一个共享内存对象
2)将共享内存对象的句柄传入MapViewOfFile函数,调用函数后会返回我们创建的共享内存的首地址
3)然后我们通过一个指针来拿到这块内存地址,直接对其进行操作就好了,对于跨进程的程序,如果有必要的话,还应考虑同步,互斥的问题

以下是完整代码:

#include <iostream>
#include<Windows.h>

//打开超过3个exe就关闭
//创建一个共享内存,每打开一个进程就操作共享缓冲区的计数器,给他+1
//如果number>阈值就return掉即可

#define BUF_SIZE 4096
#define ERROR -1

int main()
{
    //修改控制台输出颜色
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, FOREGROUND_GREEN);

    HANDLE hFileMapping;
    hFileMapping = OpenFileMappingA(FILE_MAP_ALL_ACCESS, NULL, "只因你太美");
    if (hFileMapping == NULL) {
        hFileMapping = CreateFileMappingA(
            INVALID_HANDLE_VALUE,   // 使用页文件创建文件映射
            NULL,                   // 默认安全性
            PAGE_READWRITE,         // 可读可写
            0,                      // 文件映射的大小(高位)
            BUF_SIZE,               // 文件映射的大小(低位)
            "只因你太美"            // 共享内存对象名
        );
    }
    if (hFileMapping == NULL) {
        printf("CreateFileMapping failed (error %lu)\n", GetLastError());
        return ERROR;
    }
    LPVOID lpMapView = MapViewOfFile(
        hFileMapping,           // 共享内存对象句柄
        FILE_MAP_WRITE,     // 可读可写
        0,                  // 文件偏移高位
        0,                  // 文件偏移低位
        BUF_SIZE            // 映射视图大小
    );
    if (lpMapView == NULL) {
        printf("MapViewOfFile failed (error %lu)\n", GetLastError());
        CloseHandle(hFileMapping);
        return ERROR;
    }

    //在共享缓冲区写内容
    int ProcessCnt = 0;
    DWORD* pBuffer = (DWORD*)lpMapView;

    *pBuffer += 1;
    ProcessCnt = *pBuffer;

    if (ProcessCnt > 3) {
        MessageBoxA(0, "浮沉摆烂时间大于3天,请立即滚回去学习!", "严重警告!!!", MB_OK);
        *pBuffer -= 1;
        return 0;
    }
    printf("*****************浮沉摆烂监控程序已启动!*****************\n");
    printf("应用进程正常加载!但是:\n");
    printf("今天是浮沉摆烂的第%d天\n", ProcessCnt);
    system("pause");
    *pBuffer -= 1;
    printf("浮沉竟然竟然好好了一天!\n");
    printf("*****************浮沉摆烂监控被干掉一个!*****************\n");
    system("pause");
    SetConsoleTextAttribute(hConsole, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/533203.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

720度沉浸式体验,VR虚拟展馆的价值有哪些?

展馆作为一个展示商品、会议交流、信息传播、经济贸易的场所&#xff0c;能够创造巨大的经济效益和社会效益。什么是VR虚拟展馆呢&#xff1f;VR虚拟展馆是基于VR全景技术打造的线上展厅&#xff0c;可以应用在多种领域中展示各式的商品和内容&#xff0c;观众通过VR虚拟展馆可…

ubuntu系统下使用ros控制UR真实机械臂,逻辑清晰,亲测有效

梳理一下在ubuntu系统使用ros控制UR真实机械臂的思路&#xff0c;逻辑清晰&#xff0c;亲测有效&#xff0c;并记录踩过的坑。从0开始&#xff0c;使用ros控制真实UR机械臂。 环境&#xff1a;ubuntu18.04 ros版本&#xff1a;melodic 机械臂型号&#xff1a;UR5e 一&#xff…

当我与单链表分手后,在酒吧邂逅了双向循环链表.....

链表的种类有8种&#xff0c;但我们最常用的为无头单向非循环链表和带头双向循环链表。 带头双向循环链表 当带头双向循环链表只有哨兵位头的时候&#xff0c;双向链表的指向如下图。 head->pre和head->next都是指向自己&#xff0c;这个是有巨大优势的&#xff0c;代码…

CTFHub | 文件包含

0x00 前言 CTFHub 专注网络安全、信息安全、白帽子技术的在线学习&#xff0c;实训平台。提供优质的赛事及学习服务&#xff0c;拥有完善的题目环境及配套 writeup &#xff0c;降低 CTF 学习入门门槛&#xff0c;快速帮助选手成长&#xff0c;跟随主流比赛潮流。 0x01 题目描述…

一篇文章让你上手Canal数据同步神技~

视频教程传送门&#xff1a; Canal极简入门&#xff1a;一小时让你快速上手Canal数据同步神技~_哔哩哔哩_bilibiliCanal极简入门&#xff1a;一小时让你快速上手Canal数据同步神技~共计13条视频&#xff0c;包括&#xff1a;01.课前导学与前置知识点、02.Canal组件了解、03.My…

光纤收发器可以连接光模块吗?

随着科技的进步发展&#xff0c;城市信息化速度的加快&#xff0c;光通信产品在数据中心和安防监控等场景中的运用越来越广泛&#xff0c;而这之间的连接则需要光模块和光纤收发器来实现。很多用户对光模块和光纤收发器的使用有些疑虑&#xff0c;两者该如何连接&#xff1f;又…

2023年5月实时获取地图边界数据方法,省市区县街道多级联动【附实时geoJson数据下载】

首先&#xff0c;来看下效果图 在线体验地址&#xff1a;https://geojson.hxkj.vip&#xff0c;并提供实时geoJson数据文件下载 可下载的数据包含省级geojson行政边界数据、市级geojson行政边界数据、区/县级geojson行政边界数据、省市区县街道行政编码四级联动数据&#xff0…

第7章链接:重定位、可执行目标文件、加载可执行目标文件

文章目录 7.7 重定位7.7.1 重定位表目7.7.2 重定位符号引用重定位PC相关的引用重定位绝对引用 7.8 可执行目标文件7.9 加载可执行目标文件 7.7 重定位 一旦链接器完成了符号解析这一步&#xff0c;它就把代码中的每个符号引用和确定的一个符号定义&#xff08;也就是&#xff…

自学成材的黑客很多,但还是得掌握方法,给你黑客入门与进阶建议

建议一&#xff1a; 黑客七个等级&#xff08;仅供参考&#xff09; 黑客&#xff0c;对很多人来说充满诱惑力。很多人可以发现这门领域如同任何一门领域&#xff0c;越深入越敬畏&#xff0c;知识如海洋&#xff0c;黑客也存在一些等级&#xff0c;参考知道创宇 CEO ic&#…

如何把图片无损放大?教你图片怎么无损放大

随着数字图像技术的不断发展&#xff0c;图片无损放大成为了许多人关注的问题。当我们需要将小图片放大到更大的尺寸时&#xff0c;使用传统的放大方法可能会导致图片失真、模糊等质量问题。那么如何在保持高清晰度和精度的同时进行无损放大&#xff0c;一直是一个备受关注的课…

数字化时代,如何从战略设计到架构来打造智慧银行?

导语 | 随着人工智能、大数据、云计算等技术向纵深发展&#xff0c;数字化转型已成为银行发展的“必答题”。调整战略规划和架构重组、加大信息科技投入、推进科技人才队伍建设、持续推出数字化产品……近年来&#xff0c;深化数字化转型&#xff0c;以科技赋能金融服务已成为不…

【C++初阶】第十三篇:模板进阶(非类型模板参数、模板的特化以及模板的分离编译)

文章目录 一、非类型模板参数二、模板的特化2.1 概念2.2 函数模板特化2.3 类模板特化2.3.1 全特化2.3.2 偏特化/半特化 三、模板的分离编译3.1 什么是分离编译3.2 模板的分离编译3.3 解决方法 四、模板总结 一、非类型模板参数 模板参数类型可分为&#xff1a;类型形参和非类型…

通达信SCTR强势股选股公式,根据六个技术指标打分

SCTR指标(StockCharts Technical Rank)的思路来源于著名技术分析师约翰墨菲&#xff0c;该指标根据长、中、短三个周期的六个关键技术指标对股票进行打分&#xff0c;根据得分对一组股票进行排名&#xff0c;从而可以识别出强势股。 与其他技术指标一样&#xff0c;SCTR的设计…

中国社科院与美国杜兰大学能源管理硕士项目是你职场通关的密码吗

职场是一场没有硝烟的战争&#xff0c;想要在职场取得取胜&#xff0c;就要拥有超能力。从职场小白晋升到管理层一路走来诸多不易&#xff0c;想要坐稳或升得更高&#xff0c;要不断提升自己能力&#xff0c;要不间断地学习。社科院与美国杜兰大学能源管理硕士项目是你通关的密…

navicat连接oracle报错 ORA-28547

报错 原因 Navicat自带的oci.dll并不支持oracle11g 具体操作 1. 先用idea连接oracle&#xff0c;查看oracle版本 select * from v$version; 2. 去官网下载 Instant Client 地址&#xff1a; Oracle Instant Client Downloads 下载 选择对应的版本&#xff08;下载时&#x…

未注册老域名扫描软件-免费未注册老域名挖掘

未注册老域名挖掘教程 在SEO优化中&#xff0c;老域名的价值不言而喻&#xff0c;它们的搜索引擎权重、离线广告效果等都比新域名更高。然而&#xff0c;如何挖掘出高质量的老域名并进行注册并非易事。今天&#xff0c;我们将介绍一款名为“147SEO老域名挖掘软件”的工具&…

【SpringBoot】二:自动配置

文章目录 1.自动配置类2. Import3. AutoConfigurationImportSelector4. AutoConfiguration 1.自动配置类 Spring Boot的自动装配机制会试图根据你所添加的依赖来自动配置你的Spring应用程序。 例如&#xff0c;如果你添加了Mysql依赖&#xff0c;而且你没有手动配置任何DataS…

从今天起,不再为 API 烦恼 !

做技术管理的童鞋&#xff0c;往往会陷入这样一种困境&#xff1a;疲于奔命&#xff0c;到处救火填坑&#xff0c;沟通推进&#xff0c;却挤不出时间思考对团队和项目来说真正重要的事情。 你有没有经历过这样的场景&#xff1a; 1. 下属老是改了接口但不维护文档&#xff0c;屡…

初探高并发—ExecutorCompletionService

初探高并发—ExecutorCompletionService 为什么要引入高并发 众所周知&#xff0c;程序中的代码是从下往下顺序执行的&#xff0c;当我们需要在一个方法中同时执行多个耗时的任务时所消耗时间就会大于等于这些任务消耗的累加时间。那么有没有一种办法可以让这些耗时的任务同时…

微信小程序入门04-后端脚手架搭建

我们上一篇已经介绍了权限系统的库表搭建&#xff0c;光有表还是不够的&#xff0c;我们还需要有一个后台系统和数据库进行交互。搭建后台的时候既需要选择使用什么语言&#xff0c;也需要选择框架。 框架分为前端框架和后端框架。在第一篇微信开发者工具搭建的时候我们其实前…