《深刻淺出node.js》第四章——內存控制(筆記)

內存控制

  1. V8的垃圾回收機制 / 內存限制

    1. V8讓JS虛擬機的性能達到了很快的地步,因此node實如今V8上
    2. V8的內存限制:Node中經過JS使用內存只能使用部份內存(64G下大概1.4GB),因此Node沒法直接操做大內存對象
    3. V8的對象分配
      1. V8中,全部JS對象都是經過堆進行分配,若是已申請的堆空閒不夠分配新的對象,將繼續申請堆內存
      2. 經過process.memoryUsage()能夠看到如今的內存使用狀況
      3. Node在啓動的時候能夠傳遞 --max-old-space-size / --max-new-space-size 來調整內存限制的大小,只在初始化有效。
    4. V8 的垃圾回收機制
      1. V8主要的垃圾回收算法:分代式垃圾回收機制
        1. V8中將內存分爲新生代和老生代,新生代的對象存活時間短,老生代是存存活活時間長/常駐內存的對象
        2. 新生代中的對象主要經過scavenge算法進行垃圾回收,而在scavenge的具體實現使用了Cheney算法
          1. 將對內存一分爲二,每一部分空間成爲semi space。
          2. 2個semispace只有一個在使用,一個處於閒置狀態。處於使用狀態的semi space空間稱爲from空間,處於閒置狀態的空間成爲To空間。
          3. 分配對象的時候,先從from空間中開始分配
          4. 垃圾回收時,會檢查from空間中的存活對象,存活對象唄複製到To空間,不存活的對象佔用的空間將會被釋放。
          5. to和from空間 交換
          6. 總之,這個算法的意思就是將存活對象在兩個semi space空間中進行復制/翻轉
          7. 這個算法犧牲了空間,換取時間;之因此採用這個算法是由於新生代中對象的生命週期很短,佔用的內存也少
          8. 當一個對象通過屢次複製和轉換依然存活,它會被認爲是生命週期較長的對象,從而晉升到老生代中。而晉升的套件通常是2個
            1. 對象是否經歷過scavenge回收,若是有,就晉升
            2. To空間的內存佔用比是否超過比例,若是超過,直接晉升
        3. 老生代中經常使用Mark - Sweep(標記清除)& Mark - compact(標記整理)方法
          1. mark-sweep 標記清除
            1. 遍歷每一個對象,並標記存活的對象,清楚階段只清楚沒有被標記的對象。
            2. 缺點:很容易出現清理以後內存空間不連續,這種內存碎片對後續的內存分配形成問題,由於極可能出現須要分配一個打碎片,全部的碎片空間都沒法完成這次分配,就會提早觸發垃圾回收
          2. mark-compact 標記整理
            1. 標記每一個對象,將活着的對象往一端移動,移動完成以後,直接清理掉邊界外的內存。
          3. incremental Marking 增量標記,減小垃圾回收的停頓時間
            1. 由於每次回收的時候,JS會阻塞,因此把大的垃圾回收工程拆分一小步一小步進行,好比增量標記和增量清理,還有延遲清理(lazy sweeping)
    5. 查看垃圾回收日誌
      1. 啓動node的時候添加 --trace-gc參數
  2. 高效使用內存

    1. 做用域退出/再也不使用了 就會釋放這個做用域裏的變量
      1. 變量的主動釋放
        1. 若是變量是全局變量,對象將常駐在內存。刪除手段有
          1. delete操做刪除引用關係
          2. 從新賦值,讓舊對象脫離引用關係/ 其實直接設置成空/null不行嗎
      2. 閉包(closure)
        1. 閉包致使閉包自身做用域不獲得釋放
  3. 內存指標

    1. 查看內存使用狀況:經過process.memoryUsage()能夠看到如今的內存使用狀況
    2. 查看進程的內存使用,process.memoryUsage()
    3. 查看系統的內存佔用,os模塊的totalmem() 和 freemem() 用來查看系統的總內存和限制內存
    4. 堆外內存:咱們把不是經過V8分配的內存叫作 堆外內存
  4. 內存泄漏

    1. 形成內存泄漏的緣由常見的有
      1. 緩存
      2. 隊列消費不及時
      3. 做用域未釋放
    2. 內存 != 緩存,慎重
      1. 一旦命中緩存,就能夠節省一次I/O的時間;可是一個對象被看成緩存使用,意味着它常駐在老生代中;緩存中存儲的鍵越多,長期存活的對象也就越多,那麼垃圾回收舊會作無用功
      2. 緩存限制策咯:限制緩存的無限增加
        1. 做者寫過一個limitablemap模塊——P128
      3. 緩存的解決方案
        1. 直接將內存做爲緩存的方案,除了緩存大小的顧慮外,還要考慮進程之間沒法共享內存,進程內使用緩存將致使緩存不可避免的有重複,浪費物理空間
        2. 解決方案:採用進程外的緩存,進程自身不存儲狀態
          1. 將緩存轉移到外部,減小常駐內存的對象的數量,讓垃圾回收變得高效
          2. 進程之間能夠共享緩存
        3. 常見的緩存方案( 有客戶端)
          1. Redis
          2. Memcached
    3. 關注隊列狀態,由於也有可能形成內存泄漏
  5. 內存泄漏排查

    1. 常見工具:v8-profiler(3年沒維護了),node-heapdump,node-mtrace,dtrace,node-memwatch
  6. 大內存應用

    1. 使用stream模塊處理大文件(因爲V8內存限制,咱們沒法經過fs.readFile()和fs.writeFile()直接對大文件進行操做)javascript

    2. 使用fs.createReadStream() / fs.createWriteStream()方法經過流的方式實現對大文件的操做java

      1. var reader = fs.createReadStream('in.txt');
        var writer = fs.createWriteStream('out.txt');
        reader.on('data',function(chunk){
            writer.write(chunk);
        })
        reader.on('end',function(){
            writer.end;
        })
        
        //利用es6 中的pipe,簡寫後
        
        var reader = fs.createReadStream('in.txt');
        var writer = fs.createWriteStream('out.txt');
        reader.pipe(writer);
        複製代碼
相關文章
相關標籤/搜索