什么是持久redis内存持久化:持久redis内存持久化编程系列

内存数据库是怎么保证数据持久性的? - 知乎38被浏览<strong class="NumberBoard-itemValue" title="分享邀请回答41 条评论分享收藏感谢收起2添加评论分享收藏感谢收起写回答《windows核心编程系列》谈谈内存映射文件
& 内存映射文件允许开发人员预订一块地址空间并为该区域调拨物理存储器,与虚拟内存不同的是,内存映射文件的物理存储器来自磁盘中的文件,而非的页交换文件。将文件映射到内存中后,我们就可以在内存中操作他们了,就像他们被载入内存中一样。
内存映射文件主要有三方面的用途:
1:系统使用内存映射文件来将exe或是dll文件本身作为后备存储器,而非系统页交换文件,这大大节省了系统页交换空间,由于不需要将exe或是dll文件加载到页系统交换文件,也提高了启动速度。
2:使用内存映射文件来将磁盘上的文件映射到进程的空间区域,使得开发人员操作文件就像操作内存数据一样,将对文件的操作交由操作系统来管理,简化了开发人员的工作。
3:windows提供了多种进程间通信的方法,但他们都是基于内存映射文件来实现的。
这里首先讨论第一种情况:
&&&&& 在一个exe文件运行之前,系统首先为新进程创建一个进程内核对象,同时预订一块足够大的地址空间来容纳该文件。然后,对该地址空间进行标注,注明他的后备存储器来自exe文件,而非系统的页交换文件。此措施对提高系统性能有重大意义。
&&&&&&&& 一个可执行文件,当他有多个实例同时运行,系统在创建另一个新的实例时,仅仅是打开了另一个内存映射视图。所有这些视图都来自于同一个文件映射对象(即可执行文件本身)。
当新实例开始运行时,系统只是把包含应用程序代码和数据的虚拟内存页面映射到了他的地址空间中,当其中的一个实例试图去修改数据段中的数据,如果不采取有效措施,那么应用程序的所有其他实例的内存都会被修改,这是不合常理的。因此windows采取了一种叫做写时复制的特性,来防止这种情况的发生。
&&&&&&& 系统将可执行文件映射到地址空间中时,会计算有多少页面是可写的。(通常包含数据的页面被标记为PAGE_READWRITE属性,它们是可写的)然后会从系统的页交换文件中调拨物理存储器,来容纳这些可写的页面。但是系统只是调拨这些页面,并不会实际载入页面的内容,只有当写入可写页面的时候才会真正实际载入。(后面会详细介绍)
&&&&&& 任何时候当应用程序试图写入内存映射文件的时候,系统会截获此类尝试,接着从先前在系统页交换文件中分配的空间中取出一页,复制要写入页面的内容,让应用程序写入刚刚从系统页交换文件中分配的页,而不是内存映射文件中的页。由于写入到的区域仅仅是内存映射文件的副本,不会对内存映射文件写入,这样就保证了其他实例不会受到任何影响。另外需要注意的是,内存映射文件的副本(在系统页交换文件中)被映射到了新实例的地址空间区域的同一位置。
&&&&&&& 以上介绍的是在同一个可执行文件的多个实例之间不会共享数据的情况。有时候在多个实例之间共享数据非常有用,可以大大提高效率。接着我们就讨论如何在一个可执行文件的多个实例中共享数据。
&&&&& 我们知道默认情况下,我们定义的初始化数据被放到了数据段,未初始化的数据放到了.bss段。除了使用这些标准段之外,我们也可以将数据放在我们自己的段中。
&&&&&& 首先,就要知道如何创建一个段。
&&&&&& #pragm data_seg(&sectionname&)//创建一个名为sectionname的段。
&&&&& 看例子:
&&&&& #pragm data_seg(&newsection&)//此处创建一个名为newsection的段
&&&&& int a=23;//向此段中添加变量。
&&&& #pragm data_seg()//结束添加
&&&& 此例创建了一个名为newsection的段,并向此段添加int类型变量a。#pragm data_seg()用于结束向段中添加数据。
要注意一点编译器只会将以初始化的变量放入我们的段中,如上例中的a。
&&&&& 如果这样:
&&& #pragm data_seg(&newsection&)
&&& int a=23;
&&& #pragm data_seg()
&&& b是不会被添加到段newsection中的。而是放到默认的标准段中。
&&& 虽然编译器只会将初始化的变量放入自定义段中,但是我们可以强制的将一个未初始化的数据放我任何我们想放入的段中。
&&& _declspec(allocate(&newsection&) )将b放入newsection中。
&&& 仅仅新建一个段,并将要共享的数据放入新建段中是不够的,还需要将该段声明为共享段。
&&& 我们可以使用:
&&& 1:#pragm comment(linker,&/SECTION:newsection,RWS&)
&&& 2:链接器开关:/SECTON:newsecton,RWS
&& 其中R表示READ,W表示WRITE,S表示SHARE。他们为newsection指定的属性。SHARE即为共享的意思,意思是把此段让所有实例共享。
&&&&&& 放入共享段的变量在多个实例中只有一份,不会再向数据段中的变量一样:每个实例都有一个副本。所以任何实例都可以修改它们。非常重要的一点就是:由于多个实例可以同时修改共享段中的变量,因此要注意同步问题。可以采取线程同步中所介绍的一些方法。
&&&& 现在来讨论内存映射文件介绍的第二个用途:内存映射磁盘数据文件。
要使用内存映射磁盘文件需要三个步骤:
&&&& 1:创建或打开一个文件内核对象。
&&&& 2:创建一个文件映射内核对象。
&&& 3:将文件映射对象映射到进程地址空间。
&&& 对于第一点,可以调用CreateFile或是OpenFile,很简单,此处不作介绍。
HANDLE WINAPI CreateFile( __in&&&&& LPCTSTR lpFileName, __in&&&&& DWORD dwDesiredAccess, __in&&&&& DWORD dwShareMode, __in_opt& LPSECURITY_ATTRIBUTES lpSecurityAttributes, __in&&&&& DWORD dwCreationDisposition, __in&&&&& DWORD dwFlagsAndAttributes, __in_opt& HANDLE hTemplateFile );
&&&& 第二点:可以调用CreateFileMapping
HANDLE WINAPI CreateFileMapping( __in&&&&& HANDLE hFile, __in_opt& LPSECURITY_ATTRIBUTES lpAttributes, __in&&&&& DWORD flProtect, __in&&&&& DWORD dwMaximumSizeHigh, __in&&&&& DWORD dwMaximumSizeLow, __in_opt& LPCTSTR lpName );
&& 第一个hFile为要映射到进程地址空间中的文件句柄,CreateFile或是OpenFile返回。
&& 第二个psa为安全属性,一般都传NULL,表示使用默认安全属性。
&& 第三个为fdwProtect保护属性,指定当将文件映射到进程地址空间的时候,应该给物理存储器的页面指定何种保护属性。
&& 第四个,第五个参数告诉系统内存映射文件的最大大小。
第四个参数dwMaximumSizeHigh为表示文件大小的64位整数的高字节,dwMaximumSizeLow为低字节。对于小于4G的文件来说,高字节当然为0.
&&&& 如果要以文件的当前大小创建一个映射对象时,只要将他们设为0就可以。如果要文件中添加数据,一定要使指定的大小大于文件的真实大小。
&&& 第六个参数为文件映射内核对象的名称。用于跨进程共享命名内核对象。(请参考windows核心编程 第五版 第三章)需要特别强调下,如果为flProtect指定PAGE_READWRITE属性,当文件的真实大小小于参数中指定的大小的时候,CreateFileMapping会自动增大文件大小。为的是在将文件作为内存映射文件后,物理存储器已经就绪。向其写入数据不会发生错误。如果指定PAGE_READONLY或是PAGE_WRITECOPY,那么传入的大小不能大于文件的真实大小,因为我们只并不能向文件中增加数据。
&&& 第三步:将文件映射到进程地址空间。
&& MapViewOfFile
LPVOID WINAPI MapViewOfFile( __in& HANDLE hFileMappingObject, __in& DWORD dwDesiredAccess, __in& DWORD dwFileOffsetHigh, __in& DWORD dwFileOffsetLow, __in& SIZE_T dwNumberOfBytesToMap );
第一个参数hFileMappingObject即为CreateFileMapping或是OpenFileMapping返回的文件映射内核对象句柄。
&&&& 第二个参数是访问数据的方式。他们依赖于CreateFileMapping 和CreateFile传递的访问方式。
&&&& 第三个和第四个参数告诉系统把数据文件中的的那些内容映射到进程地址空间中。他们分别为要映射文件的偏移 量,是64位的,分别表示高32位和低32位。
&&& 第五个参数指明要把磁盘文件的多少数据映射到进程地址空间中。如果指定为0,系统会把文件中从偏移量开始直到文件末尾的数据全部映射到进程地址空间中。
&&&&&&& 当调用MapViewOfFile时指定FILE_MAP_COPY标志,系统会从系统页交换文件调拨物理存储器,大小有dwNumberOfBytesToMap指定。只要我们不执行读取数据之外的任何操作,系统就不会使用从页交换文件中调拨页面 。但是一旦有任何线程写入文件映射视图的任何地址,系统就会从已经调拨的页交换文件中选择一个页面把原始数据复制到页交换文件中的页面,然后让线程进行修改这个副本,再将此页面映射到进程地址空间中。因此任何线程都只会修改数据的副本而不会修改原始数据。
&&&&& 当不再需要把文件中的数据映射到进程的地址空间的时候,可以调用UnmapViewOfFile& 来释放映射的数据。
BOOL WINAPI UnmapViewOfFile( __in& LPCVOID lpBaseAddress );
&&& lpBaseAddress用于指定区域的基地址,必须和MapViewOfFile相同。
为了提高运行速度,系统会对文件数据的页面进行缓存处理,也就是说对文件映射对象映射后的视图进行修改,不会立即反映到数据文件中。如果想要立即反映到数据文件中,可以调用FlushViewOfFile。来强制系统把修改过的数据协会磁盘文件。
&&&&&&& 如果视图最初使用FILE_MAP_COPY标志来映射的,那么对数据文件的修改实际上对系统页交换文件中的副本进行的修改。请参考红色字段。如果这种情况下调用FlushViewOfFile,系统不会将做出的修改保存到磁盘文件中,而会直接释放系统页交换文件中的相关数据,导致数据丢失。这只是FILE_MAP_COPY的特性,为了防止数据丢失可以用其他标志进行映射。
&&&&&& 最后不要忘记调用CloseHandle关闭文件内核对象和文件映射内核对象。
&&&&&& 如果文件非常大,一次无法全部映射到进程的地址空间中,这是该怎么办呢?
&&&&&&& 此时可以每次只映射一部分文件到进程空间,使用完毕后,撤销映射。再映射下一部分,使用完毕后再次撤销映射。如此循环往复。直至将整个文件映射完毕 。
&&&&&& 系统允许我们把一个数据文件映射到多个视图中。如果我们使用的是同一个文件映射对象映射到不同视图,一旦有一个视图中的数据被修改,其他视图中会立刻更新进而显示更新后的视图。也就是说各个视图中的数据是一致的。为什么各个视图的数据都是一致的呢?
&&&&&&&& 因为他们都是从同一个文件映射对象映射的,数据文件在内存中只有一份,却映射到了不同视图中。但要注意,此处有一前提,就是各个视图都是有同一文件映射对象映射的,如果是同一数据文件为后备存储器创建不同文件映射对象,那就不能保证他们的数据是一致的了。为了防止这种情况,可以在CreateFile时将dwShareMode 设为独占对文件的访问。从而防止不一致性。
&&&&&&&& 注意:在使用完内存映射文件后一定要先撤销对视图的映射和关闭文件映射对象句柄,在对执行打开文件等对文件进行的操作。否则将会造成执行文件操作的失败。另外,执行的操作又失败的可能性,一定要检查。(日,在写文件逆置程序时,撤销对视图的映射后,没有关闭文件映射对象的句柄就执行文件操作,结果这些文件操作都失败了。但是没有执行判断,花了好久才查到错误。)
&&&&&& 下面来讨论第三个问题:内存映射文件实现进程间共享数据。
&&&&&& 如果我们在创建文件映射对象时为它命名,那么就可以实现在不同进程间访问同一文件映射内核对象了。但要注意,要在不同进程分别调用MapViewOfFile,来将同一命名文件映射内核对象,映射到各自的进程地址空间中。
&&&&&& 到此,以我们目前掌握的知识,我们知道要实现在多个进程间共享数据,要创建一个文件对象和一个命名的文件映射内核对象。然后在另一个进程内将此命名的内核对象映射到本进程。这一系列的步骤说明:如果我们要在多个进程间共享数据,我们就必须创建文件,将数据保存在文件中,然后创建文件对象,文件映射对象。。。。这是很繁琐的。
&&&&&&& Microsoft意识到了这一点,为我们提供了支持:让系统创建以页交换文件为后备存储器的内存映射文件。这就是说当实现进程共享数据时,不再需要创建以磁盘文件为后备存储器的文件映射对象。此时,文件映射对象的后备存储器来自系统页交换文件。这种方法和为磁盘文件创建内存映射文件几乎完全相同。区别就是:此时无需创建文件对象,在创建文件映射对象时,只需将INVALID_HANDLE_VALUE传给hFile就可以了。他告诉系统要以系统页交换文件中调拨物理存储器。以后的步骤跟为磁盘文件创建内存映射文件相同。
很简单,不是吗?来看个例子:
HANDLE hFile=CreateFile(...)
HANDLE hMap=CreateFileMapping(hFile........);
if(hMap==NULL)
.....................
看出来什么问题吗?
我们知道调用CreateFile失败的时候,返回的是INVALID_HANDLE_VALUE,而此处没有判断文件对象是否成功,就直接创建文件映射对象,一旦创建文件对象失败,hFile就是INVALID_HANDLE_VALUE,系统会以为程序员要创建以系统页交换文件为后备存储器的内存映射文件,而不是为磁盘文件创建内存映射文件。这就导致了错误。所以在可能导致失败的函数执行之后一定要进程判断。
参考自《windows核心编程&第五版》第三部分,以上仅仅是个人总结,如有纰漏,请不吝赐教,谢谢。同时,想结交志同道合之士,交流windows核心编程的学习
摘自 ithzhang的专栏什么时候会发生jvm堆内存溢出?内存溢出了怎么办_百度知道
什么时候会发生jvm堆内存溢出?内存溢出了怎么办
我有更好的答案
简单的来说 java的堆内存分为两块:permantspace(持久带) 和 heap space。持久带中主要存放用于存放静态类型数据,如 Java Class, Method 等, 与垃圾收集器要收集的Java对象关系不大。而heapspace分为年轻带和年老带 年轻代的垃圾回收叫 Young GC, 年老代的垃圾回收叫 Full GC。在年轻代中经历了N次(可配置)垃圾回收后仍然存活的对象,就会被复制到年老代中。因此,可以认为年老代中存放的都是一些生命周期较长的对象年老代溢出原因有
循环上万次的字符串处理、创建上千万个对象、在一段代码内申请上百M甚至上G的内存持久代溢出原因
动态加载了大量Java类而导致溢出
采纳率:100%
为您推荐:
其他类似问题
内存溢出的相关知识
&#xe675;换一换
回答问题,赢新手礼包&#xe6b9;
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。1. Java有几种内存?每一种内存有什么功能?哪一种内存速度快?_百度知道
1. Java有几种内存?每一种内存有什么功能?哪一种内存速度快?
答案2堆是先进先出,而栈是先进后处 1. 栈(stack)与堆(heap)都是Java用来在Ram中存放数据的地方。与C++不同,Java自动管理栈和堆,程序员不能直接地设置栈或堆。 2. 栈的优势是,存取速度比堆要快,仅次于直接位于CPU中的寄存器。但缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性。另外,栈数据可以共享,详见第3点。堆的优势是可以动态地分配内存大小,生存期也不必事先告诉编译器,Java的垃圾收集器会自动收走这些不再使用的数据。但缺点是,由于要在运行时动态分配内存,存取速度较慢 还有人有更好的补充吗,无论什么样的答案,只要您愿意写,都表示感谢!
我有更好的答案
有六个地方都可以存储数据:
寄存器(Registers)。这是速度最快的存储场所,因为寄存器其他所有存储媒介都不同:它位于处理器内部。不过,寄存器的数量十分有限,所以寄存器是根据需要由编译器适当地分配。作为一个程序员,我们对此没有直接的控制权,也没办法在程序里头感觉到寄存器的任何存在迹象。
Stack(栈)。位于一般的RAM(random-access
memory,随机访问内存)中。处理器通过其指针(“栈指针”,stack
pointer)获得处理的直接支持。栈指针若向下(后)移,会分配新的内存;若向上(前)移,则会释放那些内存。这是一种特别快、特别有效率的数据存储方式,速度仅次于寄存器。由于Java编译器有责任产生“将stack指针前后移动”的程序代码,所以它必须能够完全掌握它所编译的程序中“存在stack里头的所有数据的实际大小和存活时间”。如此一来便会限制程序的弹性。由于这个限制,尽管有些Java数据要存储在栈里——特别是对象句柄,但Java对象并不放到其中。
Heap(堆)。Heap是一种通用性质的内存存储空间(也存在于RAM中),用来置放所有Java对象。“内存堆”或“堆”(Heap)胜过stack之处在于,编译器不需知道究竟得从堆里分配多少存储空间,也不需知道从堆上分配的空间究竟要存活多长的时间。因此,用堆存储数据时会得到更大的灵活性。要求创建一个对象时,只需用new即可。执行这些代码时,会在堆里分配空间。当然,为达到这种灵活性,必然会付出一定的代价:在堆里分配存储空间时会比从栈里分配花掉更长的时间(假设你真的可以在Java中像C++一样地从stack上产生对象的话)!
静态存储空间(Static
storage)。这儿的“静态”(Static)是指“位于固定位置”(也在RAM里头)。静态存储空间存放着“程序运行期间”一直存在的数据。可用static关键字将某个对象内的特定成员设为静态,但Java对象本身永远都不会置入静态存储空间。
常量存储空间(Constant
storage)。常量值通常被直接置于程序代码里头。因为它们永远都不会改变,所以也是安全的。有的常数需要严格地保护,所以可考虑将它们置入只读存储器(read-only
memory,ROM)中。
非RAM存储空间(Non-RAM
storage)。若数据完全存活于程序之外,则程序不运行时数据仍继续存在,脱离了程序的控制范围。其中两个最主要的例子便是“串流化对象(streamed
objects)”和“持久性对象(persistent
objects)”。在串流化对象形式中,对象会被转换为一连串的字节(bytes)流,这些bytes通常会被传送给另一台机器。而在持久性对象形式中,对象被存储于磁盘,即使程序运行结束,这些对象还能够继续保有。这种类型的存储空间的特点在于,它们能够将对象转换为可存储于其他媒介的形式,并在需要时,将所存储的数据还原成可存储于RAM中的一般对象。Java提供了对“轻量级持久性(Lightweight
persistence)”的支持。新版本有可能提供更完善的解决方案。 针对你的补充我也补充一下:堆内存和栈内存确实是我们常常用的东西,比如Animal a = new Animal();这个时候相当于在堆内存中开辟了一个空间保存了Animal的信息以及着块空间的内存地址,然后在栈内存中划了一小快空间保存了堆中的内存地址,这个时候我们就可以说引用a指向Animal()了.可是有时候,有个静态类.Animal,里面有个静态方法speak();那么可以这么直接调用Animal.sepak();这个时候既没有new,也没有Animal a=??;所以既没有在堆中开辟空间也没有在栈内存中开辟空间 ,可是方法确实能执行,一切程序都运行在内存里,那么证明有新的内存区,就是静态空间了.当然还有常量什么的等等
采纳率:40%
学习了 说的很好了!
为您推荐:
其他类似问题
您可能关注的内容
内存的相关知识
&#xe675;换一换
回答问题,赢新手礼包&#xe6b9;
个人、企业类
违法有害信息,请在下方选择后提交
色情、暴力
我们会通过消息、邮箱等方式尽快将举报结果通知您。持久内存编程(第 1 节)什么是持久内存? | Intel(R) Software

我要回帖

更多关于 持久性内存 的文章

 

随机推荐