地址空间与内存分配

  • 内存地址
    • Linux内存模型
    • brk与sbrk
  • malloc
    • 分配前初始化
    • 内存块的结构
    • 获取内存块
      • 内存对齐
      • 从空闲block获取
      • 拓展新block
  • free
    • 碎片处理
    • 合法性检查
    • 释放内存空间
  • STL内存管理
    • gnu中的八种内存分配器
      • __gnu_cxx::new_allocator
      • __gnu_cxx::__pool_alloc
      • __gnu_cxx::__mt_alloc
      • __gnu_cxx::malloc_allocator
      • __gnu_cxx::debug_allocator
      • __gnu_cxx::throw_allocator
      • __gnu_cxx::array_allocator
      • __gnu_cxx::bitmap_allocator
    • SGI内存管理组件
      • 分配器的使用
      • uninitialized组件
  • loki库的小对象内存管理
    • Chunk
    • FixedAllocator
    • SmallObjAllocator
    • 基于策略的SmallObjAllocator
      • AllocatorSingleton
      • SmallObjectBase

内存地址

Linux内存模型

虚拟内存地址与物理内存地址:用户编程使用的地址称为虚地址或逻辑地址,其对应的存储空间称为虚存空间或逻辑地址空间;而计算机物理内存的访问地址则称为实地址或物理地址,其对应的存储空间称为物理存储空间或主存空间。对于虚拟内存来说,每个进程有各自独立的2^N(N为机器位数)字节的虚拟地址空间。虚拟地址空间的作用主要是简化程序的编写及方便操作系统对进程间内存的隔离管理,虚拟内存到物理内存的映射由操作系统按页动态维护。虚拟内存一方面保护了操作系统的安全,另一方面允许应用程序使用比实际物理内存更大的地址空间。

在这里插入图片描述

Linux 64位系统的内存空间:理论上,64bit内存地址可用空间为0x0000000000000000 ~ 0xFFFFFFFFFFFFFFFF,而Linux64位操作系统仅使用低47位,高17位做扩展(只能是全0或全1)。所以,实际用到的地址为空间为0x0000000000000000 ~ 0x00007FFFFFFFFFFF和0xFFFF800000000000 ~ 0xFFFFFFFFFFFFFFFF,其中前面为用户空间(User Space),后者为内核空间(Kernel Space)。进程虚拟内存空间:虚拟内存地址由MMU(Memory Management Unit)动态转化为物理内存地址对真实数据进行操作。这个空间被划分以下几部分1)code:存放程序的二进制代码段 。2).data:存放常量字符串,初始化的全局变量和静态变量,程序结束后由系统释放。3).bss:未初始化的全局变量和静态变量,程序结束后由系统释放。4)heap:一般由程序员分配释放,若程序员不释放,程序结束时可能由 OS 回收。5)mapping area:这里是与mmap系统调用相关的区域。大多数实际的malloc实现会考虑通过mmap分配较大块的内存区域。6).stack :由编译器自动分配释放,存放函数的参数值,局部变量的值等。

在这里插入图片描述

堆:堆是由三段连续内存组成,分别为开始部分,最大限制,堆顶。堆顶指针指向映射区域的边界,从堆起始地址到break之间的地址空间为映射好的,可以供进程访问;而从break往上,是未映射的地址空间,如果访问这段空间则程序会报错。

brk与sbrk

int brk(void *addr);
void *sbrk(intptr_t increment);
	brk将break指针直接设置为某个地址,而sbrk将break从当前位置移动increment所指定的增量。brk在执行成功时返回0,否则返回-1并设置errno为ENOMEM;sbrk成功时返回break移动之前所指向的地址,否则返回(void *)-1。如果将increment设置为0,则可以获得当前break的地址。由于Linux是按页进行内存映射的,所以如果break被设置为没有按页大小对齐,则系统实际上会在最后映射一个完整的页,从而实际已映射的内存空间比break指向的地方要大一些。

malloc

分配前初始化

#include <unistd.h> /*sbrk 函数所在的头文件 */void malloc_init()
{last_valid_address = sbrk(0); /* 用 sbrk 函数在操作系统中取得最后一个有效地址(当前break的位置) */managed_memory_start = last_valid_address; /* 将 最 后 一 个有效地址作为管理内存的起始地址 */has_initialized = 1; /* 初始化成功标记 */
}

内存块的结构

在这里插入图片描述

内存块的数据结构:所要申请的内存是由多个内存块构成的链表。内存块的大致结构:每个块由meta区和数据区组成,meta区:记录数据块的元信息(数据区大小、空闲标志位、指针等等),数据区是真实分配的内存区域,并且数据区的第一个字节地址即为malloc返回的地址。
typedef struct s_block *t_block;
struct s_block {size_t size; /* 数据区大小 */t_block next; /* 指向下个块的指针 */int free; /* 是否是空闲块 */int padding; /* 填充4字节,保证meta块长度为8的倍数 */char data[1] /* meta区的末尾,这是一个虚拟字段,表示数据块的第一个字节,长度不应计入meta */
};
	关于数据区:1)为了完全地管理内存,需要能够追踪要分配和回收哪些内存。在对内存块进行了 free调用之后,需要做的是诸如将它们标记为未被使用的等事情,并且,在调用 malloc 时,要能够定位未被使用的内存块。因此, malloc 返回的每块内存的起始处首先要有这个结构:		
struct mem_control_block
{	int is_available;//是否空闲int size; //内存块大小
};

获取内存块

内存对齐

	考虑64位情况,指针必须是8的倍数(64位 = 8字节),因为元数据块已经对齐,唯一需要的是对齐数据块的大小。
对齐公式:
(X − 1)/8 × 8 + 8
即:
(((((X)-1)>>3)<<3)+8)

从空闲block获取

