国产精品电影_久久视频免费_欧美日韩国产激情_成年人视频免费在线播放_日本久久亚洲电影_久久都是精品_66av99_九色精品美女在线_蜜臀a∨国产成人精品_冲田杏梨av在线_欧美精品在线一区二区三区_麻豆mv在线看

Mongodb源碼分析--內存文件映射(MMAP)

數據庫 其他數據庫 MongoDB
在Mongodb中,其使用了操作系統底層提供的內存映射機制,即MMAP。MMAP可以把磁盤文件的一部分或全部內容直接映射到內存,這樣文件中的信息位置就會在內存中有對應的地址空間,這時對文件的讀寫可以直接用指針來做,而不需要read/write函數了。同時操作系統會將數據刷新保存到磁盤上。

在Mongodb中,其使用了操作系統底層提供的內存映射機制,即MMAP。MMAP可以把磁盤文件的一部分或全部內容直接映射到內存,這樣文件中的信息位置就會在內存中有對應的地址空間,這時對文件的讀寫可以直接用指針來做,而不需要read/write函數了。同時操作系統會將數據刷新保存到磁盤上。如下圖:

鑒于linux,window系統為mmap所提供的API大同小異(見下圖)。這里僅以mongodb對window系統的mmap調用機制為例,來說明一下其具體的實現方式,以及在mongodb啟動時,客戶端提交查詢和插入操作請求時mongodb的mmap執行流程。

上面類圖中:

  1. MongoFile:定義了mongo文件對象常用操作,包括創建,關閉,設置名稱,flushAll,獲取MongoFile文件總尺寸等。
  2. MMF: 一個類型定義,其聲明:typedef MemoryMappedFile MMF;    
  3. MongoMMF:為了便于journaling/durability操作,對MemoryMappedFile進行了一些封裝(特別是對private views )

下面著重看一下windows提供的mmap的常用API:

  1. MapViewOfFile(): 把文件數據映射到進程的地址空間
  2. CreateFileMapping() : 創建一個新的文件映射內核對象 
  3. FlushViewOfFile(): 強制系統將內存中修改過的數據重新寫入磁盤映像,從而可以確保所有的數據更新能及時保存到磁盤
  4. CloseHandle(): 關閉文件映射對象和文件對象
  5. MapViewOfFileEx(): 將文件映射到指定的進程地址空間

參數說明:   

  1. MapViewOfFile(  
  2.     __in HANDLE hFileMappingObject,  /*hFileMappingObject是共享文件對象*/ 
  3.     __in DWORD dwDesiredAccess, /*dwDesiredAccess是文件共享屬性*/ 
  4.     __in DWORD dwFileOffsetHigh, /*dwFileOffsetHigh是文件共享區的偏移地址*/ 
  5.     __in DWORD dwFileOffsetLow, /*dwFileOffsetLow是文件共享區的偏移地址*/ 
  6.     __in SIZE_T dwNumberOfBytesToMap /*dwNumberOfBytesToMap是共享數據長度*/ 
  7.     );   
  1. //winbase.h  
  2.     CreateFileMappingW(  
  3.     __in      HANDLE hFile,   /*hFile是創建共享文件的句柄*/ 
  4.     __in_opt LPSECURITY_ATTRIBUTES lpFileMappingAttributes, /*lpFileMappingAttributes是文件共享的屬性*/ 
  5.     __in      DWORD flProtect,  /*flProtect是當文件映射時讀寫文件的屬性*/ 
  6.     __in      DWORD dwMaximumSizeHigh, /*是文件共享的大小高位字節*/ 
  7.     __in      DWORD dwMaximumSizeLow, /*是文件共享的大小低位字節*/ 
  8.     __in_opt LPCWSTR lpName /*lpName是共享文件對象名稱*/ 
  9.     );  
  10.     #ifdef UNICODE  
  11.     #define CreateFileMapping  CreateFileMappingW  
  12.     #else  
  13.     #define CreateFileMapping  CreateFileMappingA  
  14.     #endif // !UNICODE  
  1. FlushViewOfFile(  
  2. __in LPCVOID lpBaseAddress, /*內存映射文件中的視圖的一個字節的地址*/ 
  3. __in SIZE_T dwNumberOfBytesToFlush /*想要刷新的字節數*/ 
  4. );  
  1. MapViewOfFileEx(  
  2. __in HANDLE hFileMappingObject,  /*共享文件對象*/ 
  3. __in DWORD dwDesiredAccess, /*文件共享屬性*/ 
  4. __in DWORD dwFileOffsetHigh, /*文件共享區的偏移地址*/ 
  5. __in DWORD dwFileOffsetLow, /*文件共享區的偏移地址*/ 
  6. __in SIZE_T dwNumberOfBytesToMap /*共享數據長度*/ 
  7. __in_opt LPVOID lpBaseAddress /*指定映射文件映射對象的地址。如這個地址處沒有足夠的內存空間,  
  8.                                 那么對MapViewOfFileEx的調用會失效*/ 
  9. ); 

