Golang——垃圾回收GC(2)

1 垃圾回收中的重要概念

1.1 定義

In computer science, garbage collection (GC) is a form of automatic memory management. The garbage collector, or just collector, attempts to reclaim garbage, or memory occupied by objects that are no longer in use by the program. Garbage collection was invented by John McCarthy around 1959 to simplify manual memory management in Lisp. (引用自維基百科))

1.2 GC性能的評價標準

  • 吞吐量:是指單位時間內是有多少時間是用來運行user application的。GC占用的時間過多,就會導致吞吐量較低。

  • 最大暫停時間:基本上所有的垃圾回收算法,都會在執行GC的過程中,暫停user application。如果暫停時間過長,必然會影響用戶體驗,尤其是那些交互性較強的應用。

  • 堆使用效率:影響堆使用效率的主要有兩個因素,一個是對象頭部大小,一個是堆的用法。一般來說,堆的頭部越大,存儲的信息越多,那么GC的效率就會越高,吞吐量什么的也會有更佳的表現。但是,我們必須明白,對象頭越小越好。另外,不同的算法對于堆的不同用法,也會導致堆使用效率差距非常大。比如復制算法,用戶應用只能使用一般的堆大小。GC是自動管理內存的,如果因為GC導致過量占用堆,那么就是本末倒置了。

  • 訪問的局部性:具有引用關系的對象之間很可能存在連續訪問的情況。因此,把具有引用關系的對象安排在堆中較勁的位置,可以充分利用內存訪問局部性。有的GC算法會根據引用關系重排對象,比如復制算法。

  • 等等等等

?

設計垃圾回收算法時,折中無處不在。較大的吞吐量和較短的最大暫停時間往往不可兼得。

2 常見的GC算法

?

2.1 標記-清除算法 STW

?

mark_sweep(){????mark()sweep()}

分兩個階段,整個過程需要STW:

?

  • mark phase:時間復雜度跟活動對象數量成正比

  • sweep phase:時間復雜度跟堆的大小成正比

?

sweep階段回收的空間會連接到空閑鏈表上,分配空間時從空閑鏈表分配,因此分配空間時間復雜度可能會有點高,即需要遍歷整個空閑鏈表。

?

優點是實現簡單,并且與保守式GC兼容(即沒有移動對象)

?

缺點也非常明顯:

?

  • 存在內存碎片

  • 分配速度較慢:使用多個空閑鏈表

  • 與寫時復制技術不兼容:位圖標記法

  • sweep操作時間復雜度同堆大小成正比:延遲清除法

?

延遲清除法:沒有空閑鏈表。定義一個全局變量sweeping,從sweeping開始遍歷分配新空間,遍歷的開始位置處于上一次lazy_sweep操作發現的分塊的右邊。如果當前分塊mark=true,則取消標記。如果mark=false并且分開大小大于申請空間,則分配給他。 當遍歷到堆末尾時,需要將sweeping設置為heap_start,并且需要重新標記。

?

2.2 引用計數法

?

在標記-清除等GC算法中,沒有分塊可用時,mutator會調用下面的函數啟動GC回收空間,以便進行分配:

?

garbage_collect(){...
}

然而引用計數法是沒有啟動GC的語句的,它與mutator的執行密切相關,它在mutator的執行過程中通過增減引用計數器的值來實現實時的內存管理和垃圾回收。

?

引用計數器的更新主要有兩個場景:

?

  • 分配新對象時

  • 更新指針時

?

如果引用計數值變為0,則會立即連接到空閑鏈表上去。分配空間時,從空閑鏈表分配,分配失敗,則直接失敗返回。

?

優點是:

?

  • 最大暫停時間短

  • 可即刻回收垃圾

?

缺點是:

?

  • 引用計數值的增減處理繁重,吞吐量低

  • 循環引用

  • 計數器占用很多位

2.3 復制算法 STW

?

