Go 语言中的 make 和 new

2019 年 12 月 03 日

Go 语言中的 make 和 new

4.5 make 和 new


当我们想要在 Go 语言中初始化一个结构时,其实会使用到两个完全不同的关键字,也就是 makenew,同时出现两个用于『初始化』的关键字对于初学者来说可能会感到非常困惑,不过它们两者有着却完全不同的作用。


在 Go 语言中,make 关键字的主要作用是初始化内置的数据结构,也就是我们在前面提到的 数组和切片哈希表Channel,而当我们想要获取指向某个类型的指针时其实可以使用 new 关键字,只是知道如何使用 new 的人真的比较少,我们在这一节中就会介绍 makenew 它们的区别以及实现原理。


__1. 概述


虽然 makenew 都是能够用于初始化数据结构,但是它们两者能够初始化的结构类型却有着较大的不同;make 在 Go 语言中只能用于初始化语言中的基本类型:


slice := make([]int, 0, 100)hash := make(map[int]bool, 10)ch := make(chan int, 5)
复制代码


这些基本类型都是语言为我们提供的,我们在前面的章节中其实已经介绍过了它们初始化的过程以及原理,但是在这里还是需要提醒各位读者注意的是,这三者返回了不同类型的数据结构:


  1. slice 是一个包含 datacaplen结构体

  2. hash 是一个指向 hmap 结构体的指针

  3. ch 是一个指向 hchan 结构体的指针


而另一个用于初始化数据结构的关键字 new 的作用其实就非常简单了,它只是接收一个类型作为参数然后返回一个指向这个类型的指针:


i := new(int)
var v inti := &v
复制代码


上述代码片段中的两种不同初始化方法其实是等价的,它们都会创建一个指向 int 零值的指针。



到了这里我们对 Go 语言中这两种不同关键字的使用也有了一定的了解:make 用于创建切片、哈希表和管道等内置数据结构,new 用于分配并创建一个指向对应类型的指针。


__2. 实现原理


接下来我们将分别介绍 makenew 在初始化不同数据结构时的具体过程,我们会从编译期间和运行时两个不同的阶段理解这两个关键字的原理,不过由于前面已经详细地介绍过 make 的实现原理,所以我们会将重点放在 new 上从 Go 语言的源代码层面分析它的实现。


__2.1. make


在前面的章节中我们其实已经谈到过 make 在创建 数组和切片哈希表Channel 的具体过程,所以在这一小节中,我们也只是会简单提及 make 相关的数据结构初始化原理。



在编译期间的 类型检查 阶段,Go 语言其实就将代表 make 关键字的 OMAKE 节点根据参数类型的不同转换成了 OMAKESLICEOMAKEMAPOMAKECHAN 三种不同类型的节点,这些节点最终也会调用不同的运行时函数来初始化数据结构。


__2.2. new


内置函数 new 会在编译期间的 SSA 代码生成 阶段经过 callnew 函数的处理,如果请求创建的类型大小时 0,那么就会返回一个表示空指针的 zerobase 变量,在遇到其他情况时会将关键字转换成 newobject


func callnew(t *types.Type) *Node {    if t.NotInHeap() {        yyerror("%v is go:notinheap; heap allocation disallowed", t)    }    dowidth(t)
if t.Size() == 0 { z := newname(Runtimepkg.Lookup("zerobase")) z.SetClass(PEXTERN) z.Type = t return typecheck(nod(OADDR, z, nil), ctxExpr) }
fn := syslook("newobject") fn = substArgTypes(fn, t) v := mkcall1(fn, types.NewPtr(t), nil, typename(t)) v.SetNonNil(true) return v}
复制代码


需要提到的是,哪怕当前变量是使用 var 进行初始化,在这一阶段可能会被转换成 newobject 的函数调用并在堆上申请内存:


func walkstmt(n *Node) *Node {    switch n.Op {    case ODCL:        v := n.Left        if v.Class() == PAUTOHEAP {            if prealloc[v] == nil {                prealloc[v] = callnew(v.Type)            }            nn := nod(OAS, v.Name.Param.Heapaddr, prealloc[v])            nn.SetColas(true)            nn = typecheck(nn, ctxStmt)            return walkstmt(nn)        }    case ONEW:        if n.Esc == EscNone {            r := temp(n.Type.Elem())            r = nod(OAS, r, nil)            r = typecheck(r, ctxStmt)            init.Append(r)            r = nod(OADDR, r.Left, nil)            r = typecheck(r, ctxExpr)            n = r        } else {            n = callnew(n.Type.Elem())        }    }}
复制代码


当然这也不是绝对的,如果当前声明的变量或者参数不需要在当前作用域外『生存』,那么其实就不会被初始化在堆上,而是会初始化在当前函数的栈中并随着 函数调用 的结束而被销毁。


newobject 函数的工作就是获取传入类型的大小并调用 mallocgc 在堆上申请一片大小合适的内存空间并返回指向这片内存空间的指针:


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


mallocgc 函数的实现大概有 200 多行代码,在这一节中就不展开详细分析了,我们会在后面的章节中详细介绍 Go 语言的内存管理机制。


__3. 总结


到了最后,简单总结一下 Go 语言中 makenew 关键字的实现原理,make 关键字的主要作用是创建切片、哈希表和 Channel 等内置的数据结构,而 new 的主要作用是为类型申请一片内存空间,并返回指向这片内存的指针。


__4. Reference



__5. 其他


__5.1. 关于图片和转载


文章未经许可均禁止转载,图片在使用时请保留图片中的全部内容,可适当缩放并在引用处附上图片所在的文章链接,图片使用 Sketch 进行绘制。


**本文转载自 Draveness 技术博客。


原文链接:https://draveness.me/golang/keyword/golang-make-and-new.html#45-make-和-new


2019 年 12 月 03 日 15:1075

评论

发布
暂无评论
发现更多内容

总结-05-缓存

梦子说

学习

架构师训练营 - 第五周总结

teslə

架构师训练营第五周作业

极客大学架构师训练营

【架构师训练营】第五期作业

云064

vue3.0 全局API的变化

志学Python

Vue Vue3

架构师训练营第五周总结

Geek_2dfa9a

第5周总结

ruettiger

架构师训练营 -Week 05 学习总结

华乐彬

学习 极客大学架构师训练营

Week5

丿淡忘

科技为翼,让普惠金融拂过烟火人间

脑极体

Week 05学习总结

Jeremy

产学研结合打牢技术基础为“数字云南”建设赋能

CECBC区块链专委会

架构师训练营Week5命题作业

小高

第五周作业

数字

Week 5 作业

Jeremy

架构师训练营(第 5 周作业)

李德政

极客大学架构师训练营

架构师训练营-week5

强哥

极客大学架构师训练营

极客大学架构师训练营第五周学习总结

竹森先生

架构技术选型一(总结)

小叶

极客大学架构师训练营

第5周作业

技术选型(上)

eazonshaw

极客大学架构师训练营

一致性hash算法

WW

一致性hash算法-java

jason

架构师训练营 -W5-20200708

丁亚宁

极客大学架构师训练营 一致性hash

Week 004 总结

徐培

Week 004 作业

徐培

【架构师训练营 - week5 -1】作业

早睡早起

技术选型 一

_MISSYOURLOVE

极客大学架构师训练营

Python实现二叉树的各种遍历

王坤祥

Python 二叉树

第五周总结

changtai

极客大学架构师训练营

第五周作业 - 命题作业

molly

极客大学架构师训练营

Go 语言中的 make 和 new-InfoQ