下面我們看一下mongodb如何使用上述API,來實現windows環境下對mongofile進行mmap操作的.

  1. //mmap_win.cpp  
  2.     mutex mapViewMutex("mapView");//聲明mapView的互斥體(mutex)對象  
  3.    ourbitset writable;  
  4.  
  5.    /** unmapping 通知,以便清空 writable bits */ 
  6.    void MemoryMappedFile::clearWritableBits(void *p) {  
  7.        for( unsigned i = ((size_t)p)/ChunkSize; i <= (((size_t)p)+len)/ChunkSize; i++ ) {  
  8.            writable.clear(i);  
  9.            assert( !writable.get(i) );  
  10.        }  
  11.    }  
  12.  
  13.    MemoryMappedFile::MemoryMappedFile()  
  14.        : _flushMutex(new mutex("flushMutex")) {  
  15.        fd = 0;  
  16.        maphandle = 0;  
  17.        len = 0;  
  18.        created();  
  19.    }  
  20.    //關閉文件MemoryMappedFile  
  21.    void MemoryMappedFile::close() {  
  22.        for( vector<void*>::iterator i = views.begin(); i != views.end(); i++ ) {  
  23.            clearWritableBits(*i);  
  24.            UnmapViewOfFile(*i);  
  25.        }  
  26.        views.clear();  
  27.        if ( maphandle )  
  28.            CloseHandle(maphandle);//關閉文件映射對象和文件對象  
  29.        maphandle = 0;  
  30.        if ( fd )  
  31.            CloseHandle(fd);//關閉文件映射對象和文件對象  
  32.        fd = 0;  
  33.    }  
  34.  
  35.    unsigned long long mapped = 0;  
  36.    //創建只讀map  
  37.    void* MemoryMappedFile::createReadOnlyMap() {  
  38.        assert( maphandle );  
  39.        scoped_lock lk(mapViewMutex);  
  40.        void *p = MapViewOfFile(maphandle, FILE_MAP_READ, /*f ofs hi*/0, /*f ofs lo*/ 0, /*dwNumberOfBytesToMap 0 means to eof*/0);  
  41.        if ( p == 0 ) {  
  42.            DWORD e = GetLastError();  
  43.            log() << "FILE_MAP_READ MapViewOfFile failed " << filename() << " " << errnoWithDescription(e) << endl;  
  44.        }  
  45.        else {  
  46.            views.push_back(p);  
  47.        }  
  48.        return p;  
  49.    }  
  50.  
  51.    //創建指定名稱和大小的MapViewOfFile  
  52.    void* MemoryMappedFile::map(const char *filenameIn, unsigned long long &length, int options) {  
  53.        assert( fd == 0 && len == 0 ); // 僅能打開一次  
  54.        setFilename(filenameIn);  
  55.        /* big hack here: Babble uses db names with colons.  doesn't seem to work on windows.  temporary perhaps. */ 
  56.        char filename[256];  
  57.        strncpy(filename, filenameIn, 255);  
  58.        filename[255] = 0;  
  59.        {  
  60.            size_t len = strlen( filename );  
  61.            for ( size_t i=len-1; i>=0; i-- ) {  
  62.                if ( filename[i] == '/' ||  
  63.                        filename[i] == '\\' )  
  64.                    break;  
  65.  
  66.                if ( filename[i] == ':' )  
  67.                    filename[i] = '_';  
  68.            }  
  69.        }  
  70.  
  71.        updateLength( filename, length );//如果指定文件已存在,則用已存在的文件長度更新length值  
  72.        {  
  73.            DWORD createOptions = FILE_ATTRIBUTE_NORMAL;  
  74.            if ( options & SEQUENTIAL )  
  75.                createOptions |= FILE_FLAG_SEQUENTIAL_SCAN;//針對連續訪問對文件緩沖進行優化選項  
  76.            DWORD rw = GENERIC_READ | GENERIC_WRITE;//普通讀/寫  
  77.            fd = CreateFile(//創建相關文件  
  78.                     toNativeString(filename).c_str(),//創建的文件名稱  
  79.                     rw, // desired access  
  80.                     FILE_SHARE_WRITE | FILE_SHARE_READ, // share mode  
  81.                     NULL, // security  
  82.                     OPEN_ALWAYS, // create disposition  
  83.                     createOptions , // flags  
  84.                     NULL); // hTempl  
  85.            if ( fd == INVALID_HANDLE_VALUE ) {  
  86.                DWORD e = GetLastError();  
  87.                log() << "Create/OpenFile failed " << filename << " errno:" << e << endl;  
  88.                return 0;  
  89.            }  
  90.        }  
  91.  
  92.        mapped += length;  
  93.        {  
  94.  
  95.           //采用“讀寫文件數據”方式的頁面保護屬性             
  96.  
  97.           DWORD flProtect = PAGE_READWRITE;  
  98.  
  99.            //創建一個文件映射內核對象并告訴系統文件的尺寸以及訪問文件的方式  
  100.            maphandle = CreateFileMapping(fd, NULL, flProtect,  
  101.                                          length >> 32 /*maxsizehigh*/,  
  102.                                          (unsigned) length /*maxsizelow*/,  
  103.                                          NULL/*lpName*/);  
  104.            if ( maphandle == NULL ) {  
  105.  
  106.               // 先獲取操作信息, 因為下面的log()要刪除lasterror信息  
  107.                DWORD e = GetLastError();  
  108.                log() << "CreateFileMapping failed " << filename << ' ' << errnoWithDescription(e) << endl;  
  109.                close();  
  110.                return 0;  
  111.            }  
  112.        }  
  113.  
  114.        void *view = 0;  
  115.        {  
  116.            scoped_lock lk(mapViewMutex);  
  117.            DWORD access = (options&READONLY)? FILE_MAP_READ : FILE_MAP_ALL_ACCESS;  
  118.  
  119.            //把文件數據映射到進程的地址空間  
  120.            view = MapViewOfFile(maphandle, access, /*f ofs hi*/0, /*f ofs lo*/ 0, /*dwNumberOfBytesToMap 0 means to eof*/0);  
  121.        }  
  122.        if ( view == 0 ) {  
  123.            DWORD e = GetLastError();  
  124.            log() << "MapViewOfFile failed " << filename << " " << errnoWithDescription(e) << endl;  
  125.            close();  
  126.        }  
  127.        else {  
  128.            views.push_back(view);  
  129.        }  
  130.        len = length;  
  131.  
  132.        return view;  
  133.    }  
  134.  
  135.    class WindowsFlushable : public MemoryMappedFile::Flushable {  
  136.    public:  
  137.        WindowsFlushable( void * view , HANDLE fd , string filename , boost::shared_ptr<mutex> flushMutex )  
  138.            : _view(view) , _fd(fd) , _filename(filename) , _flushMutex(flushMutex)  
  139.        {}  
  140.  
  141.        void flush() {  
  142.            if (!_view || !_fd)  
  143.                return;  
  144.  
  145.            scoped_lock lk(*_flushMutex);  
  146.            // 強制系統將內存中修改過的數據重新寫入磁盤映像,從而可以確保所有的數據更新能及時保存到磁盤。  
  147.            bool success = FlushViewOfFile(_view, 0 /*0表示全部mapping*/);  
  148.            if (!success) {  
  149.                int err = GetLastError();  
  150.                out() << "FlushViewOfFile failed " << err << " file: " << _filename << endl;  
  151.            }  
  152.  
  153.            success = FlushFileBuffers(_fd);//刷新內部文件緩沖區的數據刷到磁盤上  
  154.            if (!success) {  
  155.                int err = GetLastError();  
  156.                out() << "FlushFileBuffers failed " << err << " file: " << _filename << endl;  
  157.            }  
  158.        }  
  159.  
  160.        void * _view;  
  161.        HANDLE _fd;  
  162.        string _filename;  
  163.        boost::shared_ptr<mutex> _flushMutex;  
  164.    };  
  165.    //是否進行異步的flush操作(該操作會將修改過的數據部分或全部重新寫入磁盤映像)  
  166.    void MemoryMappedFile::flush(bool sync) {  
  167.     uassert(13056, "Async flushing not supported on windows", sync);//windows系統不支持異步flush  
  168.        if( !views.empty() ) {  
  169.            WindowsFlushable f( views[0] , fd , filename() , _flushMutex);  
  170.            f.flush();  
  171.        }  
  172.    }  
  173.   //預先刷數據操作,該方法確保這個對象是可以執行flush()操作,以便在調用該方法之后執行flush操作.  
  174.   //參見mmap.cpp flushAll操作  
  175.   MemoryMappedFile::Flushable * MemoryMappedFile::prepareFlush() {  
  176.        return new WindowsFlushable( views.empty() ? 0 : views[0] , fd , filename() , _flushMutex );  
  177.    }  
  178.    void MemoryMappedFile::_lock() {}  
  179.    void MemoryMappedFile::_unlock() {} 

