前言
本文为笔者所著 Go 语言基础系列之一,本文笔者将概述 Go 语言最为核心的特性,这些特性也是最为打动笔者的地方;
本文为作者原创作品,转载请注明出处;
编译
程序编译总朗阔为 4 个部分,如下,$$\xrightarrow{Golang源码}\text{语法和词法分析}\xrightarrow{AST树} \text{类型检查}\xrightarrow{合法的AST树} \text{生成中间代码}\xrightarrow{中间代码} \text{生成机器代码}$$
过程中最为出彩的就是通过中间代码
生成机器代码
的这个步骤;我们知道,Java 语言编译得到中间代码.class
文件后,直接交给 Java 虚拟机来处理,由 Java 虚拟机来保证 Java 语言的跨平台执行;但是 Golang 更进一步,直接生成目标机器的机器代码,也就是目标机器的可执行程序,比如 windows 中的 .exe 文件,Linux 中的 .sh 文件等等,这样使得它非常轻量级的实现了语言的跨平台的特性;
不过要能够直接生成目标机器的机器代码也绝非易事,因为不同架构的 CPU 对应着不同的机器指令集,这就使得 Golang 在生成目标机器代码的时候,必须根据不同 CPU 的指令架构生成对应的机器代码;因此,在编译的时候,通常我们需要指定目标机器 CPU 的指令架构,比如,我们需要编译生成 darwin 操作系统并基于 AMD 指令架构的机器代码,1
$ GOARCH=amd64 GOOS=darwin make sample.go
在编译 sample.go 的时候,通过GOARCH
指定 CPU 的指令架构,通过GOOS
指定操作系统或者运行环境;这样,我们就将源码sample.go
生成了目标系统的机器代码;
常见的 CPU 指令架构有,amd64、arm、arm64、mips、mips64、ppc64、s390x、x86 和 wasm 等;
协程
最为打动我的地方就是协程
Goroutine
了,为了避免像传统编程语言直接使用内核线程来实现并发,导致高并发场景下,系统资源的极大浪费的问题(在高并发下因内核线程的频繁切换造成 CPU 上下文的频繁切换,进而导致系统资源的极大浪费),Golang 居然自己创建了一套在用户层的虚拟线程 Goroutine
,它模拟了内核线程的机制,每个Goroutine
拥有自己的栈,堆,来保存自己的临时变量和对象,并且所有的线程切换(挂起、唤醒、执行等)都是在用户层的 Go runtime 中实现的,这样使得 Go 语言在高并发场景下的执行效率非常的高,因为它再也不会直接导致 CPU 进行上下文切换了;
如图,假设,当前我们有 4 个 Goroutine 虚拟线程,单核 CPU;Go runtime 负责调度,将不同的 Goroutine 线程分配到不同的内核线程中去执行,如图,Goroutine A 和 B 分配给了内核线程 1,C 和 D 分配给了内核线程 2;
特点,
- Goroutine 线程之间的切换是在用户层完成的,内核线程和 CPU 无感知,因此 Golang 的并发性非常的好;
- 如果某个 Goroutine 线程的不当操作导致了内核线程被挂起了,Go runtime 可以及时的将其它的 Goroutine 线程调度到另外一个内核线程中去执行,保证 Goroutine 一直在执行中;
综上,Golang 就是一门为并发而生的语言;
垃圾回收器
Golang 的垃圾回收主要使用到了三色标记
法,写屏障
等技术来清理内存;
总结
正是因为 Golang 所独有的跨平台特性以及其强大的协程
,吸引了我,决定开启 Golang 之旅;