最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Golang中的new()和make()函數(shù)本質(zhì)區(qū)別

 更新時間:2025年02月19日 09:44:45   作者:水草  
在 Go 語言開發(fā)中,new() 和 make() 是兩個容易讓開發(fā)者感到困惑的內(nèi)建函數(shù),盡管它們都用于內(nèi)存分配,但其設(shè)計目的、適用場景和底層實現(xiàn)存在本質(zhì)差異,本文將通過類型系統(tǒng)、內(nèi)存模型和編譯器實現(xiàn)三個維度,深入解析這兩個函數(shù)的本質(zhì)區(qū)別,感興趣的朋友一起看看吧

在 Go 語言開發(fā)中,new() 和 make() 是兩個容易讓開發(fā)者感到困惑的內(nèi)建函數(shù)。盡管它們都用于內(nèi)存分配,但其設(shè)計目的、適用場景和底層實現(xiàn)存在本質(zhì)差異。本文將通過類型系統(tǒng)、內(nèi)存模型和編譯器實現(xiàn)三個維度,深入解析這兩個函數(shù)的本質(zhì)區(qū)別。

一、類型系統(tǒng)的哲學分野

1.1 new() 的通用性設(shè)計

new(T) 是為所有類型設(shè)計的通用內(nèi)存分配器,其行為模式高度統(tǒng)一:

// 為 int 類型分配零值內(nèi)存
pInt := new(int)  // *int 類型
// 為自定義結(jié)構(gòu)體分配內(nèi)存
type MyStruct struct { a int }
pStruct := new(MyStruct) // *MyStruct 類型

其核心特征:

  • 返回類型始終為 *T
  • 分配的內(nèi)存被初始化為類型零值
  • 適用于任何類型(包括基本類型、結(jié)構(gòu)體、數(shù)組等)

1.2 make() 的特化使命

make() 是 Go 為特定引用類型設(shè)計的構(gòu)造器:

// 創(chuàng)建 slice
s := make([]int, 5, 10) 
// 初始化 map
m := make(map[string]int)
// 建立 channel
ch := make(chan int, 5)

關(guān)鍵限制:

  • 僅適用于 slice、map 和 channel 三種類型
  • 返回已初始化的類型實例(非指針)
  • 支持類型特定的初始化參數(shù)

二、內(nèi)存模型的實現(xiàn)差異

2.1 new() 的底層機制

當編譯器遇到 new(T) 時:
1.計算類型大?。簊ize = unsafe.Sizeof(T{})
2.調(diào)用 runtime.newobject 分配內(nèi)存
3.執(zhí)行內(nèi)存清零操作(對應(yīng)零值初始化)
4.返回指向該內(nèi)存的指針

以下偽代碼示意其過程:

func new(T) *T {
    ptr := malloc(sizeof(T))
    *ptr = T{}  // 零值初始化
    return ptr
}

2.2 編譯器前端的語法解析

// 原始代碼片段
type MyStruct struct { a int }
p := new(MyStruct)
// 轉(zhuǎn)換為中間表示 (IR)
ptr := runtime.newobject(unsafe.Pointer(&MyStruct{}))

編譯器會將 new(T) 替換為對 runtime.newobject 的直接調(diào)用,傳遞類型元信息作為參數(shù)。

2.3 進入運行時系統(tǒng)的內(nèi)存分配

runtime.newobject 是 new() 的核心入口,定義于 runtime/malloc.go:

func newobject(typ *_type) unsafe.Pointer {
    return mallocgc(typ.size, typ, true)
}

關(guān)鍵參數(shù)解釋

  • typ.size: 目標類型的大?。ㄓ删幾g器靜態(tài)計算)
  • typ: 指向類型元數(shù)據(jù)的指針(描述內(nèi)存布局)
  • true: 指示是否需要進行清零操作(對應(yīng)零值初始化)

2.4 深入 mallocgc 的內(nèi)存分配流程

mallocgc 是通用內(nèi)存分配函數(shù),負責根據(jù)對象大小選擇不同的分配策略:

微小對象分配(Tiny Allocator)
對于小于 16 字節(jié)的對象:

if size <= maxSmallSize {
    if noscan && size < maxTinySize {
        // 使用 per-P 的 tiny allocator
        off := c.tinyoffset
        if off+size <= maxTinySize && c.tiny != 0 {
            x = unsafe.Pointer(c.tiny + off)
            c.tinyoffset = off + size
            return x
        }
        // ...
    }
}
  • 利用線程本地緩存 (mcache) 提升小對象分配速度
  • 合并多個微對象到一個內(nèi)存塊,減少碎片

常規(guī)對象分配
對于較大的對象,走標準分配路徑:

var span *mspan
systemstack(func() {
    span = largeAlloc(size, needzero, noscan)
})
x = unsafe.Pointer(span.base())
  • 通過 mheap 全局堆管理器申請新的內(nèi)存頁
  • 涉及復雜的空閑鏈表查找和頁面分割算法

2.5 make() 的類型特化處理

編譯器將 make 轉(zhuǎn)換為不同的運行時函數(shù)調(diào)用:

