- 博主简介:努力学习的大一在校计算机专业学生,热爱学习和创作。目前在学习和分享:数据结构、Go,Java等相关知识。
- 博主主页: @是瑶瑶子啦
- 所属专栏: Go语言核心编程
- 近期目标:写好专栏的每一篇文章
一、Go语言的诞生
Go 语言从 2009 年 9 月 21 日开始作为谷歌公司 20% 兼职项目,即相关员工利用 20% 的空余时间来参与 Go 语言的研发工作。
谷歌的“20%时间”工作方式,允许工程师拿出20%的时间来研究自己喜欢的项目。语音服务Google Now、谷歌新闻Google News、谷歌地图Google Map上的交通信息等,全都是20%时间的产物。
该项目的三位领导者均是著名的 IT 工程师:
- Robert Griesemer,参与开发 Java HotSpot 虚拟机;
- Rob Pike,Go 语言项目总负责人,贝尔实验室 Unix 团队成员,参与的项目包括 Plan 9,Inferno 操作系统和 Limbo 编程语言;
- Ken Thompson,贝尔实验室 Unix 团队成员,C 语言、Unix 和 Plan 9 的创始人之一,与 Rob Pike 共同开发了 UTF-8 字符集规范。
二、Why?——为什么需要Go
存在即合理,新事物的产生有它的道理
在了解为什么我们需要Go之前,我们先了解现有语言可以大致分为两大类:
2.1:静态类型语言、动态类型语言
首先,语言根据静态和动态类型分为以下两类分为以下两类:
-
静态类型语言(Statically Typed Language(强类型语言,编译语言)
- 💡简介 :在程序编译时变量的数据类型就被确定。且大多数静态语言在编写代码时,都要求声明变量的数据类型
- 💡特性 :将所有代码编译(***pile)完成,生成可执行文件,再执行。
- 💡优点 :高效: 运行速度快,便于调试,类型安全,结构规范
- 💡缺点 :每次改动都需要重新编译,开发和学习效率低
- 💡代表语言 :C,C++,Java(半编译,半解释),Pascal
👁️🗨️总结:静态类型语言,如C++,执行效率高,但编译、学习、开发效率低
-
动态语言(弱类型语言
- 💡简介 :程序在运行的时候可以改变其结构:新的函数可以被引进,已有的函数可以被删除等在结构上的变化,判断变量的类型是在程序运行的时候完成的
- 💡特性: 变量声明时无需指定类型,会在该变量第一次将数据赋给该变量时,在内部记下该数据类型。将代码按行解释(launch)并允许。
- 💡优点: 代码简洁(不需要写非常多的类型相关的代码)、可以把更多精力放在业务逻辑的处理上(完成相同逻辑的程序,使用ruby或者Python类动态语言所需要的代码量只有Java实现版本的1/10,相比C/C++差距更大)、更加灵活、易于开发、学习、当有改动时无需重新编译,直接解释运行
- 💡缺点 :不方便调试,命名不规范导致可读性差、运行速度慢
- 💡代表语言 :PHP、Python、JavaScript
👁️🗨️总结:学习、开发效率高,执行效率低
补充:之前学习Java的时候,有一个概念是多态,其实本质就是一个变量的不同形态(类型),像静态语言,不能给变量赋予不同类型的值,需要采取:继承、接口等方式实现多态。而动态语言采用dynamic typing,就是说变量在运行期间是可变的,意味着对象的多态性是与生俱来的
2.2:Go——平衡语言差别
根据上面分析,
- 传统语言如静态语言C++,学习和开发效率低(命名规范等等,需要花很多时间取学习,才能上手一个程序来写个程序实现某个功能),同时编译花费时间长,编译效率低(编写——>编译——>运行这个完成这个链条的周期较长)
- 动态语言,比如Python,没有强类型的约束,很多问题在程序运行起来才发现,虽然编写——>运行,节省了静态语言编译效率低的问题,但是这种类型相关的低级错误,应该交给编译器来发现
总之,简单来说,在Go之前,没有语言可以兼顾执行效率、开发效率
当然,这只是Go诞生的其中一点
下面列举Goggle公司创造Go语言的原因:
2.3:为什么Google创造了Go
- 硬件技术更新快,机器性能大幅提高。目前主流的编程语言落后与硬件,不能合理利用多核多CPU的优势提高软件性能
- 现有编程语言:风格不统一、技术能力不够、处理大并发不够好。且软件系统复杂度高,数以万计的代码,维护成本高,需要足够简洁高效的编程语言,提高开发效率,降低运维成本
- c/c++项目,虽然运行速度快,但编译速度慢,且存在内存泄漏的问题。
- 部署平台各式各样,交叉编译难
- 在执行效率、开发效率上达到平衡
🤦🏻♀️为了解决以上问题,Goggle公司就自己造了个轮子——GO
三、Go的优点
💡Go的主要优点
- 编译速度快
- 执行速度快
- 内存管理
- 并发编程(多核CPU
3.1:Go编译快的原因
👉Go的编译器:
最初设计团队的想法是实现Go语言编译的前端,是基于C的g***的编译器来编译成机器代码。这个前端编译器是目前Go的编译器之一:g***Go
- 上面一行是“编译器前端”组成部分
- 下面一行是“优化”+“编译器后端”组成部分
在Go的1.5中,Go团队使用Go编写了Go的编译器(叫作自举),对比与g***go:1,提高了编译速度,虽然执行速度略有下降;3,增加可编译平台
👁️🗨️C++为什么慢?
C++是也可以用g***编译器编译的,所以从编译器角度来说,应该不是编译器使Go更快。很大程度是是Go语言的设计本身
⚠注意:这里我们比较编译速度都是比较的静态编译速度
🫐 Go语言·默认·编译方式是·静态编译·不依赖任何动态链接库,可以避免各种动态链接库依赖的问题;编译好后,只要平台是一致的(linux amd64),就可以任意部署,完全不用考虑链接库依赖问题;
💁🏻♂️静态编译&动态编译:
-
💙静态编译
-
简介: 编译器在编译可执行文件时,会把该文件中使用到的链接库提取出来,链接打包进可执行文件中。编译之后的结果只生成一个可执行文件。
-
优点:不需要管理不同版本库的兼容问题
-
-
🧡动态编译
- 简介: 可执行文件需要附带独立的库文件,不打包库到可执行文件中,减少可执行文件体积,编译完成,程序执行时,执行到了,再从库里面调用。
- 优点:减少内存和存储占用(不同程序共享一个库)
将外部代码嵌入到当前生成的可执行文件中的就是静态编译,以跳转的方式引用外部代码的就是动态编译
在说回C++为什么慢?
- include头文件
- 模板编译
关于模板编译:首先,C++的模板使用,是为了支持泛型编程(这篇文章说的很清楚,这里就不重复造轮子了)。可以看到。编写不同类型的泛型函数,对于编码者来说,确实很方便,但是从那篇文章中,也有可能给编译器增加不必要的编译负担:1,代码膨胀,导致编译时间变长,2,容易出现模板编译错误,且错误信息不易定位
💁🏻♂️优化:
-
基于C/C++慢的第一个引入头文件的问题,后生的语言大部分采用
import module
代替include 头文件
的方式——Go采用的也是import module
方式(下面是基于C的几种引入头文件的对比)-
#include
: include做的事情其实就是简单的复制粘贴,将目标.h文件中的内容一字不落地拷贝到当前文件中,并替换掉这句include。 -
#import:
实质上做的事情和#include是一样的,只不过OC为了避免重复引用可能带来的编译错误(这种情况在引用关系复杂的时候很可能发生,比如B和C都引用了A,D又同时引用了B和C,这样A中定义的东西就在D中被定义了两次,重复了),而加入了#import,从而保证每个头文件只会被引用一次。
如果想深究,import的实现是通过#ifndef一个标志进行判断,然后在引入后#define这个标志,来避免重复引用的 -
PCH(Pre***piled Header):
实质上import也还是拷贝粘贴,这样就带来一个问题:当引用关系很复杂,或者一个头文件被非常多的实现文件引用时,编译时引用所占的代码量就会大幅上升(因为被引用的头文件在各个地方都被copy了一遍)。
为了解决这个问题,C系语言引入了预编译头文件(Pre***piled Header),将公用的头文件放入预编译头文件中预先进行编译,然后在真正编译工程时再将预先编译好的产物加入到所有待编译的Source中去,来加快编译速度。 -
import Modules:
理论上说,想要提高编译速度,可以把所有头文件引用都放到pch中。但是这样面临的问题是在工程中随处可用本来不应该能访问的东西,而编译器也无法准确给出错误或者警告,无形中增加了出错的可能性。Modules相当于将框架进行了封装,然后加入在实际编译之时加入了一个用来存放已编译添加过的Modules列表。如果在编译的文件中引用到某个Modules的话,将首先在这个列表内查找,找到的话说明已经被加载过则直接使用已有的,如果没有找到,则把引用的头文件编译后加入到这个表中。这样被引用到的Modules只会被编译一次,但是在开发时又不会被意外使用到,从而同时解决了编译时间和引用泛滥两方面的问题。
-
-
基于第二个问题,Go根本没有将泛型编程纳入设计框架,根本不存在模板编译带来的开销。(不过Go1.18中已经支持泛型了)
3.1.2👩💻总结
Go编译速度快的几个原因
- 使用
import
引用管理方式 - 没有模板编译负担
- 1.5版本后编译器的优化
- 语言本身关键字很少
3.2:Go的性能
Go的执行速度,可以参考一个语言性能测试数据网站👉 he ***puter Language Benchmarks Game。
- Go的性能接近C++
- 算法时间花销和Java接近,但内存花销远低于Java
- 和python 对比,时间和内存开销都优秀
3.3:并发编程
上面我们提到,硬件技术更新快,机器性能大幅提高(eg:多核CPU)。Go语言设计其实初衷也是为了追平编程语言和硬件之间的差距。Go很多特性都是为了并发而生(一些语言也支持并发,但是是一些后期库支持并发和第三方库支持并发,这和Go天生并发的设计是不同的)
-
Go的并发量可以比大部分语言里普通的线程实现要
这受益于轻量级的Goroutine,轻量化主要是它所占用的空间要小得多,例如64位环境下的JVM,它会默认固定为每个线程分配1MB的线程栈空间,而Goroutines大概只有4-8KB,之后再按需分配。足够轻量化的线程在相同的内存下也就可以有更高并发量(服务器CPU还没有饱和的情况下),同时也可以减少很多上下文切换的时间开销。但是如果你的每个线程占用空间都非常大时(比如10MB,当然这是非常规需求的情况下),Go的轻量化优势就没有那么明显了。
-
语言设计上支持了并发
3.3:垃圾回收(GC)
垃圾回收(英语:Garbage Collection,缩写为GC),在计算机科学中是一种自动的存储器管理机制。当一个计算机上的动态存储器不再需要时,就应该予以释放,以让出存储器,这种存储器资源管理,称为垃圾回收。垃圾回收器可以让程序员减轻许多负担,也减少程序员犯错的机会
在使用Go或者其他有垃圾回收机制的语言(如Java),无需像C/C++一样进行手动释放内存空间(free()/delete
)
- 优点:方便
- 限制:
- 暗箱操作的不透明性
- GC处理的性能开销
- 对于性能要求高的场景,需要进行主动释放和优化(eg:自定义内存池)
如果对你有帮助还请:点赞+关注+收藏
如果文中有不足还请多多指出
关注我,一起学习Golang!