您當前的位置:首頁 > 攝影

學習vlang程式設計(二)記憶體管理堆•棧篇

作者:由 yalight 發表于 攝影時間:2022-11-12

V首先透過使用值型別和字串緩衝區,來避免不必要的記憶體分配,大多數物件(大約90-100%)是由V的自動釋放引擎釋放的,編譯器在編譯過程中自動插入記憶體釋放的函式方法。剩下的小部分的物件透過引用計數釋放。開發人員不需要更改程式碼中的任何內容。“It just works”,就像在Python、Go或Java,除了沒有繁重的GC跟蹤或高成本的對每個物件的引用計數。V提供了記憶體管理的靈活性,能夠實現自動化,半自動化,和手動的記憶體管理。

堆和棧的基本概念

計算機的記憶體按用途可劃分為棧(stack)和堆(heap)。cpu有專門的處理棧的指令,資料進棧和彈出按照先入後出、後入先出的原則進行操作,管理起來簡單,開銷最小。v優先把各種資料結構的資料放到棧中進行處理,不需要進行額外的GC操作。但系統的棧空間太小,往往很難滿足程式的需求,所以開闢一塊大的記憶體進行資料的儲存就有必要了,這塊記憶體就稱為堆。

堆記憶體就需要合理的管理了。程式申請了一塊記憶體一直不釋放,造成堆記憶體資源緊張最後耗費盡了,計算機程式就崩潰了。一塊記憶體被不同的程式同時申請使用,造成資料干涉衝突,同樣會引起程式的崩潰。不合理的堆記憶體申請和釋放還會造成記憶體的碎片,使得記憶體的利用率和程式的效能大大降低。

V處理堆和棧的方法

出於效能考慮,V會盡量將物件放在棧上,但是在明顯需要的時候在堆上分配它們。例子:

//聲明瞭一個v的複合資料結構:struct,名稱為MyStruct,該結構的成員是n,型別是int(32位有符號整數)。

//宣告本身並不會進行記憶體的分配

struct MyStruct {

n int

}

//聲明瞭一個結構RefStruct,成員r的型別是結構MyStrunct的應用,用&表示引用。被引用的物件必須分配在堆上面。

struct RefStruct {

r &MyStruct

}

//main()是v的入口函式

fn main() {

//呼叫函式(),該函式有兩個返回值

q, w := f()

//列印輸出

println(‘q: $q。r。n, w: $w。n’)

}

//函式f()的定義和函式體

fn f() (RefStruct, &MyStruct) {

//初始賦值語句會分配記憶體給變數a

a := MyStruct{

n: 1

}

b := MyStruct{

n: 2

}

c := MyStruct{

n: 3

}

e := RefStruct{

r: &b

}

x := a。n + c。n

println(‘x: $x’)

return e, &c

}

這裡a被儲存在棧上,因為它的地址從未離開函式f() 。但是對b 的引用是返回的e的一部分,也引用了返回的c,因此, b 和c 將被堆分配。

手動控制堆的分配

V用編譯器指令強制將結構資料分配到堆上面:

[heap]

heap

struct

MyStruct

{

n

int

}

fn

main

()

{

m

:=

MyStruct

{}

//宣告r為可變變數

mut

r

:=

RefStruct

{

r

&

m

}

r

g

()

println

r

$

r

}

//定義函式g(),該函式的接收者為結構RefStruct的變數

fn

mut

r

RefStruct

g

()

{

s

:=

MyStruct

{

n

7

}

r

f

&

s

`

}

fn

mut

r

RefStruct

f

s

&

MyStruct

{

r

r

=

s

}

如果不用[heap]宣告,上面的程式碼中宣告r。r=s語句就會出現編譯錯誤。這裡宣告struct MyStruct 的[heap]屬性是解決這種困境的方法,它指示編譯器

總是

分配物件MyStruct 在堆上。這樣,即使在函式方法 g() 返回之後,對變數s 的引用仍然有效。編譯器考慮到MyStruct 物件總是分配在堆上,在檢查 f() 時分配,並允許將對s 的引用賦值給r。r欄位。

小 結

記憶體棧和堆的管理方法和開銷是不同的

複雜和資料量大的資料結構應分配在堆上

分配在堆上的物件可以被引用,或者說被引用的物件必須分配在堆上

v可以用編譯器指令強制分配資料物件在堆上。

參考文獻

Vlang文件

https://

github。com/vlang/v/blob

/master/doc/docs。md