GC復制算法將堆空間分成大小相等的兩塊:from空間和to空間。新對象只能從from空間分配。

?

當from空間沒有可用空間時,則會啟動GC,將from空間的活動對象復制到to空間,同時將from和to的身份互換。因此其時間復雜度,與活動對象的數量成正比,而與堆的大小無關。

?

復制時,是從根對象遞歸遍歷復制過去的。因此,我們定義了一個free變量記錄下從這個位置分配可用空間,分配空間的時間復雜度為常數。

?

優點:

?

  • 因為時間復雜度與活動對象數量成正比,而與堆大小無關。所以,吞吐量優秀。

  • 分配速度快

  • 不會發生碎片化

  • 訪問的局部性原理

?

缺點是:

?

  • 堆的使用效率低下

  • 移動對象,與保守式GC不兼容

?

2.4 標記-壓縮算法 STW

?

標記-壓縮算法時將標記-清除和復制算法相結合的產物。

?

標記階段跟標記-清除算法一樣,從根引用的活動變量開始遍歷。時間復雜度同活動對象的數量成正比。

?

而壓縮階段則需要遍歷整個堆,按照之前的排列順序壓縮到堆的一側去。

?

壓縮階段需要遍歷三次堆:

?

  • 第一次遍歷,需要找出計算出所有的活動對象需要移動到哪個位置去

  • 第二次遍歷,需要重寫根指針,重寫活動對象的指針

  • 第三次遍歷,移動對象到目標位置

?

優點是堆的利用率很高,沒有碎片。缺點也是灰常的明顯,壓縮的時間復雜度太高,而且與堆的大小成正比。

?

2.5 分代垃圾回收

?

分代垃圾回收在對象中引入了“年齡”的概念,通過優先回收容易成為垃圾的對象,提高GC的效率。

?

我們把剛生成的對象稱為新生代對象,到達一定年齡的對象稱為老年代對象。

?

在新生代空間執行的GC,稱為minor GC。在老年代空間執行的GC,稱為major GC。

?

堆的結構:

?

?

?

新生成對象分配的空間都是從Eden區分配的。當Eden區滿了的時候,就會觸發minor GC,將Eden區和From區的活動對象都復制到To,然后交互To和From的身份。

?

復制的時候如果超過一定的年齡或者To空間不足(即Eden和From的活動對象占用的空間超過了To),那么直接復制到老年代空間。如果老年代空間不足則會觸發major GC。

?

那些大于Eden空間的對象,一般也不會直接失敗,而是直接分配到老年代空間。實際的實現,一般是超過一定大小,則會將其分配到老年代空間。

?

新生代空間和老年代空間采用不同的GC算法。新生代采用復制算法,老年代采用標記-壓縮算法或者標記-清除算法。

3 Golang的垃圾回收算法

?

3.1 三色標記法

?

golang采用的是并發的三色標記清除算法(Tri-color marking)。

?

  • 白色:還沒搜索的對象

  • 灰色:正在搜索的對象

  • 黑色:搜索完成的對象

?

GC開始前所有的對象都是白色對象。GC開始時,會將從根能夠直接引用的對象標記為灰色,并且放到堆棧里。

?

然后,灰色對象會被依次彈出棧,其子對象也被涂成灰色,壓入棧。當其所有的子對象都變成灰色后,就會把這個對象涂成黑色。

?

當GC結束時,活動對象全部為黑色對象,垃圾則為白色對象,回收白色對象即可。

?

主要分為四個階段:

?

  • root_scan:STW

  • mark...mark...mark...mark...

  • mark termination:STW

  • sweep...sweep...sweep...

?

接下來分別介紹一下上述的四個不同階段。

?

3.1.1 root scan

?

根查找階段需要STW。找出能夠從根直接引用的對象,標記為灰色,壓入棧。

?

root_scan_phase(){????for(r?:?$roots){mark(*r)}????$gc_phase?=?GC_MARK
}mark(obj){????if(obj.mark?==?false){obj.mark?=?truepush(obj,$mark_stack)}
}