block链中的查找算法:1)First fit:从头开始,使用第一个数据区大小大于要求size的块所谓此次分配的块2)Best fit:从头开始,遍历所有块,使用数据区大小大于size且差值最小的块作为此次分配的块。两种方法各有千秋,best fit具有较高的内存使用率(payload较高),而first fit具有更好的运行效率。find_block从frist_block开始,查找第一个符合要求的block并返回block起始地址,如果找不到这返回NULL。这里在遍历时会更新一个叫last的指针,这个指针始终指向当前遍历的block。这是为了如果找不到合适的block而开辟新block使用的。
frist_block查找合适的空闲块:t_block find_block(t_block *last, size_t size) {t_block b = first_block;while(b && !(b->free && b->size >= size)) {*last = b;b = b->next;}return b;
}
	First fit有一个比较致命的缺点,就是可能会让很小的size占据很大的一块block。为了提高payload,会在列表中插入一个新的块,将其分裂为一个新的block:

在这里插入图片描述

分裂block代码:void split_block(t_block b, size_t s) {t_block new;new = b->data + s; //由于字段数据是类型char[],所以总和是按字节精度完成的。new->size = b->size - s - BLOCK_SIZE ;new->next = b->next;new->free = 1;b->size = s;b->next = new;}
	如果现有block都不能满足size的要求,则需要在链表最后开辟一个新的block。

拓展新block

	当开辟的空间小于128K时,调用brk()函数,malloc的底层实现是系统调用函数 brk(),其主要移动break位置。当开辟的空间大于128K时,malloc使用mmap()系统调用函数在虚拟地址空间中(堆和栈中间,称为“文件映射区域”的地方)找一块空间来开辟。。这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射
使用brk拓展block代码:#define BLOCK_SIZE 24 //sizeof(struct s_block)t_block extend_heap(t_block last, size_t s) {t_block b;b = sbrk(0);if(sbrk(BLOCK_SIZE + s) == (void *)-1)return NULL;b->size = s;b->next = NULL;if(last)last->next = b;b->free = 0;return b;
}

free

碎片处理

碎片:malloc一块空间时,为了更好的利用了内存,如果找到了较大的一块空间后会将这块空间分割以供给后续空间使用。于是堆空间有可能存在大量的内存碎片。free函数处理碎片:在free内存时候,通过扫描这块内存的左右内存块将与其相邻的内存碎片和当前free掉的内存块整合在一起,放入空闲列表中。
整合碎片:typedef struct s_block *t_block;struct s_block {size_t size; //数据区域大小t_block next; //指向下一区域的指针t_block prev; // 指向下一区的指针int free; //是否为空闲区域(因为内存对齐所以用int)int padding;  // 填充4字节,保证meta块长度为8的倍数 */char data[1]  // 这是一个虚拟字段,表示数据块的第一个字节,不计入meta的长度
};t_block fusion(t_block b){if (b->next && b->next ->free ){b->size += BLOCK_SIZE + b->next ->size; //更新大小b->next = b->next ->next;    //更改指向if(b->next)b->next->prev = b; }return (b);
}

合法性检查

free前需要对指针的合法性进行检验,以保证这个地址是malloc的地址,有两种方法:
1)在结构体中添加几个magic number。在free之前通过相对偏移量比较是否与设定的magic number相同。
2)在结构体中添加一个指向数据区第一个字节的指针ptr。在free之前通过比较参数指针是否等于其指向结构体的ptr成员。
指针方式合法性检查:#define BLOCK_SIZE 40 //sizeof(struct s_block)typedef struct s_block *t_block;struct s_block {...t_block prev; // 指向下一区的指针void* ptr; //指向data。...
};t_block get_block(void *p) {char *tmp; tmp = p;return (p = tmp -= BLOCK_SIZE);
}int valid_addr(void *p) {if(first_block) {if(p > first_block && p < sbrk(0)) {return p == (get_block(p))->ptr;}}return 0;
}

释放内存空间


void free(void *p) {t_block b;//合法检查if(valid_addr(p)) {b = get_block(p);b->free = 1;//整合前一个if(b->prev && b->prev->free)b = fusion(b->prev);//整合后一个if(b->next)fusion(b);else {//释放堆顶if(b->prev)b->prev->prev = NULL;else //无内存块first_block = NULL;brk(b);}}
}

STL内存管理

gnu中的八种内存分配器

gnu中为管理内存提供了八种内存分配器:

__gnu_cxx::new_allocator

	简单地封装了new和delete操作符,通常就是std::allocator对象的构造分为四步:1)申请内存空间,对应函数是allocator::allocate()2)执行构造函数,对应函数是allocator::construct()3)执行析构函数,对应函数是allocator::destroy()4)释放内存空间,对应函数是allocator::deallocate()使用::operator new和::operator delete来申请和释放内存空间(::operator new和::operator delete实际分配或释放内存调用的malloc和free,加上了异常处理等操作),使用placement new来执行构造,显示调用析构函数来执行析构并提供了多个版本的析构函数以优化
std::allocator代码示意:#include <new>// for new
#include <cstddef> //  size_t
#include <climits> // for unit_max
#include <iostream> // for cerr
using namespace std;namespace SLD {
template <class T>
class allocator
{
public:typedef T		value_type;typedef T*		pointer;typedef const T*	const_pointer;typedef T&		reference;typedef const T&	const_reference;typedef size_t		size_type;typedef ptrdiff_t	difference_type;template <class U>struct rebind{typedef allocator<U> other;};//申请内存pointer allocate(size_type n, const void* hint = 0){T* tmp = (T*)(::operator new((size_t)(n * sizeof(T))));if (!tmp)cerr << "out of memory"<<endl;return tmp;}//释放内存void deallocate(pointer p){::operator delete(p);}//构造void construct(pointer p, const T& value){new(p) T1(value);}//析构函数version1void destroy(pointer p){p->~T();}//析构函数version2,接受两个迭代器。此函式设法找出元素的数值型别,//进而利用 __type_traits<> 求取最适当措施。template <class ForwardIterator>inline void destroy(ForwardIterator first, ForwardIterator last) {__destroy(first, last, value_type(first));}// 判断元素的数值型别(value type)是否有trivial destructortemplate <class ForwardIterator, class T>inline void __destroy(ForwardIterator first, ForwardIterator last, T*){typedef typename __type_traits<T>::has_trivial_destructor trivial_destructor;__destroy_aux(first, last, trivial_destructor());}// 如果元素的数值型别(value type)有non-trivial destructor…template <class ForwardIterator>inline void__destroy_aux(ForwardIterator first, ForwardIterator last, __false_type) {for ( ; first < last; ++first)destroy(&*first);}// 如果元素的数值型别(value type)有 trivial destructor…template <class ForwardIterator>inline void __destroy_aux(ForwardIterator, ForwardIterator, __true_type) {}//析构函数version3,针对迭代器为char*和wchar_t*的特化版inline void destroy(char*, char*) {}inline void destroy(wchar_t*, wchar_t*) {}//取地址pointer address(reference x){return (pointer)&x;}const_pointer const_address(const_reference x){return (const_pointer)&x;}size_type max_size() const {return size_type(UINT_MAX/sizeof(T));}
};
}