上面的代碼比較簡單,大家看一下注釋就可以了,下面看一下mmf對于上面的MemoryMappedFile類實現是如何封裝的,因為mmf會在journaling/durability這類場景下使用PrivateMap():    

  1.    //mongommf.cpp文件  
  2.     //構造PrivateMap  
  3.     void* MemoryMappedFile::createPrivateMap() {  
  4.         assert( maphandle );  
  5.         scoped_lock lk(mapViewMutex);  
  6.         //void *p = mapaligned(maphandle, len);  
  7.         void *p = MapViewOfFile(maphandle, FILE_MAP_READ, 0, 0, 0);  
  8.         if ( p == 0 ) {  
  9.             DWORD e = GetLastError();  
  10.             log() << "createPrivateMap failed " << filename() << " " << errnoWithDescription(e) << endl;  
  11.         }  
  12.         else {  
  13.             clearWritableBits(p);  
  14.             views.push_back(p);  
  15.         }  
  16.         return p;  
  17.     }  
  18.     //重新映射PrivateView  
  19.     void* MemoryMappedFile::remapPrivateView(void *oldPrivateAddr) {  
  20.         dbMutex.assertWriteLocked(); // short window where we are unmapped so must be exclusive  
  21.  
  22.         // mapViewMutex確保在重新映射時獲得相同的地址  
  23.         scoped_lock lk(mapViewMutex);  
  24.         //清空 writable bits  
  25.         clearWritableBits(oldPrivateAddr);  
  26.         //從進程的地址空間(oldPrivateAddr)撤消文件數據的映像  
  27.         if( !UnmapViewOfFile(oldPrivateAddr) ) {  
  28.             DWORD e = GetLastError();  
  29.             log() << "UnMapViewOfFile failed " << filename() << ' ' << errnoWithDescription(e) << endl;  
  30.             assert(false);  
  31.         }  
  32.  
  33.         // 將文件映射到指定的進程地址空間  
  34.         void *p = MapViewOfFileEx(maphandle, FILE_MAP_READ, 0, 0,  
  35.                                   /*dwNumberOfBytesToMap 0 means to eof*//*len*/,  
  36.                                   oldPrivateAddr);  
  37.           
  38.         if ( p == 0 ) {  
  39.             DWORD e = GetLastError();  
  40.             log() << "MapViewOfFileEx failed " << filename() << " " << errnoWithDescription(e) << endl;  
  41.             assert(p);  
  42.         }  
  43.         assert(p == oldPrivateAddr);  
  44.         return p;  
  45.     }  
  46. #endif  
  47.     //重新映射PrivateView  
  48.     void MongoMMF::remapThePrivateView() {  
  49.         assert( cmdLine.dur );  
  50.  
  51.         // todo 1.9 : it turns out we require that we always remap to the same address.  
  52.         // so the remove / add isn't necessary and can be removed  
  53.         privateViews.remove(_view_private);  
  54.         _view_private = remapPrivateView(_view_private);  
  55.         privateViews.add(_view_private, this);  
  56.     }  
  57.     ......  
  58.  
  59.     //打開指定的文件并執行mmap操作  
  60.     bool MongoMMF::open(string fname, bool sequentialHint) {  
  61.         setPath(fname);  
  62.         _view_write = mapWithOptions(fname.c_str(), sequentialHint ? SEQUENTIAL : 0);  
  63.         return finishOpening();  
  64.     }  
  65.     //創建指定名稱的文件并執行mmap操作  
  66.     bool MongoMMF::create(string fname, unsigned long long& len, bool sequentialHint) {  
  67.         setPath(fname);  
  68.         _view_write = map(fname.c_str(), len, sequentialHint ? SEQUENTIAL : 0);  
  69.         return finishOpening();  
  70.     }  
  71.     //創建PrivateMap并加載到privateViews集合中  
  72.     bool MongoMMF::finishOpening() {  
  73.         if( _view_write ) {  
  74.             if( cmdLine.dur ) {  
  75.                 _view_private = createPrivateMap();  
  76.                 if( _view_private == 0 ) {  
  77.                     massert( 13636 , "createPrivateMap failed (look in log for error)" , false );  
  78.                 }  
  79.                 privateViews.add(_view_private, this); // note that testIntent builds use this, even though it points to view_write then...  
  80.             }  
  81.             else {  
  82.                 _view_private = _view_write;  
  83.             }  
  84.             return true;  
  85.         }  
  86.         return false;  
  87.     }  
  88.     ......  
  89.     //從privateViews集合中移除當前 _view_private,并關閉文件映射對象和文件對象  
  90.     void MongoMMF::close() {  
  91.         {  
  92.             if( cmdLine.dur && _view_write/*actually was opened*/ ) {  
  93.                 if( debug )  
  94.                     log() << "closingFileNotication:" << filename() << endl;  
  95.                 dur::closingFileNotification();  
  96.             }  
  97.             privateViews.remove(_view_private);  
  98.         }  
  99.         _view_write = _view_private = 0;  
  100.         MemoryMappedFile::close();//關閉文件映射對象和文件對象  
  101.     } 