當我們把所有直接從根引用的對象涂成了灰色時,root scan階段就結束了,mutator(即user application)會繼續執行。

?

3.1.2 mark 和 mark termination

?

mark是分多次運行的,即增量式的mark,不需要STW,它和mutator交替運行。它主要是彈出棧里面的對象,將其子對象涂成灰色,壓入棧,然后把這個對象涂成黑色。重復這個過程,直到棧為空。

?

mark termination則是需要STW的。它會root_rescan,然后重新執行mark。

?

然而,mark階段與mutator并發運行存在一個問題,可能誤把活動對象當做垃圾對象回收。

?

比如下面的情況:

第二張圖,創建了從黑色對象到白色對象的引用。第三張圖,刪除了從灰色對象到白色對象的引用。這個時候就會導致C被誤認為垃圾而回收。

?

為了避免這種情況的發生,需要引入write barrier。

?

最常用的write barrier是由Dijkstra提出的insertion style write barrier。

?

write_barrier(obj,field,newobj){????if(newobj.mark?==?false){newobj.mark?=?truepush(newobj,$mark_stack)}*field?=?newobj
}

即如果新引用的對象是白色對象,則直接把它涂為灰色:

?

mark和mark termination的偽代碼為:

?

incremental_mark_phase(){????for(i?:?1...MARK_MAX){????????//?分多次mark,不需要STWif(is_empty($mark_stack)?==?false){obj?=?pop($mark_stack)????????????for(child?:?children(obj)){mark(*child)}}else{????????????//?mark?termination,需要STWfor(r?:?roots){mark(*r)}????????????while(is_empty($mark_stack)?==?false){obj?=?pop($mark_stack)????????????????for(child?:?children(obj)){mark(*child)}}????????????$gc_phase?=?GC_SWEEP$sweeping?=?$heap_startreturn}}
}
3.1.3 sweep

?

sweep也是分多次的,增量式的回收垃圾,跟mutator交替運行。跟標記-清除算法的實現基本一致,也是需要遍歷整個堆,將白色對象掛到空閑鏈表上,黑色對象取消mark標記。

?

3.1.4 分配新對象

?

從空閑鏈表分配。

?

3.2 golang為什么沒有采用壓縮算法和分代算法

?

有點高深,想深入了解的可以參考golang-nuts上面的討論。

4 Golang垃圾回收的相關參數

?

4.1 觸發GC

?

gc觸發的時機:2分鐘或者內存占用達到一個閾值(當前堆內存占用是上次gc后對內存占用的兩倍,當GOGC=100時)

?

?#?表示當前應用占用的內存是上次GC時占用內存的兩倍時,觸發GCexport?GOGC=100

4.2 查看GC信息

?

export?GODEBUG=gctrace=1

可以查看gctrace信息。

舉例:

?

gc?1?@0.008s?6%:?0.071+2.0+0.080?ms?clock,?0.21+0.22/1.9/1.9+0.24?ms?cpu,?4->4->3?MB,?5?MB?goal,?4?P#?command-line-argumentsgc?1?@0.001s?16%:?0.071+3.3+0.060?ms?clock,?0.21+0.17/2.9/0.36+0.18?ms?cpu,?4->4->4?MB,?5?MB?goal,?4?Pgc?2?@0.016s?8%:?0.020+6.0+0.070?ms?clock,?0.082+0.094/3.9/2.2+0.28?ms?cpu,?8->9->8?MB,?9?MB?goal,?4?Pgc?3?@0.046s?7%:?0.019+7.3+0.062?ms?clock,?0.076+0.089/7.1/7.0+0.24?ms?cpu,?14->16->14?MB,?17?MB?goal,?4?Pgc?4?@0.092s?8%:?0.015+24+0.10?ms?clock,?0.060+0.10/24/0.75+0.42?ms?cpu,?25->27->24?MB,?28?MB?goal,?4?P

每個字段表示什么信息可以參考?golang doc

5 如何提高GC的性能

?

Golang的GC算法是固定的,用戶無法去配置采用什么算法,也沒法像Java一樣配置年輕代、老年代的空間比例等。golang的GC相關的配置參數只有一個,即GOGC,用來表示觸發GC的條件。

?

目前來看,提高GC效率我們唯一能做的就是減少垃圾的產生。所以說,這一章稱為提高GC的性能也不太合適。下面我們就主要討論一下,在golang中如何減少垃圾的產生,有哪些需要注意的方面。

5.1 golang中的內存分配

?

參考官網Frequently Asked Questions (FAQ)

?

How do I know whether a variable is allocated on the heap or the stack?

From a correctness standpoint, you don't need to know. Each variable in Go exists as long as there are references to it. The storage location chosen by the implementation is irrelevant to the semantics of the language.

?

The storage location does have an effect on writing efficient programs. When possible, the Go compilers will allocate variables that are local to a function in that function's stack frame. However, if the compiler cannot prove that the variable is not referenced after the function returns, then the compiler must allocate the variable on the garbage-collected heap to avoid dangling pointer errors. Also, if a local variable is very large, it might make more sense to store it on the heap rather than the stack.

?

In the current compilers, if a variable has its address taken, that variable is a candidate for allocation on the heap. However, a basic escape analysis recognizes some cases when such variables will not live past the return from the function and can reside on the stack.

我們看一個例子有個直觀的認識:

?

1?package?main2?3?import?()4?5?func?foo()?*int?{6?????var?x?int7?????return?&x8?}9?10?func?bar()?int?{11?????x?:=?new(int)12?????*x?=?113?????return?*x14?}15?16?func?big()?{17?????x?:=?make([]int,0,20)18?????y?:=?make([]int,0,20000)19?20?????len?:=?1021?????z?:=?make([]int,0,len)22?}23?24?func?main()?{25??26?}
#?go?build?-gcflags='-m?-l'?test.go./test.go:7:12:?&x?escapes?to?heap
./test.go:6:9:?moved?to?heap:?x
./test.go:11:13:?bar?new(int)?does?not?escape
./test.go:18:14:?make([]int,?0,?20000)?escapes?to?heap
./test.go:21:14:?make([]int,?0,?len)?escapes?to?heap
./test.go:17:14:?big?make([]int,?0,?20)?does?not?escape
./test.go:17:23:?x?declared?and?not?used
./test.go:18:23:?y?declared?and?not?used
./test.go:21:23:?z?declared?and?not?used

5.2 sync.Pool對象池

?

sync.Pool主要是為了重用對象,一方面縮短了申請空間的時間,另一方面,還減輕了GC的壓力。不過它是一個臨時對象池,為什么這么說呢?因為對象池中的對象會被GC回收。所以說,有狀態的對象,比如數據庫連接是不能夠用sync.Pool來實現的。

use sync.Pool if you frequently allocate many objects of the same type and you want to save some allocation and garbage collection overhead. However, in the current implementation, any unreferenced sync.Pool objects are removed at each garbage collection cycle, so you can't use this as a long-lived free-list of objects. If you want a free-list that maintains objects between GC runs, you'll still have to build that yourself. This is only to reuse allocated objects between garbage collection cycles. ?

sync.Pool主要有兩個方法:

?

func?(p?*Pool)?Get()?interface{}?{...
}func?(p?*Pool)?Put(x?interface{})?{...
}

Get方法是指從臨時對象池中申請對象,put是指把不再使用的對象返回對象池,以便后續重用。如果我們在使用Get申請新對象時pool中沒有可用的對象,那么就會返回nil,除非設置了sync.Pool的New func:

?

type?Pool?struct?{...????//?New?optionally?specifies?a?function?to?generate//?a?value?when?Get?would?otherwise?return?nil.//?It?may?not?be?changed?concurrently?with?calls?to?Get.New?func()?interface{}
}

另外,我們不能對從對象池申請到的對象值做任何假設,可能是New新生成的,可能是被某個協程修改過放回來的。

一個比較好的使用sync.Pool的例子:

var?DEFAULT_SYNC_POOL?*SyncPoolfunc?NewPool()?*SyncPool?{DEFAULT_SYNC_POOL?=?NewSyncPool(5,?????30000,?2,?????)return?DEFAULT_SYNC_POOL
}func?Alloc(size?int)?[]int64?{return?DEFAULT_SYNC_POOL.Alloc(size)
}func?Free(mem?[]int64)?{DEFAULT_SYNC_POOL.Free(mem)
}//?SyncPool?is?a?sync.Pool?base?slab?allocation?memory?pool
type?SyncPool?struct?{classes?????[]sync.PoolclassesSize?[]intminSize?????intmaxSize?????int
}func?NewSyncPool(minSize,?maxSize,?factor?int)?*SyncPool?{n?:=?0for?chunkSize?:=?minSize;?chunkSize?<=?maxSize;?chunkSize?*=?factor?{n++}pool?:=?&SyncPool{make([]sync.Pool,?n),make([]int,?n),minSize,?maxSize,}n?=?0for?chunkSize?:=?minSize;?chunkSize?<=?maxSize;?chunkSize?*=?factor?{pool.classesSize[n]?=?chunkSizepool.classes[n].New?=?func(size?int)?func()?interface{}?{return?func()?interface{}?{buf?:=?make([]int64,?size)return?&buf}}(chunkSize)n++}return?pool
}func?(pool?*SyncPool)?Alloc(size?int)?[]int64?{if?size?<=?pool.maxSize?{for?i?:=?0;?i?<?len(pool.classesSize);?i++?{if?pool.classesSize[i]?>=?size?{mem?:=?pool.classes[i].Get().(*[]int64)//?return?(*mem)[:size]return?(*mem)[:0]}}}return?make([]int64,?0,?size)
}func?(pool?*SyncPool)?Free(mem?[]int64)?{if?size?:=?cap(mem);?size?<=?pool.maxSize?{for?i?:=?0;?i?<?len(pool.classesSize);?i++?{if?pool.classesSize[i]?>=?size?{pool.classes[i].Put(&mem)return}}}
}

有一個開源的通用golang對象池實現,有興趣的可以研究一下:Go Commons Pool,在此不再贅述。

?

5.3 append

?

我們先看一下append的基本用法。

?

nums:=make([]int,0,10)

創建切片,len=0,cap=10,底層實際上分配了10個元素大小的空間。在沒有append數據的情況下,不能直接使用nums[index]。

?

?

nums:=make([]int,5,10)

創建切片,len=5,cap=10,底層實際上分配了10個元素大小的空間。在沒有append數據的情況下,可以直接使用nums[index],index的范圍是[0,4]。執行append操作的時候是從index=5的位置開始存儲的。

?

?

nums?:=?make([]int,5)

如果沒有指定capacity,那么cap與len相等。

?

nums?=?append(nums,10)

執行append操作的時候,nums的地址可能會改變,因此需要利用其返回值重新設置nums。至于nums的地址會不會改變,取決于還沒有空間來存儲新的數據,如果沒有空閑空間了,那就需要申請cap*2的空間,將數據復制過去。

?

因此,我們在使用append操作的時候,最好是設置一個比較合理的cap值,即根據自己的應用場景預申請大小合適的空間,避免無謂的不斷重新申請新空間,這樣可以減少GC的壓力。

?

由append導致的內存飆升和GC壓力過大這個問題,需要特別注意一下。

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/276690.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/276690.shtml
英文地址,請注明出處:http://en.pswp.cn/news/276690.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

java gui框架_推薦!程序員整理的Java資源大全

構建這里搜集了用來構建應用程序的工具。Apache Maven&#xff1a;Maven使用聲明進行構建并進行依賴管理&#xff0c;偏向于使用約定而不是配置進行構建。Maven優于Apache Ant。后者采用了一種過程化的方式進行配置&#xff0c;所以維護起來相當困難。Gradle&#xff1a;Gradle…

帆軟報表(finereport)控件背景色更改

setTimeout(function() {$(.fr-trigger-btn-up).css({"background-color": "#003399" });}, 100); 轉載于:https://www.cnblogs.com/Williamls/p/11571586.html

開心網分析,師從“中國緣”

作者&#xff1a;麥田   一&#xff0c;師從“中國緣” 開心網從08年“爆發”之后&#xff0c;網上出現很多評論文章。幾乎100%的評論文章都談到了開心網“不可思議”的爆發增長速度&#xff0c;比如幾個月就進入了alexa前500等等。但是&#xff0c;幾乎沒有一篇文章提到“開心…

HTML5+CSS3+JQuery1.9 輸入框切換和Div失焦模擬

Div失焦原理&#xff1a;判斷document點擊對象是否在Div容器以內&#xff0c;否則觸發事件 需要腳本&#xff1a;jquery-1.9.1.js 下載地址&#xff1a;http://download.csdn.net/detail/dmtnewtons/5807757 <!DOCTYPE> <html> <head> <meta http-equ…

資本冬天已至,開發者卻可以著眼未來

云&#xff0c;在國內外都已成為軟件開發者的首選服務。縱觀歷史&#xff0c;在云計算發展的這些年里&#xff0c;不管云上做了多少產品和服務&#xff0c;其實都離不開云最本質的價值體系&#xff1a;自服務、高彈性、按需提供、免運維&#xff0c;這些特性也讓云服務天然成為…

mybatis 大于_酸爽!IDEA 中這么玩 MyBatis,讓編碼速度飛起!

作者&#xff1a;Orsoncnblogs.com/java-class/p/6237564.html1. 搭建 MyBatis Generator 插件環境a. 添加插件依賴 pom.xmlb. 配置文件 generatorConfig.xmlc. 數據庫配置文件 jdbc.propertiesd. 配置插件啟動項2.項目實戰a. 比如在一個項目 我們要刪除某個小組下某個用戶的信…

Java的三種代理模式完整源碼分析

Java的三種代理模式&完整源碼分析 Java的三種代理模式&完整源碼分析 參考資料&#xff1a; 博客園-Java的三種代理模式 簡書-JDK動態代理-超詳細源碼分析 [博客園-WeakCache緩存的實現機制](https://www.cnblogs.com/liuyun1995/p/8144676.html) 靜態代理 靜態代理在使…

scatter函數_matplotlib.pyplot常用函數scatter講解大全(三)

前言這篇文章再來總結一個常用畫圖函數scatter-散點圖。參數常用參數示例import matplotlib.pyplot as plt import numpy as np#導入需要的包 datanp.random.multivariate_normal([0,1],[[1,0],[0,1]],200)#準備數據&#xff0c;二維正態分布plt.rcParams["axes.unicode_m…

如何徹底卸載MySQL

本文摘自&#xff1a;http://www.heiqu.com/show-64764-1.html 內容為&#xff1a; 由于安裝MySQL的時候&#xff0c;疏忽沒有選擇底層編碼方式&#xff0c;采用默認的ASCII的編碼格式&#xff0c;于是接二連三的中文轉換問題隨之而來&#xff0c;就想卸載了重新安裝MYSQL&…

vue-cli項目模板的一些思考

之前有個想法&#xff0c;就是要利用vue寫一套ui。然后當時也沒有搞清楚到底怎么寫。 幾經周轉吧&#xff0c;通過付費的方式在gitbook上面找到了答案。 找到答案之后再看我們正在開發的項目&#xff0c;看伙伴寫的代碼&#xff0c;突然發現完全可以按照寫ui組件庫的方式調整目…

flex基于svn協同開發

想做一個游戲&#xff0c;正好有人陪我做。于是想到用flex來協同開發。本來是想使用cvs&#xff0c;可是結果搗鼓了半天&#xff0c;也沒個結果——估計是最近沒怎么看電影&#xff0c;IQ降下來了。于是改用svn。 參考資料&#xff1a;http://www.flashmagazine.com/tutorials/…

cookie與session詳解

session與cookie是什么?session與cookie屬于一種會話控制技術.常用在身份識別&#xff0c;登錄驗證&#xff0c;數據傳輸等.舉個例子&#xff0c;就像我們去超市買東西結賬的時候&#xff0c;我們要拿出我們的會員卡才會獲取優惠.這時候&#xff0c;我們怎么識別這個會員卡真實…

c++萬能頭文件_初學Python,與C對比

?背景學了一學年的C的基礎&#xff0c;下學年開課Python&#xff0c;現在正在自學中...C也不是不學了&#xff0c;而是之前買了一本《CPrimer》在學校里&#xff0c;就準備先學一下Python&#xff0c;下學期利用自由時間接著學習C。這里分析了一下二者的優缺點&#xff0c;供大…

listen(int fd, int backlog)中的backlog含義

1. listen(int fd, int backlog)中的backlog不能限制連接數量??? http://bbs.chinaunix.net/viewthread.php?tid870564 backlog應該是未完成3次握手連接和已完成3次握手而未被accept的兩對列之和.不知道我說的對不? 如果要控制連接數量,是不是要自己編碼控制...下面的可以…

本地無法啟動MySQL服務,報的錯誤:1067,進程意外終止---解決

原文鏈接&#xff1a;http://blog.csdn.net/shenhonglei1234/article/details/5928873 在本地計算機無法啟動MYSQL服務錯誤1067進程意外終止 這種情況一般是my.ini文件配置出錯了 首先找到這個文件&#xff1a; 默認安裝路徑 C:/Program Files/MySQL/MySQL Server 5.1/my.ini …

一篇文章助你理解Python3中字符串編碼問題

前幾天給大家介紹了unicode編碼和utf-8編碼的理論知識&#xff0c;以及Python2中字符串編碼問題&#xff0c;沒來得及上車的小伙伴們可以戳這篇文章&#xff1a;淺談unicode編碼和utf-8編碼的關系和一篇文章助你理解Python2中字符串編碼問題。下面在Python3環境中進行代碼演示&…

Express框架開發知識點總結

express --viewpug myapp 以上語句在當前工作目錄中創建名為 myapp 的 Express 應用程序&#xff0c;采用的模板是jade。 以前還在糾結使用hbs模板引擎或者ejs模板&#xff0c;實際上只要將--view后面的pug換成hbs&#xff08;使用的是Handlebars模板引擎&#xff09;&#xff…

《高級軟件架構師講義》學習筆記5

第五章 設計模式與軟件架構設計 一、面向對象軟件架構設計思想 a) 面向對象范式 i. 面向對象范式的核心是“對象”的概念 ii. 所有的東西都聚焦于對象 iii. 圍繞對象-而非函數-組織代碼 b) 對象從不同視角觀察 i. 概念層&#xff1a;…

python 安裝xpath_Python網絡爬蟲(四)- XPath1.XPath2.XPath在python中的應用

目錄&#xff1a; 1.XPath XPath即為XML路徑語言&#xff08;XML Path Language&#xff09;&#xff0c;它是一種用來確定XML文檔中某部分位置的語言。它使用路徑表達式來選取 XML 文檔中的節點或節點集。節點是通過沿著路徑 (path) 或者步 (steps) 來選取的。 XPath語法 2.XP…

團隊升級

2019獨角獸企業重金招聘Python工程師標準>>> 轉載于:https://my.oschina.net/yulongblog/blog/2988702