在这里插入图片描述

__gnu_cxx::__pool_alloc

	基于内存池,通常就是std::alloc,但std::alloc在更高版本的gnu cxx中已移除。SGI STL中pool alloc分配器以128个字节来区分小块和大块内存,将空间配置器分为两级结构,一级空间配置器处理大块内存,二级空间配置器处理小块内存。一级空间配置器:__malloc_alloc_template一级空间配置器的原理就是直接将malloc和free进行了封装,当调用malloc和realloc申请不到内存空间的时候,会改调用oom_malloc()和oom_realloc(),这两个函数会反复调用用户传递过来的out of memory handler处理函数,直到能用malloc或者realloc申请到内存为止。如果用户没有传递__malloc_alloc_oom_handler,__malloc_alloc_template会抛出__THROW_BAD_ALLOC异常。
一级空间配置器代码示意:static void* allocate(size_t __n)
{void* __result = malloc(__n);if (0 == __result)__result = _S_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 = _S_oom_realloc(__p, __new_sz);return __result;
}
二级空间配置器:__default_alloc_template二级空间配置器专门负责处理小于128字节的小块内存。SGI STL采用了内存池的技术来提高申请空间的速度以及减少额外空间的浪费,采用哈希桶的方式来提高用户获取空间的速度。为何是128Kb:malloc函数以128Kb为区分,对于大于128Kb的空间,由映射取分配,可直接释放;对于小于128Kb的空间,靠着移动堆顶指针来分配,并用链表管理,不能随时释放,会出现内存碎片。一级分配器是在申请的空间大于128Kb的情况下对malloc的封装,所获取的内存属于映射区,能随时申请和释放,不会产生碎片;二级分配器申请的空间小于128Kb,属于堆区,使用malloc容易产生碎片,因而设计内存池自己管理。

在这里插入图片描述

	二级分配器中,存在16条空闲链表,每条链表中的空闲块的大小都是固定的:第一条链表的空闲块大小是8bytes, 往后依次是16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 128bytes。16条空闲链表的起始地址组成大小为16的数组。另外存在一个内存池,内存池的维护需要三根指针:s_start_free,s_end_free,s_heap_size。它们分别指向整个内存池的起始地址,结束地址和可用空间大小。

在这里插入图片描述

申请空间:1)向上对齐字节数后,所需字节数大于128由一级空间配置器来处理,不大于由二级空间配置器处理。2)对于二级配置器,先找到对应的桶,检查桶下是否存在内存块,存在直接将第一个内存块返回给用户3)不存在则要先向桶中添加内存块,然后将第一个内存块返回给用户

在这里插入图片描述

填充内存块:1)一次性向内存池索取20个内存块(chunk),真实返回的内存块不小于12)只有一块就直接返回给用户,否则将第一块返回给用户,剩余的挂在对应的桶中

在这里插入图片描述

向内存池索要空间:1)计算20个内存块总大小和内存池剩余空间大小,能提供20块切割空间则返回2)不能提供20块,能否提供至少一块,能提供切割空间则返回3)一块也不能提供,说明内存池空间不足,此时将内存池剩余空间挂到对应桶下并补充内存池4)通过系统堆补充内存池,补充成功则重新回到第一步5)补充失败,从哈希桶中找更大的内存块补充,补充成功重新回到第一步6)哈希桶也补充失败,向一级空间配置器申请补充

在这里插入图片描述

