欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

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

1.1 new() 的通用性設計

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

// 為 int 類型分配零值內存
pInt := new(int)  // *int 類型
// 為自定義結構體分配內存
type MyStruct struct { a int }
pStruct := new(MyStruct) // *MyStruct 類型

其核心特征:

  • 返回類型始終為 *T
  • 分配的內存被初始化為類型零值
  • 適用于任何類型(包括基本類型、結構體、數組等)

1.2 make() 的特化使命

make() 是 Go 為特定引用類型設計的構造器:

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

關鍵限制:

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

二、內存模型的實現差異

2.1 new() 的底層機制

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

以下偽代碼示意其過程:

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

2.2 編譯器前端的語法解析

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

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

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

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

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

關鍵參數解釋

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

2.4 深入 mallocgc 的內存分配流程

mallocgc 是通用內存分配函數,負責根據對象大小選擇不同的分配策略:

微小對象分配(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) 提升小對象分配速度
  • 合并多個微對象到一個內存塊,減少碎片

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

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

2.5 make() 的類型特化處理

編譯器將 make 轉換為不同的運行時函數調用:

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

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

// 編譯器將 make([]int, 5, 10) 轉換為
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()零值初始化的實現細節(jié)

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

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

3.2 new() 的零值困境

雖然 new() 能完成基本的內存分配,但對于復雜類型可能產生非預期結果:

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

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

  • 底層數組指針為 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 分配底層數組
  • 初始化 map 的哈希桶
  • 創(chuàng)建 channel 的環(huán)形緩沖區(qū)

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

4.1 逃逸分析的差異處理

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

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

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

// 如果發(fā)生逃逸,生成 runtime.newobject 調用
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)化為直接分配已初始化的內存

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

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

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

六、實踐建議與模式選擇

6.1 選擇決策樹

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

6.2 性能考量

對于結構體初始化,推薦直接使用值類型:

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

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

6.3 特殊使用模式

組合使用實現延遲初始化:

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.盡量讓小型結構體留在棧上

  • 控制結構體大小,避免無意識逃逸

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

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

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

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

八、從設計哲學理解差異

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

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

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

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

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

相關文章

  • GO必知必會的常見面試題匯總

    GO必知必會的常見面試題匯總

    這篇文章主要為大家介紹了GO必知必會的常見面試題匯總
    2022-08-08
  • go語言優(yōu)雅地處理error工具及技巧詳解

    go語言優(yōu)雅地處理error工具及技巧詳解

    這篇文章主要為大家介紹了go語言優(yōu)雅地處理error工具及技巧詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-11-11
  • 詳解Golang中Channel的用法

    詳解Golang中Channel的用法

    如果說goroutine是Go語言程序的并發(fā)體的話,那么channels則是它們之間的通信機制。這篇文章主要介紹Golang中Channel的用法,需要的朋友可以參考下
    2020-11-11
  • GO中?分組聲明與array,?slice,?map函數

    GO中?分組聲明與array,?slice,?map函數

    這篇文章主要介紹了GO中?分組聲明與array,slice,map函數,Go語言中,同時聲明多個常量、變量,或者導入多個包時,可采用分組的方式進行聲明,下面詳細介紹需要的小伙伴可以參考一下
    2022-03-03
  • go單例實現雙重檢測是否安全的示例代碼

    go單例實現雙重檢測是否安全的示例代碼

    這篇文章主要介紹了go單例實現雙重檢測是否安全,本文給大家分享雙重檢驗示例代碼,代碼簡單易懂,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2022-03-03
  • GO語言 復合類型專題

    GO語言 復合類型專題

    這篇文章主要介紹了GO語言 復合類型的的相關資料,文中講解非常細致,代碼幫助大家更好的理解和學習,感興趣的朋友可以了解下
    2020-06-06
  • Golang使用CGO與Plugin技術運行加載C動態(tài)庫

    Golang使用CGO與Plugin技術運行加載C動態(tài)庫

    這篇文章主要介紹了Golang使用CGO與Plugin技術運行加載C動態(tài)庫,Golang?程序在運行時加載C動態(tài)庫的技術,跳過了Golang項目編譯階段需要鏈接C動態(tài)庫的過程,提高了Golang項目開發(fā)部署的靈活性
    2022-07-07
  • go語言reflect.Type?和?reflect.Value?應用示例詳解

    go語言reflect.Type?和?reflect.Value?應用示例詳解

    這篇文章主要為大家介紹了go語言reflect.Type?和?reflect.Value?應用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • Go語言常見錯誤之any沒傳遞任何信息解決分析

    Go語言常見錯誤之any沒傳遞任何信息解決分析

    Go語言,由于其高效強大的并行處理能力和優(yōu)雅簡單的設計哲學,一直以來都是編程世界的寵兒,然而,對于一些Go新手和甚至熟悉Go的程序員也可能會遇到一個常見的錯誤:?any沒傳遞任何信息,那么,如何規(guī)避這個錯誤,本文將揭示其中的秘密
    2024-01-01
  • 基于Go語言實現猜謎游戲

    基于Go語言實現猜謎游戲

    這篇文章主要為大家詳細介紹了如何基于Go語言實現猜謎游戲,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習
    2023-09-09

最新評論