mongodb完成了上面的工具類的聲明定義之后,就會在前臺使用這些類了,下面通過插入數據操作(之前主要流程我已在這篇文章中有所描述)過程中,對上面類的使用來進行闡述.  

首先需要說明的是,如果是***在本地運行mongod,則不會在指定的數據庫目錄(dbpath 參數)下生成數據庫文件,但如果有數據插入時,則會生成相應文件,這里可以理解為生成文件的過程就是mmap的創建過程。
    
之前的文章中提到過,當客戶端要插入記錄時,則系統會根據客戶端的操作枚舉信息來調用相應的操作,這里它會執行instance.cpp文件中的receivedInsert方法,并進而調用 pdfile.cpp 文件的 insert()函數,而在該方法下有如下一段代碼:

  1. DiskLoc DataFileMgr::insert(const char *ns, const void *obuf, int len, bool god, const BSONElement &writeId, bool mayAddIndex) {  
  2. ......  
  3.       NamespaceDetails *d = nsdetails(ns);//獲取ns的詳細信息  
  4.       if ( d == 0 ) {  
  5.           addNewNamespaceToCatalog(ns);//向system catalog添加新的名空間,它會再次調用當前insert()方法  
  6.           
  7.           // 創建***個數據庫文件,方法位于database.cpp  
  8.           cc().database()->allocExtent(ns, Extent::initialSize(len), false);  
  9. ......  

上面的allocExtent方法用于分配Extent要求的磁盤空間,其中Extent用于記錄多個record記錄信息,而record就是數據庫中的一條記錄。這里可以將Extent看成是一個數據集合,但與我們通常所理解的"數據表"(datatable)有所差異,因為在同一個namespace下可以有一個或多個extent(可以不連續),extent之間是一個雙向鏈表結構,其通過cursor進行向前(forward)或反轉(reverse)的訪問。有關這些內容,參見我之前寫的這篇文章。  

言歸正傳,在上面的allocExtent方法中,會執行pdfile.cpp中的如下方法:

  1. //pdfile.cpp  
  2.  Extent* MongoDataFile::createExtent(const char *ns, int approxSize, bool newCapped, int loops) {  
  3.      .....  
  4.      int ExtentSize = approxSize <= header()->unusedLength ? approxSize : header()->unusedLength;  
  5.      DiskLoc loc;  
  6.      if ( ExtentSize < Extent::minSize() ) {//判斷當前ExtentSize的大小  
  7.          ......  
  8.          //addAFile方法位于 database.cpp  
  9.          return cc().database()->addAFile( 0, true )->createExtent(ns, approxSize, newCapped, loops+1);  
  10.      .....  
  11.  
  12.  }  

***在addAFile方法中,我們會看下如下代碼段:

  1. //database.cpp  
  2.    MongoDataFile* Database::addAFile( int sizeNeeded, bool preallocateNextFile ) {  
  3.        int n = (int) files.size();  
  4.        MongoDataFile *ret = getFile( n, sizeNeeded );//調用下面的getFile方法  
  5.        .....  
  6.    }  
  7.  
  8.    //database.cpp    
  9.    MongoDataFile* Database::getFile( int n, int sizeNeeded , bool preallocateOnly) {  
  10.        ......  
  11.        namespaceIndex.init();  
  12.        .....  
  13.    }  
  14.      
  15.    //namespace.cpp    
  16.    void NamespaceIndex::init() {  
  17.        ......  
  18.        unsigned long long len = 0;  
  19.        boost::filesystem::path nsPath = path();  
  20.        string pathString = nsPath.string();  
  21.        void *p = 0;  
  22.        if( MMF::exists(nsPath) ) {//使用本文前面提到的MMF類,判斷數據庫文件是否存在  
  23.            if( f.open(pathString, true) ) {//打開指定的文件并執行mmap操作  
  24.                len = f.length();  
  25.                if ( len % (1024*1024) != 0 ) {  
  26.                    log() << "bad .ns file: " << pathString << endl;  
  27.                    uassert( 10079 ,  "bad .ns file length, cannot open database", len % (1024*1024) == 0 );  
  28.                }  
  29.                p = f.getView();//返回mapview  
  30.            }  
  31.        }  
  32.        else {//不存在  
  33.            // use lenForNewNsFiles, we are making a new database  
  34.            massert( 10343, "bad lenForNewNsFiles", lenForNewNsFiles >= 1024*1024 );  
  35.            maybeMkdir();//創建相應目錄(如不存在)  
  36.            unsigned long long l = lenForNewNsFiles;  
  37.            if( f.create(pathString, l, true) ) {//創建指定名稱的文件并執行mmap操作  
  38.                getDur().createdFile(pathString, l); // always a new file  
  39.                len = l;  
  40.                assert( len == lenForNewNsFiles );  
  41.                p = f.getView();//返回mapview  
  42.            }  
  43.        }  
  44.        ......  
  45.    } 

下面用一張時序圖來大體回顧一下這***程:

在創建了該數據庫文件及相應mmap操作之后,下面再重新啟動mongod時,系統會通過構造client類的上下文對象 (context)方法來最終調用namespaceIndex.init()方法,其時序圖如下,大家可以通過調試源碼來難證這***程:

好了,今天的內容到這里就告一段落。

參考鏈接:

  1. http://www.cnblogs.com/daizhj/archive/2011/03/30/1999699.html
  2. http://en.wikipedia.org/wiki/Mmap
  3. http://linux.about.com/library/cmd/blcmdl2_mmap.htm
  4. http://msdn.microsoft.com/en-us/library/aa366761.aspx
  5. http://hi.baidu.com/%B2%A4%B2%CB%B1%F9%B1%F9/blog/item/f6e6fb2561c0136a35a80f70.html            

 原文鏈接:http://www.cnblogs.com/daizhj/archive/2011/04/25/mongos_mmap_source_code.html

【編輯推薦】

  1. 走進MongoDB的世界 展開MongoDB的學習之旅
  2. 淺析Mongodb源碼之游標Cursor
  3. 野心勃勃的NoSQL新貴 MongoDB應用實戰
  4. MongoDB與CouchDB全方位對比
  5. MongoDB1.8發布,分布式文檔數據庫

 

責任編輯:艾婧 來源: 博客園
相關推薦

2023-03-01 10:37:51

2021-11-11 05:00:02

JavaMmap內存

2014-07-28 11:20:20

mmap虛擬映射Linux

2025-07-09 02:00:00

2021-12-03 16:20:26

鴻蒙HarmonyOS應用

2011-05-26 10:05:48

MongoDB

2011-05-26 16:18:51

Mongodb

2009-07-24 10:00:38

.NET 4.0內存映

2011-08-16 09:34:34

Nginx

2011-04-29 13:40:37

MongoDBCommand

2012-06-20 14:16:36

Java內存映射

2015-11-16 11:22:05

Java對象內存分配

2024-10-31 09:24:42

2020-10-09 07:13:11

Linux系統編程mmap

2021-12-03 16:22:05

鴻蒙HarmonyOS應用

2009-08-13 14:21:04

.NET內存映射文件

2021-04-27 13:56:49

內存.映射地址

2021-04-23 20:59:02

ThreadLocal內存

2023-08-03 07:30:01

JavaNIO庫

2021-12-02 15:08:23

鴻蒙HarmonyOS應用
點贊
收藏

51CTO技術棧公眾號

日本成人激情视频| 日韩电影免费在线看| 亚洲黄色高清| 久久九九免费视频| 2020中文字幕在线| 中文字幕免费一区二区| 精品国产一区a| 超级碰碰视频| 女生裸体视频一区二区三区| 精品福利av导航| 国产奶头好大揉着好爽视频| jizz国产精品| 国产成人丝袜美腿| 九九热这里只有精品6| 免费的黄网站在线观看| 亚洲午夜在线电影| 在线观看欧美| 色综合欧美在线视频区| 成人免费aaa| 国产精品日本| 91香蕉国产在线观看| 激情不卡一区二区三区视频在线| 91精品视频网| 尤物视频网站在线观看| 中文子幕无线码一区tr| 草草视频在线免费观看| 免费人成网站在线观看欧美高清| 成人h视频在线观看| 人人网欧美视频| 欧美国产日韩xxxxx| 日本一区免费网站| 日韩黄色高清视频| 最新黄网在线观看| 欧美日韩成人在线一区| 蜜芽tv福利在线视频| 精品久久久久人成| 在线视频你懂| 亚洲国产wwwccc36天堂| 色偷偷免费视频| 亚洲欧美日韩精品久久久久| 中文字幕在线综合| 国产精品福利一区二区三区| 国产xxxxx在线观看| 91蜜桃视频在线| 大香煮伊手机一区| 国产精品欧美一级免费| 男人资源网站| 亚洲国产日韩一级| 免费在线一级视频| 欧美日韩免费不卡视频一区二区三区| 国产免费av高清在线| 色综合色综合色综合| av网站无病毒在线| 精品少妇一区二区三区免费观看 | 加勒比久久高清| 久久在线观看视频| 国产成人在线中文字幕| 555www成人网| 日韩在线观看| 国产精品日韩高清| 久久av最新网址| 中文字幕精品在线播放 | 91av在线影院| 国产成人精品三级高清久久91| 国产精品日韩欧美大师| 欧美激情性爽国产精品17p| 精品一区二区三区自拍图片区 | 香港三级经典全部种子下载| 午夜国产精品一区| 超鹏97在线| 最近的2019中文字幕免费一页| 韩国精品福利一区二区三区| 成人免费在线视频网站| 西西裸体人体做爰大胆久久久| av中文字幕av| 亚洲美女区一区| 激情视频在线观看| 深夜福利国产精品| 欧美日韩在线播放视频| 色综合电影网| 国产精品日韩精品欧美在线| 久久av少妇| 中文国产亚洲喷潮| 日韩精品免费一区二区在线观看| 麻豆av一区| 9l国产精品久久久久麻豆| 天堂电影在线| 亚洲天堂网站在线观看视频| 免费电影一区二区三区| 欧美一区二区三区在线免费观看| 91亚洲精品久久久蜜桃| 九色网友自拍视频手机在线| 在线成人一区二区| 欧美在线国产| 9久久9毛片又大又硬又粗| 五月综合激情网| 久久亚洲精品爱爱| 国产精品乱子乱xxxx| 91理论电影在线观看| 日韩伦理在线电影| 911国产网站尤物在线观看| 日韩高清在线一区| 视频免费在线看| 亚洲深夜福利视频| 亚洲五月综合| 激情五月婷婷久久| 亚洲福利在线看| 久久影院100000精品| 999久久欧美人妻一区二区| 欧美日韩加勒比精品一区| 欧洲美女精品免费观看视频| 国产精品我不卡| 国产精品欧美久久久久一区二区| 香蕉久久aⅴ一区二区三区| 日本老师69xxx| 国产91精品欧美| 国产乱视频在线观看| 久久久久久有精品国产| 久久一二三四| 在线国产视频观看| 久久男人av资源网站| 激情综合色播五月| 在线免费看黄网站| 国产在线视频91| 国产精品免费观看视频| se01亚洲视频| 日本在线视频不卡| 欧美午夜影院一区| blacked蜜桃精品一区| 青青草av网站| 亚洲午夜av久久乱码| 久久一区欧美| 电影av一区| 亚洲精品女av网站| 亚洲成av人片观看| 日韩av资源网| 国产一级特黄a大片免费| 亚洲视频在线观看网站| 三级久久三级久久久| 成年人在线免费观看| 国产精品欧美激情在线播放| 国产亚洲制服色| 精品久久亚洲| 自慰无码一区二区三区| 亚洲夜晚福利在线观看| 久久综合综合久久综合| av电影院在线看| 在线观看成人av电影| 亚洲精品一区二区三区99| 久久国产精品久久w女人spa| 浪潮av一区| 日韩欧美亚洲区| 精品91自产拍在线观看一区| 首页欧美精品中文字幕| 久久av色综合| 青青草免费在线视频观看| 亚洲视频在线看| av在线这里只有精品| 欧美videos粗暴| 日韩精品视频久久| 久久久女女女女999久久| 中文字幕一区在线观看| 蜜桃一区二区三区| 国产女王在线**视频| 成人性生交大片免费看视频直播 | 国产成人免费观看| 午夜影院久久久| 国产精品久久久久久| 在线成年人视频| 51蜜桃传媒精品一区二区| 色婷婷国产精品久久包臀| 亚洲精品专区| 国产99在线| 欧美亚洲国产成人| 2019亚洲男人天堂| 日韩欧美综合在线视频| 亚洲欧美日韩国产综合精品二区| 国产天堂在线播放视频| 蜜臀av色欲a片无码精品一区| 久久综合久久八八| 一区二区三区在线观看动漫| 欧美日韩中文| 在线观看爽视频| 丰满少妇在线观看| 国产欧美精品一区二区三区-老狼 国产欧美精品一区二区三区介绍 国产欧美精品一区二区 | 日本片在线观看| 日韩xxxx视频| 91精品国产91久久久久久| 同产精品九九九| 日韩黄色免费电影| 日日夜夜综合| 最色在线观看| 日韩亚洲不卡在线| 欧美日韩成人在线播放| 亚洲最新在线观看| 秋霞电影网一区二区| 日韩一区二区三区色| 日本亚洲一区| 人妻av无码专区| 91久久中文字幕|