二级空间配置器代码示意:template <bool threads, int inst>
class __default_alloc_template {private:// Really we should use static const int x = N// instead of enum { x = N }, but few compilers accept the former.enum {_ALIGN = 8};//小块区域的上界enum {_MAX_BYTES = 128};//小块区域的下降enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN,有多少个区域/*SGI 为了方便内存管理, 把128B 分成16*8 的块*///将Byte调到8的倍数static size_t_S_round_up(size_t __bytes) { return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }//管理内存的链表union _Obj {union _Obj* _M_free_list_link;char _M_client_data[1];    /* The client sees this.        */};//声明了16个 free_list, 注意 _S_free_list是成员变量static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS];//同了第几个free_list, 即_S_free_list[n],当然这里是更具区域大小来计算的static  size_t _S_freelist_index(size_t __bytes) {return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);}// Returns an object of size __n, and optionally adds to size __n free list.static void* _S_refill(size_t __n);// Allocates a chunk for nobjs of size size.  nobjs may be reduced// if it is inconvenient to allocate the requested number.static char* _S_chunk_alloc(size_t __size, int& __nobjs);// Chunk allocation state.static char* _S_start_free;//内存池的起始位置static char* _S_end_free;//内存池的结束位置static size_t _S_heap_size;//堆的大小public://分配内存/* __n must be > 0      */static void* allocate(size_t __n);//释放内存/* __p may not be 0 */static void deallocate(void* __p, size_t __n);//重新分配内存static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz);}//下面是一些 成员函数的初始值的设定
template <bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_start_free = 0;template <bool __threads, int __inst>
char* __default_alloc_template<__threads, __inst>::_S_end_free = 0;template <bool __threads, int __inst>
size_t __default_alloc_template<__threads, __inst>::_S_heap_size = 0;template <bool __threads, int __inst>
typename __default_alloc_template<__threads, __inst>::_Obj* __STL_VOLATILE
__default_alloc_template<__threads, __inst> ::_S_free_list[] = 
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, };
static void* allocate(size_t __n)
{void* __ret = 0;if (__n > (size_t) _MAX_BYTES) {//如果大于128B, 直接调用一级配置器__ret = malloc_alloc::allocate(__n);}else {//找出 16个free-list 中的一个_Obj* __STL_VOLATILE* __my_free_list= _S_free_list + _S_freelist_index(__n);_Obj* __RESTRICT __result = *__my_free_list;if (__result == 0)//如果满了,则我refill整一个链表__ret = _S_refill(_S_round_up(__n));else {*__my_free_list = __result -> _M_free_list_link;__ret = __result;}}return __ret;
};static void deallocate(void* __p, size_t __n)
{if (__n > (size_t) _MAX_BYTES)//如果大于128,直接释放malloc_alloc::deallocate(__p, __n);else {//找到对应的链表_Obj* __STL_VOLATILE*  __my_free_list= _S_free_list + _S_freelist_index(__n);_Obj* __q = (_Obj*)__p;//回收,该链表__q -> _M_free_list_link = *__my_free_list;*__my_free_list = __q;// lock is released here}
}template <bool __threads, int __inst>
char*
__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size, int& __nobjs)
{char* __result;size_t __total_bytes = __size * __nobjs;//申请的总内存空间size_t __bytes_left = _S_end_free - _S_start_free;//内存池剩余的内存空间if (__bytes_left >= __total_bytes) {//如果你能满足我__result = _S_start_free;_S_start_free += __total_bytes;return(__result);} else if (__bytes_left >= __size) {//如果能满足我一块或一块以上,参考__Obj这个联合体(free_list)__nobjs = (int)(__bytes_left/__size);__total_bytes = __size * __nobjs;__result = _S_start_free;_S_start_free += __total_bytes;return(__result);} else {//如果连一块都给不出size_t __bytes_to_get = 2 * __total_bytes + _S_round_up(_S_heap_size >> 4);// Try to make use of the left-over piece.if (__bytes_left > 0) {_Obj* __STL_VOLATILE* __my_free_list =_S_free_list + _S_freelist_index(__bytes_left);((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;*__my_free_list = (_Obj*)_S_start_free;}.//从堆空间重新分配内存_S_start_free = (char*)malloc(__bytes_to_get);if (0 == _S_start_free) {//连堆都没有内存了size_t __i;_Obj* __STL_VOLATILE* __my_free_list;_Obj* __p;for (__i = __size;__i <= (size_t) _MAX_BYTES;__i += (size_t) _ALIGN) {__my_free_list = _S_free_list + _S_freelist_index(__i);__p = *__my_free_list;if (0 != __p) {*__my_free_list = __p -> _M_free_list_link;_S_start_free = (char*)__p;_S_end_free = _S_start_free + __i;return(_S_chunk_alloc(__size, __nobjs));}}_S_end_free = 0;	// In case of exception.//调用一级配置器,主要是为了调用_S_oom_malloc压榨出内存来_S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);// This should either throw an// exception or remedy the situation.  Thus we assume it// succeeded.}//更改一下内存池_S_heap_size += __bytes_to_get;_S_end_free = _S_start_free + __bytes_to_get;return(_S_chunk_alloc(__size, __nobjs));}
}

__gnu_cxx::__mt_alloc

	对多线程环境进行了优化

支持多线程的内存分配器__gnu_cxx::__mt_alloc

__gnu_cxx::malloc_allocator

	简单地封装了malloc和free函数

__gnu_cxx::debug_allocator

__gnu_cxx::throw_allocator

__gnu_cxx::array_allocator

__gnu_cxx::bitmap_allocator

gcc-4.6.3/ibstdc++

SGI内存管理组件

分配器的使用

	对于STL容器,包含相应头文件后,只需将分配器作为模板参数传入即可,如vector<int, __gnu_cxx::__pool_alloc<int>> vec。

uninitialized组件

template <class _InputIter, class _ForwardIter>
inline _ForwardIter
uninitialized_copy(_InputIter __first, _InputIter __last,_ForwardIter __result)
{return __uninitialized_copy(__first, __last, __result,__VALUE_TYPE(__result));
}
	uninitialized_copy()会将迭代器_first和_last之间的对象拷贝到迭代器_result开始的地方。它调用的__uninitialized_copy(__first, __last, __result,__VALUE_TYPE(__result)),会判断迭代器_result所指的对象是否是POD类型,如果是POD类型,则调用算法库的copy实现;否则遍历迭代器_first~_last之间的元素,在_result起始地址处一一构造新的元素。uninitialized_fill()和uninitialized_fill_n()同样如此。
template <class _ForwardIter, class _Tp>
inline void uninitialized_fill(_ForwardIter __first,_ForwardIter __last, const _Tp& __x)
{__uninitialized_fill(__first, __last, __x, __VALUE_TYPE(__first));
}template <class _ForwardIter, class _Size, class _Tp>
inline _ForwardIter 
uninitialized_fill_n(_ForwardIter __first, _Size __n, const _Tp& __x)
{return __uninitialized_fill_n(__first, __n, __x, __VALUE_TYPE(__first));
}

loki库的小对象内存管理

在这里插入图片描述

	Loki Allocator中有三个类,对比pool allocator,它的优点就在于它会把内存还给系统:1)Chunk2)FixedAllocator3)SmallObjAllocator

Chunk

Chunk是一段由许多个固定大小内存块组成的连续的空间,内部调用malloc函数分配空间。在Chunk初始化时,每个内存块的大小和Chunk中的内存块个数都由传入参数确定。,内存块个数不超过256个。Chunk中内存块的管理方式为数组模拟的链表。

在这里插入图片描述

	Chunk管理内存块需要用到三个变量		