類型內(nèi)部函數(shù)關(guān)鍵參數(shù)
sliceruntime.makeslice元素類型、長度、容量
mapruntime.makemap初始 bucket 數(shù)量
channelruntime.makechan緩沖區(qū)大小

以 slice 為例的底層處理流程:

// 編譯器將 make([]int, 5, 10) 轉(zhuǎn)換為
ptr, len, cap := runtime.makeslice(unsafe.Sizeof(int(0)), 5, 10)
return Slice{ptr: ptr, len: 5, cap: 10}

三、零值 vs 就緒狀態(tài)

3.1 new()零值初始化的實現(xiàn)細節(jié)

new() 返回的指針指向的內(nèi)存會被自動置零:

if needzero {
    memclrNoHeapPointers(x, size)
}
  • memclrNoHeapPointers 是用匯編編寫的快速清零例程
  • 對不同大小的內(nèi)存塊使用 SIMD 指令優(yōu)化清零速度

3.2 new() 的零值困境

雖然 new() 能完成基本的內(nèi)存分配,但對于復雜類型可能產(chǎn)生非預(yù)期結(jié)果:

// 創(chuàng)建 slice 指針
sp := new([]int)
*sp = append(*sp, 1)  // 合法但非常規(guī)用法
(*sp)[0] = 1          // 運行時 panic(索引越界)

此時雖然分配了 slice 頭結(jié)構(gòu)(ptr/len/cap),但:

  • 底層數(shù)組指針為 nil
  • length 和 capacity 均為 0

3.3 make() 的初始化保證

make() 確保返回的對象立即可用:

s := make([]int, 5)
s[0] = 1          // 安全操作
ch := make(chan int, 5)
ch <- 1           // 不會阻塞
m := make(map[string]int)
m["key"] = 1      // 不會 panic

初始化過程包括:

  • 為 slice 分配底層數(shù)組
  • 初始化 map 的哈希桶
  • 創(chuàng)建 channel 的環(huán)形緩沖區(qū)

四、編譯器優(yōu)化策略

4.1 逃逸分析的差異處理

new() 分配的對象可能被分配到棧上:

func localAlloc() *int {
    return new(int)  // 可能進行棧分配
}

編譯器會在編譯期間決定對象是否需要分配到堆上:

// 如果發(fā)生逃逸,生成 runtime.newobject 調(diào)用
if escapeAnalysisResult.escapes {
    call = mkcall("newobject", ...)
} else {
    // 直接在棧上分配空間
}
  • 通過 -gcflags=“-m” 可查看具體逃逸決策
  • 棧分配完全繞過 mallocgc,顯著提升性能

而 make 創(chuàng)建的對象總是逃逸到堆:

func createSlice() []int {
    return make([]int, 10)  // 必須堆分配
}

4.2 初始化優(yōu)化

編譯器會對 new() 后的立即賦值進行優(yōu)化:

p := new(int)
*p = 42
// 優(yōu)化為直接分配已初始化的內(nèi)存

五、典型平臺的匯編輸出驗證

以 AMD64 平臺為例,觀察生成的機器碼:

//go tool compile -S test.go
MOVQ    $type.MyStruct(SB), AX  ;; 加載類型元數(shù)據(jù)
CALL    runtime.newobject(SB)   ;; 調(diào)用分配函數(shù)
  • 類型元數(shù)據(jù)在只讀段存儲,保證多協(xié)程訪問安全
  • 最終調(diào)用約定遵循 Go 特有的 ABI 規(guī)范

六、實踐建議與模式選擇

6.1 選擇決策樹

是否創(chuàng)建引用類型?
├─ 是 → 必須使用 make()
└─ 否 → 是否需要指針?
       ├─ 是 → 使用 new()
       └─ 否 → 使用字面量初始化

6.2 性能考量

對于結(jié)構(gòu)體初始化,推薦直接使用值類型:

// 優(yōu)于 new(MyStruct)
var s MyStruct

當需要明確的指針語義時再使用 new()

6.3 特殊使用模式

組合使用實現(xiàn)延遲初始化:

type LazyContainer struct {
    data *[]string
}
func (lc *LazyContainer) Get() []string {
    if lc.data == nil {
        lc.data = new([]string)
        *lc.data = make([]string, 0, 10)
    }
    return *lc.data
}

七、性能優(yōu)化啟示

1.盡量讓小型結(jié)構(gòu)體留在棧上

  • 控制結(jié)構(gòu)體大小,避免無意識逃逸

2.警惕大對象導致的 GC 壓力

  • 超過 32KB 的對象直接從堆分配

3.批量初始化替代多次 new()

  • 使用對象池或切片預(yù)分配降低開銷

八、從設(shè)計哲學理解差異

Go 語言通過 new 和 make 的分離體現(xiàn)了其類型系統(tǒng)的設(shè)計哲學:

  • 明確性:強制開發(fā)者顯式處理引用類型的特殊初始化需求
  • 安全性:避免未初始化引用類型導致的運行時錯誤
  • 正交性:保持基本類型系統(tǒng)與引用類型系統(tǒng)的隔離

