阻塞的實現方式有哪些?php
如何防止data racehtml
Context的原理以及使用場景java
Golang併發原理(協程有什麼優點)以及GPM調度策略git
1. 內存管理以及垃圾回收
2. Golang的網絡IO模型?大量對象初始化如何優化,好比在http 接收數據時bufio的建立
複製代碼
PHP程序員
PHP7革新與性能優化 github
1. error 變爲 exception
2. AST 抽象語法樹
[php5] php code-> parser ->opcode -> excute
[php7] php code->parser->ast->opcode->excute
3. JIT 及時編譯技術 再opcode 後面增長了類型判斷以及及時編譯,提高了8倍
4. zval 結構修改 內存從24 降低到16
5. zend_string 結構修改 val值從 char* 變成[]char 用來下降cpu cache miss
6. hasttable 以前是 buckets 是存儲指針 指向各個bucket php7後 將怎麼buchkets連在一塊,分配在同一快內存
同時bucket 的大小也降低50%
7 函數調用機制 減小重複的指令 再預處理階段就提早完成某些函數處理 ,提升處理效率
複製代碼
PHP 垃圾回收機制詳解golang
1. 採用引用計數的方式 每一個zval 都擁有 is_ref 跟ref_count 屬性
每增長一次引用 ref_count就會加1
當ref_count = 0時,程序就自動進行gc
這裏的問題在於循環引用會有問題 這個再php5.3以後就已經解決了
解決的方式是
php 會將 ref_count 減一後 還大於0 ,則就會認爲他多是一個垃圾
會將這些放入一個垃圾buffer池中。對這個池進行dfs 將每一個節點進行ref_count -1 並標記是不是‘已減’狀態
再遍歷一次 將全部ref_count 不爲1 的變量 清除並回收 改回收動做發送在buffer池滿的狀況下
複製代碼
GOLANG編程
1. sync.WaitGroup var wg sync.WaitGroup wg.add(1) wg.wait 2. 空select 3. 空for 循環 4. sync.Mutex 鎖上一個已經鎖上的鎖 會一直阻塞 var m sync.Mutex m.lock m.lock 5. os.Signal 信號會一直等待信號的到來 6.空channel c := make(chan struct{}) <- c 複製代碼
在代碼運行先 先作競爭檢測 --race
若是存在問題就會顯示出來
一般有競爭檢測的問題經常發生在 多goroutine 同時修改變量或者map
通常處理的方式就是加鎖
複製代碼
context 做用: 用來控制goroutine 的生命週期 type Context interface { Deadline() Done() Err() Value(key interface) interface() } 有這四種方法,因此能夠基於如下方式生成context 實例 WithDeadline WithCancel 等等 場景1 多goroutine 執行超時通知 使用 context.withiTimeout 場景2 函數方法之間傳遞父子狀態,能夠實現相似 tracelog 之類的全鏈路追蹤 複製代碼
golang 併發依照CSP模型,原理是使用goroutine 來傳遞數據而不是經過加鎖的方式來實現數據共享 do not communicate by sharing memory,instead sharing memory by communicating 而主流的編程如java 等 都是用過共享內存的方式實現傳遞數據的 協程有什麼優點: 協程爲輕量級線程 1.爲何輕量 線程併發執行流程: 線程是內核對外提供的服務。有內核對其進行調用和切換,線程再等待io操做時 線程會變成unrunnable,進而進行上下文切換。操做系統通常是搶佔式調度。上下文切換通常發生在時鐘中斷或者系統調用返回前 上下文切換 顧名思義 context switch 先進行context的pop 須要進行save,而後進行switch 因此這一切流程會較爲冗長 協程併發執行流程: 不依賴操做系統,golang 本身實現GMP 併發模型。而且協程稱爲用戶態線程,其切換也發生在用戶態。用戶態並無時鐘中斷以及系統調用 2 go協程佔用內存少 go協程只佔用棧內存 大約4~5kb 而一個協程約爲1Mb 3 go 聲明簡單 一個go func 就能直接申明協程 而且可以隨意建立上萬個協程 複製代碼
線程實現模型 緩存
M:內核級線程的封裝 G:表明goroutine P: 表明執行go代碼片斷的上下文環境
操做系統內存管理:
最初: 內存當作一個數組 每一個元素大小爲1B,CPU經過內存地址來選址,取到輸入寫入寄存器,進而執行機器指令。
此時遇到一個問題(如何處理多任務同時運行)
1. 內存訪問衝突,2個程序同時訪問同一個內存塊 這是產生奔潰
2. 內存不足,由於每一個程序員都須要本身申請內存。可是free 不及時,則會形成內存浪費
3. 程序開發成本高
改進I : 增長虛擬內存 全部程序使用統一的連續虛擬地址,系統會將虛擬地址翻譯成真正的物理地址,再加載到內存中
改進II. 對於內存不足的狀況 虛擬內存本質是將磁盤當成最終的存儲,而內存當作cache,好比申請1G 的空間 其實最終內存不會用到1G的空間,只會開闢很小的一塊給程序使用,以避免形成申請過多內存而不使用的浪費,在內存尋址過程當中,發現虛擬內存不指向真正的內存地址時,則會進行再次申請空間 本質就是經常使用的放在內存,不經常使用的放在磁盤中
CPU內存訪問全過程
1. CPU使用虛擬內存訪問數據,MOV指令加載到寄存器中,將地址傳遞給MMU
2. MMU生產PTE地址,並嘗試從內存中獲得他
3. 若是根據PTE地址獲取到真實的內存地址,則會正常讀取數據,流程結束
4. 若是沒法獲取到真實的內存地址,則會引發缺頁異常
5. 操做系統捕捉到這個異常,會從新生成一頁內存,並更新頁表
6. 而後回到第三步,流程結束
這裏3,4步 就會涉及到一個概念 內存命中率
若是物理內存不足,則會頻繁致使3,4步驟,由於磁盤的讀取速度緩慢,會致使總體性能降低。cpu指標中的iowait 會很高
這裏還會延伸出 CPU cache 的概念
CPU cache 是位於 cpu 與內存之間的緩存 通常有L1,L2,L3 3級緩存,用來調節 cpu 與內存 至 磁盤之間的速度,性能差
GO內存管理:
基於 tcmalloc 實現的內存管理
內存池的概念:
若是不用池的話
{
malloc mmap 申請的代價:
1. 系統調用會致使 程序進入內核態,分配完成後,再返回用戶態
2. 頻繁的申請,會存在大量的內存碎片
}
使用池的優劣
{
1. 提早申請一大片內存,用戶使用的時候不用頻繁進入內核態
2. 由於是連續的空間,不會存在內存碎片的問題
3. 局部性良好 由於訪問的是同一個空間
4.會形成必定的內存浪費,可是對於昂貴資源的申請,則創建資源池是個很是好的實現
}
內存池 mheap
go語言再啓動之初 ,就想操做系統那裏申請一大塊內存用來當作內存池,放入mheap中,
mheap 有如下幾個結構
spans(512Mb) -> bitmap(16Gb) -> arena(512GB)
各個span 經過鏈表的方式鏈接在一塊兒 造成各個功能的spans 用sizeclass 做爲標識
針對不一樣的大小的對象申請內存 使用不一樣的sizeclass 作申請 一共有67種
這種鏈表形式 叫作 mcentral
至於mcache 則是 mcentral 的cache
什麼叫作逃逸分析
c語言中 使用malloc在堆上分配內存 使用free 釋放內存
go語言中,編譯器自動幫你找到malloc的變量,使用堆內存 這個分析過程就是 逃逸分析
複製代碼
如圖所示
7 go垃圾回收
go 1.3 以前是採用 mark sweep
後面採用三色標記法
三色標記法的過程 相似二叉樹的層次遍歷
1. 將全部待處理的變量表尾白色
2. 從根節點觸發 BFS 將可達的 變量放入待處理區, 標記爲灰色
3. 將待處理區的節點取出 將其標記爲黑色 將其子節點放入待處理區
4. 直到待處理區爲空 則全部不可達的變量就是垃圾變量 對其進行清除
在2,3步驟中 程序此時仍然在運行,因此會有新的變量產生,或者舊變量被引用,爲了對這塊加控制,因此有 write barrier 寫屏障的概念 此時會觸發stw
什麼時候GC 堆上分配32Kb 內存時就要進行回收檢查 主動gc 是阻塞的
複製代碼
8 Golang 的網絡io模型
網絡io模型
阻塞io blocking io
非阻塞io non-blocking io
多路複用io multiplexing io
信號驅動io signal-driven io
異步io asynchronous io
io模型 須要瞭解的點有如下
用戶態 和內核態
用戶進程須要進行系統調用的時候 ,必須將其切換成內核態,才能執行包括操做cpu,操做io,外設,網絡通訊等等操做
等操做完畢以後,纔會切換回用戶態,其中包括了創建的大量上下文,這是很是消耗性能的
因此在 用戶進程 以及 系統進程之間 會有2個 緩衝區
用戶緩衝區 以及 內核緩衝區
而阻塞io 的 核心在於 只有從 內核緩衝區拷貝到用戶緩衝區時,才通知用戶進程 這就是 阻塞io
而 非阻塞io 是在於 當數據還處於內核緩衝區的時候 就通知用戶進程,這就是非阻塞io
非阻塞io 如何通知用戶進程呢? 就須要不斷的輪詢 壞處就是 cpu使用率很低
多路複用io 本質在於 輪詢多個非阻塞io,只要有一個任務完成,就發起通知
多路複用io 的實現有如下幾個 select poll 和epoll
縱觀全局 每個socket 實際上是non-blocking io的 可是總體用戶進程是 blocking 的,使用select 函數進行blocking
信號驅動io 使用sigaction 系統調用,其等待過程是non-blocking 的 這樣的cpu利用率高
異步io 與其他4中 同步io 的區別 同步io 是指總有某個步驟會阻塞,而 異步io 永遠不會阻塞 再內核準備數據的時候就進行返回
go語言對網絡io 的優化
1. 網絡庫全部都封裝成非阻塞方式,減小上下文切換的開銷
2. 運行時加入epoll 機制,當goroutine 進行網絡io 請求可是 並未就緒時,則掛起G,根據GPM 模型,此時P 會運行其餘G,等剛剛的G就緒了,會會從新從就緒隊列中取出,再由調度器將他們從新分配M執行
複製代碼
優化的方式就是:建立臨時對象池
靈感來自於GO的內存模型,基於tcmalloc,在程序開始階段建立一大塊內存來充當對象池
至於建立的內存空間 是來自於 棧 仍是堆 則由go 專門的編譯器進行逃逸分析獲得的
好處在於
1. 減小用戶態以及內核態之間的上下文切換
2. 減小內存碎片的產生
3. 局部良好性,至關於訪問同一個內存空間
複製代碼