// 指向内存块
unsigned char * pData_;
// 目前可用区块的第一块的索引
unsigned char firstAvailableBlock_;
// 有多少块可用
unsigned char blocksAvailable_;
 		Chunk初始化:分配内存块并用数组的方式模拟链表,将内存块串接(将每个小区块的第一个字节设置为索引排好号)
bool Chunk::Init( ::std::size_t blockSize, unsigned char blocks ) {const ::std::size_t allocSize = blockSize * blocks;pData_ = static_cast< unsigned char * >( ::operator new ( allocSize ) );Reset( blockSize, blocks );return true;
}void Chunk::Reset(::std::size_t blockSize, unsigned char blocks) {firstAvailableBlock_ = 0;blocksAvailable_ = blocks;// 这把每个区块的前一个字节 来记录索引unsigned char i = 0;for ( unsigned char * p = pData_; i != blocks; p += blockSize ) {*p = ++i;}
}
	Chunk分配:1)从 当前可用区块的第一块索引(firstAvailableBlock_所指)中取得当前可用区块并返回2)被返回的可用区块中的索引被设置到当前可用区块索引去(这样下一次就会使用到它)3)可用区块数目(blocksAvailable_所指)减1
void* Chunk::Allocate(::std::size_t blockSize) {if ( 0 == blocksAvailable_ ) {return nullptr;}// 1. 取得目前可用的第一块unsigned char * pResult = pData_ + (firstAvailableBlock_ * blockSize);firstAvailableBlock_ = *pResult;// 2. 当前可用区块 - 1--blocksAvailable_;// 3. 将目前可用第一块返回回去return pResult;
}
	Chunk回收:原则:回收回来的区块,下次分配优先分配1)计算回收回来的区块的索引idx2)当前可用区块的索引号(firstAvailableBlock_ 所指)设置为idx3)之前可用区块的索引号(firstAvailableBlock_ 的旧值)设置到回收回来的区块包含的索引中去4)当前可用区块(blocksAvailable_)加1
void Chunk::Deallocate(void* p, ::std::size_t blockSize) {unsigned char* toRelease = static_cast<unsigned char*>(p);// 找这个指针在区块中的索引unsigned char index = static_cast< unsigned char >(( toRelease - pData_ ) / blockSize);// 回收回来以后下次优先用它// 1. 回收的区块里的索引设置为当前可用区块的索引*toRelease = firstAvailableBlock_;// 2. 当前的第一块可用索引修改为回收回来的区块应该放置的位置firstAvailableBlock_ = index;// 3. 可用数 + 1++blocksAvailable_;
}
	Chunk的释放:Chunk等到所有区块被回收时,内存将被释放,这件事由Chunk的管理者FixedAllocator负责
void Chunk::Release() {::operator delete ( pData_ );
}

FixedAllocator

	FixedAllocator的成员:FixedAllocator拥有一个Chunk的数组,并使用了三个指针来管理它
// vector<Chunk>
typedef ::std::vector< Chunk > Chunks;
Chunks chunks_;// 指向 vector<Chunk> 其中某两个 Chunk
// allocChunk_ 指向上次分配区块的 Chunk
Chunk * allocChunk_;
// deallocChunk_ 指向上次回收区块的 Chunk
Chunk * deallocChunk_;// 指向唯一一个空Chunk 如果没有则为 nullptr
Chunk * emptyChunk_;
	FixedAllocator分配:1)看allocChunk_(指向上次分配过区块的Chunk) 是否有效,有效则直接分配2)否则看emptyChunk_(指向一个空Chunk) 是否有效,有效则直接分配3)否则依次遍历vector找到其中一个能分配区块的Chunk4)找不到则创建一个新的Chunk并将其push_back到vector中
void * FixedAllocator::Allocate( void ) {// 上次分配出去过区块的chunk 如果为空或者是该chunk可用区块为0// 则将空chunk赋值给它// 如果没有空chunk  if ( ( nullptr == allocChunk_ ) || 0 == allocChunk_->blocksAvailable_ ) {if ( nullptr != emptyChunk_ ) {allocChunk_ = emptyChunk_;emptyChunk_ = nullptr;} else {// 都没有则从头找:遍历一遍 vectorChunks::iterator i = chunks_.begin();for ( ; ; ++i ) {// 到达尾部还是没找着就创建一个临时对象并init这个chunk// 然后将其push到容器中if ( chunks_.end() == i ) {chunks_.push_back(Chunk());Chunk &newChunk = chunks_.back();newChunk.Init(blockSize_, numBlocks_);// 记录好,下次就从这里面去取区块allocChunk_ = &newChunk;// vector push_back的时候有可能发生成长// vector 成长是两倍申请内存然后将其拷贝过去// 如果不重新取迭代器地址的话有可能该迭代器失效deallocChunk_ = &chunks_.front();break;}// 如果找到一个chunk 的可用区块大于 0 则用它来分配if ( i->blocksAvailable_ > 0) {allocChunk_ = &*i;break;}}}// 如果上次分配区块的chunk是emptyChunk 则将 emptyChunk 置为空// 因为 emptyChunk 已经被用了} else if ( allocChunk_ == emptyChunk_) {emptyChunk_ = nullptr;}// 上次分配过区块的chunk 如果还能分配则直接分配返回回去void * place = allocChunk_->Allocate( blockSize_ );return place;
}
	FixedAllocator回收:1)寻找要回收的区块在哪个Chunk中:a. 先从上一次回收过区块的Chunk中查看b. 再从上一次分配过区块的Chunk中查看c. 如果都没有则用一种特殊的搜索方法:从上一次回收过区块的Chunk作为临界点,每次循环一次向上找、一次向下找,直到找到2)调用释放函数:如果发生了全回收,则看是否已经有一块全回收了,两块全回收时才释放其中一块,避免突然又要用到那块Chunk