這種設(shè)計雖然增加了初學者的學習成本,但為大型工程提供了更好的可維護性和運行時安全性。

通過對內(nèi)存分配機制、編譯器優(yōu)化策略和語言設(shè)計哲學的分析,我們可以清晰地認識到:new() 是通用的內(nèi)存分配原語,而 make() 是針對引用類型的類型感知構(gòu)造器。理解這一區(qū)別有助于開發(fā)者寫出更符合 Go 語言設(shè)計思想的優(yōu)雅代碼。

到此這篇關(guān)于Golang中的new()和make()函數(shù)的文章就介紹到這了,更多相關(guān)Golang new()和make()函數(shù)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go Slice 實現(xiàn)原理從底層機制到工程實踐詳解

    Go Slice 實現(xiàn)原理從底層機制到工程實踐詳解

    本文將從企業(yè)級開發(fā)視角,深入剖析 Slice的底層實現(xiàn)原理,結(jié)合性能優(yōu)化實踐,幫助開發(fā)者寫出更高效、更安全的 Go 代碼,對go slice實現(xiàn)原理感興趣的朋友跟隨小編一起看看吧
    2025-11-11
  • GO的range具體使用

    GO的range具體使用

    GO語言的for…range 能做什么呢?golang的for…range是go 身的語法,可以用來遍歷數(shù)據(jù)結(jié)構(gòu),本文就詳細的來介紹一下具體使用,感興趣的可以了解一下
    2021-10-10
  • 如何避免Go語言常見錯誤之意外的變量隱藏

    如何避免Go語言常見錯誤之意外的變量隱藏

    在Go語言中,變量隱藏(Variable Shadowing)是一個常見的錯誤來源,變量隱藏發(fā)生在一個內(nèi)部作用域中聲明的變量與外部作用域的變量同名時,這可能導致開發(fā)者無意中使用了錯誤的變量,造成難以追蹤的bug,本文講解一些關(guān)于變量隱藏的常見錯誤和如何避免它們的方法
    2024-01-01
  • 10個現(xiàn)代網(wǎng)站開發(fā)必備的Go軟件包工具盤點

    10個現(xiàn)代網(wǎng)站開發(fā)必備的Go軟件包工具盤點

    這篇文章主要為大家介紹了10個現(xiàn)代網(wǎng)站開發(fā)必備的Go軟件包,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • 詳解如何修改Go結(jié)構(gòu)體的私有字段

    詳解如何修改Go結(jié)構(gòu)體的私有字段

    在 Go 語言中,結(jié)構(gòu)體字段的訪問權(quán)限是由字段名的首字母決定的:首字母大寫表示公共字段(public),首字母小寫表示私有字段(private),本文給大家介紹了如何修改Go結(jié)構(gòu)體的私有字段,需要的朋友可以參考下
    2025-01-01
  • Go語言Grpc?Stream的實現(xiàn)

    Go語言Grpc?Stream的實現(xiàn)

    本文主要介紹了Go語言Grpc?Stream的實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-06-06
  • Golang結(jié)構(gòu)化日志包log/slog的使用詳解

    Golang結(jié)構(gòu)化日志包log/slog的使用詳解

    官方提供的用于打印日志的包是標準庫中的 log 包,該包雖然被廣泛使用,但是缺點也很多,所以Go 1.21新增的 log/slog 完美解決了以上問題,下面我們就來看看log/slog包的使用吧
    2023-09-09
  • Go語言代碼格式化入門及gofmt使用教程

    Go語言代碼格式化入門及gofmt使用教程

    Go官方提供了一個非常有用的工具gofmt來幫助開發(fā)者自動格式化他們的代碼,gofmt不僅可以幫你格式化代碼,還可以檢查一些潛在的代碼風格問題,本文介紹Go語言代碼格式化入門及gofmt使用教程,感興趣的朋友一起看看吧
    2026-07-07
  • 淺析Go語言中數(shù)組的這些細節(jié)

    淺析Go語言中數(shù)組的這些細節(jié)

    這篇文章主要為大家詳細介紹了Go語言中數(shù)組一些細節(jié)的相關(guān)資料,文中的示例代碼講解詳細,對我們學習Go語言有一定的幫助,需要的可以了解一下
    2022-11-11
  • VSCode配置Go插件和第三方拓展包的詳細教程

    VSCode配置Go插件和第三方拓展包的詳細教程

    這篇文章主要介紹了VSCode配置Go插件和第三方拓展包的詳細教程,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-05-05

最新評論

罗山县| 永丰县| 资兴市| 汶川县| 陈巴尔虎旗| 无极县| 浦北县| 卓尼县| 明光市| 安福县| 灵石县| 荥阳市| 新密市| 天台县| 沁水县| 尼木县| 平塘县| 宜君县| 札达县| 仪陇县| 阿克陶县| 大庆市| 石景山区| 辽宁省| 衡山县| 靖宇县| 黔南| 刚察县| 宜川县| 永福县| 蒙阴县| 莲花县| 阿合奇县| 商河县| 科技| 衡南县| 巧家县| 黑龙江省| 司法| 潞城市| 长寿区|