bool FixedAllocator::Deallocate( void * p ) {Chunk * foundChunk = nullptr;const ::std::size_t chunkLength = numBlocks_ * blockSize_;// 先看看是否在之前回收过区块的 Chunk里面// 再看看是否在之前分配过区块的 Chunk里面if ( p >= deallocChunk_->pData_ && p < deallocChunk_->pData_ + chunkLength ) {foundChunk = deallocChunk_;} else if ( p >= allocChunk_->pData_ && p < allocChunk_->pData_ + chunkLength ) {foundChunk = allocChunk_;} else {// 实在找不到再去搜索foundChunk = VicinityFind( p );}if ( nullptr == foundChunk ) {return false;}deallocChunk_ = foundChunk;// 此时再真正的去deallocateDoDeallocate(p);return true;
}Chunk * FixedAllocator::VicinityFind( void * p ) const {if ( chunks_.empty() ) {return nullptr;}const ::std::size_t chunkLength = numBlocks_ * blockSize_;// lo指向上一次回收过区块的 Chunk// hi指向上一次回收过区块的 Chunk的下一个Chunk * lo = deallocChunk_;Chunk * hi = deallocChunk_ + 1;const Chunk * loBound = &chunks_.front();const Chunk * hiBound = &chunks_.back() + 1;if ( hi == hiBound ) {hi = nullptr;}for (;;) {// lo 往上走,hi 往下走,直到找到,或者,触碰到边界if (lo) {if ( p >= lo->pData_ && p < lo->pData_ + chunkLength ) {return lo;}if ( lo == loBound ) {lo = nullptr;if ( nullptr == hi ) {break;}} else { --lo;}}if (hi) {if ( p >= hi->pData_ && p < hi->pData_ + chunkLength ) {return hi;}if ( ++hi == hiBound ) {hi = nullptr;if ( nullptr == lo ) {break;}}}}return nullptr;
}// 总得来说就是 当有2个全回收的Chunk才释放掉一个
void FixedAllocator::DoDeallocate(void* p) {// 让 Chunk 回收掉这个区块deallocChunk_->Deallocate(p, blockSize_);// 如果这个Chunk 已经全回收了if ( deallocChunk_->blocksAvailable_ == numBlocks_ ) {if ( nullptr != emptyChunk_ ) {Chunk * lastChunk = &chunks_.back();if ( lastChunk == deallocChunk_ ) {deallocChunk_ = emptyChunk_;} else if ( lastChunk != emptyChunk_ ) {::std::swap( *emptyChunk_, *lastChunk );}// 最后释放掉内存lastChunk->Release();chunks_.pop_back();// 修正 allocChunk 指针 防止 被释放掉的是 allocChunkif ( ( allocChunk_ == lastChunk ) || allocChunk_->blocksAvailable_ == 0 ) {allocChunk_ = deallocChunk_;}}emptyChunk_ = deallocChunk_;}
}

SmallObjAllocator

	SmallObjAllocator维护一个固定长度的FixedAllocator数组,初始化的时候确定。维护着三个成员变量:
// vector<FixedAllocator> 其实是个数组 你可以理解成 vector
::Loki::Private::FixedAllocator * pool_;
// 支持分配的最大内存大小
const ::std::size_t maxSmallObjectSize_;
// 用于对齐操作
const std::size_t objectAlignSize_;//底层由多个([maxobjectSize/objectAlignSize]个)FixedAllocator 组成,每个FixedAllocator内存大小最大为pagesize,
//FixedAllocator 的一块Chunk由numBlock个blockSize组成,其中blocksize=(i+1)*alignSize, numBlock=pagesize/blockSize
//numBlock <= UCHAR_MAX,不同下标的FixedAllocator分配的block不一样,下标i的FixedAllocator 分配的block大小为(i+1)*alignSize
SmallObjAllocator::SmallObjAllocator( std::size_t pageSize,std::size_t maxObjectSize, std::size_t objectAlignSize ) :pool_( NULL ),maxSmallObjectSize_( maxObjectSize ),objectAlignSize_( objectAlignSize )
{assert( 0 != objectAlignSize );const std::size_t allocCount = GetOffset( maxObjectSize, objectAlignSize );//GetOffset向上取整pool_ = new FixedAllocator[ allocCount ];for ( std::size_t i = 0; i < allocCount; ++i )pool_[ i ].Initialize( ( i+1 ) * objectAlignSize, pageSize );
}
	SmallObjAllocator分配从vector找合适的FixedAllocator然后再用底层的FixedAllocator去找Chunk分配内存
void * SmallObjAllocator::Allocate( std::size_t numBytes, bool doThrow )
{//超过MaxObjSize,调用C语言的malloc或者C++的newif ( numBytes > GetMaxObjectSize() )return DefaultAllocator( numBytes, doThrow );if ( 0 == numBytes ) numBytes = 1;const std::size_t index = GetOffset( numBytes, GetAlignment() ) - 1; // 得到对应pool中的小标,对应的FiexAllocator固定分配的内存大小刚好满足numBytesconst std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );(void) allocCount;assert( index < allocCount );FixedAllocator & allocator = pool_[ index ];assert( allocator.BlockSize() >= numBytes );assert( allocator.BlockSize() < numBytes + GetAlignment() );void * place = allocator.Allocate();//内存不足的情况出现,因为底层的chunk可能有空的chunk,所以调用TrimExcessMemory,尝试释放pool中每个FixedAllocator下chunks可能存在的emptyChunk,尽可能的把用户态内存先归还给操作系统,然后在分配给用户态if ( ( NULL == place ) && TrimExcessMemory() ) place = allocator.Allocate();if ( ( NULL == place ) && doThrow ) //如果还不行的话,没救了,看看要不要抛出异常,否则返回NULL{
#ifdef _MSC_VERthrow std::bad_alloc( "could not allocate small object" );
#else// GCC did not like a literal string passed to std::bad_alloc.// so just throw the default-constructed exception.throw std::bad_alloc();
#endif}return place;
}
	SmallObjAllocator回收找回收的内存对应的FixedAllocator然后调用下一层的FixedAllocator去进行真实回收操作
void SmallObjAllocator::Deallocate( void * p )
{if ( NULL == p ) return;assert( NULL != pool_ );FixedAllocator * pAllocator = NULL;const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );Chunk * chunk = NULL;for ( std::size_t ii = 0; ii < allocCount; ++ii ){chunk = pool_[ ii ].HasBlock( p ); //遍历pool,查看该块内存是否属于对应的FixedAllocatorif ( NULL != chunk ){pAllocator = &pool_[ ii ];break;}}if ( NULL == pAllocator ){DefaultDeallocator( p );return;}assert( NULL != chunk );const bool found = pAllocator->Deallocate( p, chunk );(void) found;assert( found );
}

基于策略的SmallObjAllocator

	loki库是基于策略的,也就是说通过实现一个模板框架,通过传入算法策略(模板参数)能够改变框架内部的过程,通过不同策略的组合能够实现不同功能

AllocatorSingleton

	继承自SmallObjAllocator,AllocatorSingleton,有多个模板参数,主要的策略算法有:用于管理自己单件模式的生命期策略,线程同步策略
template<template <class, class> class ThreadingModel = LOKI_DEFAULT_THREADING_NO_OBJ_LEVEL,std::size_t chunkSize = LOKI_DEFAULT_CHUNK_SIZE,std::size_t maxSmallObjectSize = LOKI_MAX_SMALL_OBJECT_SIZE,std::size_t objectAlignSize = LOKI_DEFAULT_OBJECT_ALIGNMENT,template <class> class LifetimePolicy = LOKI_DEFAULT_SMALLOBJ_LIFETIME,class MutexPolicy = LOKI_DEFAULT_MUTEX>class AllocatorSingleton : public SmallObjAllocator{...// 单例模式inline static AllocatorSingleton & Instance( void ){return MyAllocatorSingleton::Instance();}}

SmallObjectBase

	该类提供线程锁操作,重载了new与delete,属于应用层提供给用户使用,内存的申请与释放都是通过单例AllocatorSingleton.allocate来操作。实际使用的SmallObject仅仅是继承自SmallObjectBase
template
<template <class, class> class ThreadingModel,std::size_t chunkSize,std::size_t maxSmallObjectSize,std::size_t objectAlignSize,template <class> class LifetimePolicy,class MutexPolicy
>
class SmallObjectBase{//....typedef AllocatorSingleton< ThreadingModel, chunkSize,maxSmallObjectSize, objectAlignSize, LifetimePolicy > ObjAllocatorSingleton;typedef typename ObjAllocatorSingleton::MyAllocatorSingleton MyAllocatorSingleton;// 重载new操作符static void * operator new ( std::size_t size ) throw ( std::bad_alloc ){typename MyThreadingModel::Lock lock;(void)lock; // get rid of warningreturn MyAllocatorSingleton::Instance().Allocate( size, true );}//....
}
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. jQuery动态生成页面元素.append,.html,html元素变成字符串

    我在想在第二个div元素中添加一个返回按钮,却变成了一个字符串 $(".c-container")[2].append("<a>返回</a>")改进后代码 $(".c-container").eq(2).append("<a>返回</a>")...

    2024/4/24 11:52:34
  2. DuplicateKeyException

    这里写自定义目录标题Error updating database. Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Duplicate entry 13890739042 for key inside_user_tel重复键异常: “inside_user_tel”的重复条目“13890739042” #org.springframe…...

    2024/4/24 11:52:30
  3. 第四章 Schema与数据类型优化

    良好的逻辑设计和物理设计是高性能的基石,应该根据系统将要执行的查询语句来设计 schema,这往往需要权衡各种因素。 例如,反范式的设计可以加快某些类型的查询,但同 时可能使另一些类型的査询变慢。 比如添加计数表和汇总表是一种很好的优化査询的方式, 但这些表的维护成本…...

    2024/4/24 11:52:29
  4. react笔记(二)

    常用语法基础 列表渲染 类似vue中的v-for,react中也有类似的列表渲染方法。首先我们知道,在jsx中标签即是变量,因此我们可以创建一个标签数组。可以借助map等函数,实现值到标签的映射。 giftMsg=["123","345","567"]; const giftMsgs = gift…...

    2024/4/24 11:52:31
  5. 检测服务器开放端口

    1.使用nmap检测开放端口,可检测远程服务器[root@localhost ~]# nmap localhostStarting Nmap 6.40 ( http://nmap.org ) at 2020-06-09 01:40 CST Nmap scan report for localhost (127.0.0.1) Host is up (0.000023s latency). Other addresses for localhost (not scanned):…...

    2024/4/24 11:52:28
  6. 一篇文章理解线性代数中的标量、向量、矩阵和张量

    经过之前的一些积累,终于有勇气开始进军机器学习了!说实话,机器学习 这个概念是我入行的最纯粹的原因,包括大学选专业、学习 Python 语言…这些有时间仔细梳理下经历再写,总之这个系列的文章就是我自学 机器学习 的笔记,各位看看就好,希望能为一些想入门但无从下手的小伙…...

    2024/4/24 11:52:26
  7. SpringBoot(1.1.12)旧版本 + jdk 1.6

    因客户需求需要在jdk6上搭建springboot项目 支持1.6的SpringBoot项目的版本需要早于1.2,我这次选用的是 旧版本链接传送门 接下来创建项目于是尝试创建一个maven 项目创建好之后引入pom 如下 <?xml version="1.0" encoding="UTF-8"?> <project…...

    2024/4/24 11:52:28
  8. Python之数据库操作

    Python 标准数据库接口为 Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口。 Python DB-API使用流程: 引入 API 模块 获取与数据库的连接 执行SQL语句和存储过程 关闭数据库连接 文章目录MySQLdb创建数据库及表创建数据库:创建数据库表:修改数据库…...

    2024/4/24 11:52:24
  9. 红黑树的理解

    红黑树的理解原文链接...

    2024/4/15 4:53:54
  10. LeetCode 105. 从前序与中序遍历序列构造二叉树(各种遍历二叉树的性质,递归建树)

    这道题算是二叉树里面的基础题、经典问题了。 解决这道题,需要知道二叉树的递归定义。二叉树的前序遍历、中序遍历的性质,并用这个性质建树。 注:必须要有中序遍历。 编号的数字不能重复。/*** Definition for a binary tree node.* struct TreeNode {* int val;* T…...

    2024/4/15 4:53:54
  11. Netty(六)—关于Pipeline

    Netty之Pipeline不管时Netty客户端和服务端,都出现了Pipeline的身影 从之前的学习可以大致了解到在Netty中每个Channel都有且仅有一个ChannelPipeline与之对应官方解释ChannelPipeline注释,从注释中对ChannelPipeline有了进一步的认识在ChannelPipeline中存放了ChannelHandle…...

    2024/4/15 4:53:51
  12. Flutter技术点-showModalBottomSheet关闭

    由于要使用到底部弹出的选择器,在flutter中可以直接使用showModalBottomSheet来自定义底部选择器代码如下:void _showGenderPanel(context) {showModalBottomSheet(context: context,builder: (context) {return Container(height: 120,child: Column(mainAxisAlignment: Mai…...

    2024/4/15 4:53:50
  13. 马澳踢-modal

    文章目录Multi-modal Probablistic 室内定位 on a Smartphone摘I. INTRODUCTION2. RELATED WORK Multi-modal Probablistic 室内定位 on a Smartphone 摘(GPS)robust loc on smartphone outdoorindoor, no systemto achieving a similar level of ubiquitya multi-modal positi…...

    2024/4/27 7:41:44
  14. JAVA 泛型擦除法

    Java泛型的引入加强了参数类型的安全性,减少了类型的转换。Java 的泛型在编译器有效,在运行期被删除,也就是说所有泛型参数类型在编译后都会被清除掉。Java泛型采用的是擦除法实现的伪泛型,泛型信息(类型变量、参数化类型)编译之后通通被除掉了。使用擦除法的好处就是实现…...

    2024/4/20 10:22:52
  15. XAMPP的下载安装配置教程

    话不相瞒,当初为了在XAMPP(Apache+MySQL+PHP+PERL)里面配置phpwind,我在卸载与安装之间来回了不下10次,但是我的phpwind始终还是安装不成功,我可能是招坑体质,真的很无奈。最终,我放弃了phpwind。 废话不说,如大标题所示,直接干了。 文章目录一、下载(三步)第一步:…...

    2024/5/8 0:07:02
  16. oracle游标的使用

    01 概述 不论是Oracle或是SQL Server,只要是关系型数据库,都不可避免地会涉及游标的使用。所谓游标是指一种能从包括多条数据记录的结果集中每次提取一条记录的机制。简单地说,游标提供了一种在服务器内部处理结果集的方法,它可以识别一个数据集合内部指定的工作行,从而可…...

    2024/5/7 13:57:30
  17. 小白日记[Mysql与Java精度损失]

    小白一枚,请多指教小白今日任务:解决精度损失BUG1、Mysql中的精度损失1)数据准备CREATE TABLE `demo` (`f1` float(9,2) DEFAULT NULL,`f2` float(9,2) DEFAULT NULL,`d1` decimal(9,2) DEFAULT NULL,`d2` decimal(9,2) DEFAULT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8; …...

    2024/4/24 11:52:30
  18. 在HTML开发中由于文本过多使得生成#text节点,想要换行却又不知如何下手者入内

    环境由于文本过多,变成了文本节本。难受的点在于它不换行。 我是希望他能最多两行显示,在末尾上添加省略号的。 解决方案 最终在同事的提示下,我发现了是white-space的默认值的问题 white-space 属性设置如何处理元素内的空白。 这个属性声明建立布局过程中如何处理元素中的…...

    2024/5/8 10:02:23
  19. java 发送 Http 请求笔记 以及踩得坑

    java 发送 Http 请求笔记 http 代码 这几天使用 java 代码发送 http请求, 真的是搞得我焦头烂额. 在网上找了几十篇文档, 几乎都大同小异. 首先是网络上最通用的代码public static void callUrl(@NonNull String json, @NonNull String url, Charset charset) throws IOExcepti…...

    2024/5/8 9:31:41
  20. org.json.JSONException: End of input at character 0 of

    response.body().string()这个方法只能调用一次,赋值给一个变量后使用 String str=response.body().string(); 使用str去解析...

    2024/4/24 11:52:23

最新文章

  1. ansible——INVENTORY主机清单

    一、Inventory主机清单 Inventory支持对主机进行分组&#xff0c;每个组内可以定义多个主机&#xff0c;每个主机都可以定义在任何一个或多个主机组内 二、Inventory主机清单部署 2.1 前期准备 systemctl stop firewalld setenforce 0 yum install epel-release -y yum install…...

    2024/5/8 11:28:40
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/7 10:36:02
  3. 6.9物联网RK3399项目开发实录-驱动开发之PWM的使用(wulianjishu666)

    嵌入式实战开发例程&#xff0c;珍贵资料&#xff0c;开发必备&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1149x7q_Yg6Zb3HN6gBBAVA?pwdhs8b PWM 使用 前言 AIO-3399J 开发板上有 4 路 PWM 输出&#xff0c;分别为 PWM0 ~ PWM3&#xff0c;4 路 PWM 分别使用在…...

    2024/5/7 3:24:58
  4. ntp服务器搭建

    1、手动修改时区 CST可以为如下4个不同的时区的缩写&#xff1a; 美国中部时间&#xff1a;Central Standard Time (USA) UT-6:00 澳大利亚中部时间&#xff1a;Central Standard Time (Australia) UT9:30 中国标准时间&#xff1a;China Standard Time UT8:00 古巴标准时间&a…...

    2024/5/7 8:17:20
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/8 6:01:22
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/7 9:45:25
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/7 14:25:14
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/7 11:36:39
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/4 23:55:17
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/7 9:26:26
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/4 23:55:06
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/4 23:55:16
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/6 21:42:42
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/4 23:54:56
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57