golang

golang逃逸分析

蓝咒 提交于 2020-03-25 21:25:10
带GC语言给我们程序的编写带来了极大的便利,但是与此同时屏蔽了很多底层的细节,比如一个对象是在栈上分配还是在堆上分配。对于普通的代码来说虽然不需要关心这么多,但是作为强迫症程序猿,还是希望能让自己写出来的代码性能最优,所以还是需要了解什么是逃逸,以及如何判断是否发生了逃逸。 什么是堆和栈? 首先需要知道,我们说的堆和栈是啥。这个可不是数据结构里面的"堆"和"栈",而是操作系统里面的概念。 栈 在程序中,每个函数块都会有自己的内存区域用来存自己的局部变量(内存占用少)、返回地址、返回值之类的数据,这一块内存区域有特定的结构和寻址方式,大小在编译时已经确定,寻址起来也十分迅速,开销很少。这一块内存地址称为栈。栈是线程级别的,大小在创建的时候已经确定,所以当数据太大的时候,就会发生"stack overflow"。 堆 在程序中,全局变量、内存占用大的局部变量、发生了逃逸的局部变量存在的地方就是堆,这一块内存没有特定的结构,也没有固定的大小,可以根据需要进行调整。简单来说,有大量数据要存的时候,就存在堆里面。堆是进程级别的。当一个变量需要分配在堆上的时候,开销会比较大,对于go这种带GC的语言来说,也会增加gc压力,同时也容易造成内存碎片。 为什么有的变量要分配在堆,有的要分配在栈? 这个问题要从C++说起了。在C++中,假设我们有以下代码: ```c++ int* f1() { int

golang堆排序

▼魔方 西西 提交于 2020-03-25 01:52:00
package main import "fmt" func main() { arr := []int{6, 1, 2, 7, 9, 3, 4, 5, 10, 8} heapSort(arr) fmt.Println("---") fmt.Println(arr) } //堆排序 func heapSort(arr []int) { //求数组长度 //根据堆的规律,假设子节点的规律,假设子节点的坐标为i //左子节点坐标为2 i+1,右子节点坐标为2 i+2 //父节点的坐标为(i-1)/2. 此处可以计算无论最后一位数字在做左子节点,还是右子节点。父节点的坐标一定是(i-1)/2。 golang中/取整 //假设切片长度是len(arr),那么最后一位的坐标序号为len(arr)-1,可计算出父节点的位置为(len(arr)-1)/2 length := len(arr) last_node := length - 1 //建立最大堆,最大堆的概念就是父节点总是比子节点数字大。arr[0]最大 buildMaxheap(arr) //此处的含义是将堆的堆首与堆尾交换的过程,即为将最大值换到最后,最小值放到最先,然后再对arr[:n-1}执行此递归的过程 //比如 312 -> 21 3-->1 2 3=123 for i := last_node; i > 0; i-- {

golang sqlx与db.v3性能对比

不打扰是莪最后的温柔 提交于 2020-03-24 10:58:43
3 月,跳不动了?>>> 最近跟一个大佬交流的时候提到 SQL 查询的问题。 他表达的思路是不用 ORM ,用最原始的数据库驱动去查询数据。这样做的收益有两个方面 清晰表达 SQL 语义,方便后期优化。特别在报表、数据分析等复杂 SQL 撒上。 减少无用封装,提升程序性能。 我起初没有什么感触,毕竟 ORM 对程序员友好的,用了我会更轻松,但我并不了解具体差异。于是自己做了一个简单的测试,分别使用 sqlx 和 db.v3 (可能很多人没有听说过这个 DB 包,完全是个人喜好)做了性能测试。 准备地区表 jz_places ,只有3600条数据 CREATE TABLE `jz_places` ( `placeid` mediumint(8) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, `sx` mediumint(8) unsigned NOT NULL DEFAULT '0', `order` mediumint(8) unsigned NOT NULL DEFAULT '0', `updatetime` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, `createtime` timestamp NOT NULL

Golang之文件操作

无人久伴 提交于 2020-03-22 11:50:04
3 月,跳不动了?>>> 1. 相关api介绍 建立File内存地址api //返回File的内存地址,错误信息,通过os库调用 func Create(name string) (file *File, err Error) //返回文件的内存地址,通过os库调用 func NewFile(fd int, name string) *File 打开文件api //返回File的内存地址,错误信息,通过os库调用 func Open(name string) (file *File, err Error) //返回File的内存地址,错误信息,通过os库调用 func OpenFile(name string, flag int, perm unit32) (file *File, err Error) 写文件api //写入一个slice,返回写的个数,错误信息,通过File的内存地址调用 func (file *File) Write(b []byte) (n int, err Error) //从slice的某个位置开始写入,返回写的个数,错误信息,通过File的内存地址调用 func (file *File) WriteAt(b []byte, off int64) (n int, err Error) //写入一个字符串,返回写的个数,错误信息,通过File的内存地址调用

Golang 完成一个Crontab定时器(2)

一世执手 提交于 2020-03-22 10:56:00
前言 上篇文章,大概讲了一下robfig/cron 包的使用,怎么开始一个定时任务,那个东西比较简单,也就是调用函数而已,人家都给你把包都封装好了。鉴于上一章我没提到cron相关,这一章专门我写个cron相关,讲讲怎么cron语法,然后再实现一个自动生成cron语句的逻辑。 需求分析 cron的基础科普 根据时间自动生成可用的cron语句 Cron表达式的基础 Go的Cron和linux的Cron的区别就是,linux只到分钟,但是Go的Cron可以通过我上一节描述的代码设置精确到秒。所以一般的Cron表达式就是 * * * * * * command 可以看出来,这是一个时间集合,但是其中每个 * 代表什么含义呢?下面给出Golang的cron设置表 字段 需要的值 字符表示 秒 0-59 * / , - 分 0-59 * / , - 时 0-23 * / , - 日 1-31 * / , - 月 1-12 * / , - 星期 0-6 * / , - 下面举几个cron的具体例子 每秒执行一次任务 * * * * * * Command 每分钟执行一次任务 * */1 * * * * Command 每天12点执行一次任务 * 0 12 * * * Command 每个月1号12点执行一次任务 * 0 12 1 * * Command 2月14号12点执行一次任务(执行一次)

[golang] channel通道

时光总嘲笑我的痴心妄想 提交于 2020-03-21 04:29:53
说明 channel是go当中的一个核心类型,可以看做是管道。并发核心单元可以通过channel进行数据的发送和接收,从而实现通信。 在go中,channel是一种数据类型,主要被用来解决协程的同步问题以及协程之间数据共享(数据传递)的问题。 go当中的goroutine运行在相同的地址空间,因此访问共享内存地址必须做好同步,goroutine奉行通过通信来共享内存,而不是共享内存来通信。 引用类型channel可用于多个goroutine通讯,在其内部实现了同步,确保并发安全。 定义channel变量 在go中,channel也一个对应make创建的底层数据结构的 引用 。 当我们复制一个channel或者用于函数参数传递的时候,我们只是拷贝了一个channel引用,因此调用者和被调用者将引用同一个channel对象,和其他的引用类型一样,channel的零值也是nil。 定义一个channel的时候,也需要定义发送到channel的值的类型,channel可以使用内置的make函数来进行创建。 chan是创建channel使用的关键字,Type代表着channel收发数据的类型。 make(chan Type) // 等价于make(chan Type,0) make(chan Type,capacity) 当 参数capacity= 0 时,channel 是无缓冲阻塞读写的

[golang]go并发goroutine

匆匆过客 提交于 2020-03-21 01:07:41
go 并发 说明 有人把go比作是21世纪的c语言,第一是因为go的设计比较简单,第二,21世纪最重要的就是并发程序设计,而go从语言层面就支持并发。 与此同时,并发程序的内存管理是非常复杂的,而在go中提供了垃圾回收的机制。 Go语言为并发编程而内置的上层API基于顺序通信进程模型CSP(communicating sequential processes)。这就意味着显式锁都是可以避免的,因为Go通过相对安全的通道发送和接受数据以实现同步,这大大地简化了并发程序的编写。 Go语言中的并发程序主要使用两种手段来实现。goroutine和channel。 goroutine goroutine是go并发编程的核心,说到底,goroutine就是协程,比线程更小,go语言在内部帮忙实现了goroutine之间的内存共享。执行goroutine只需要极少的栈内存(大概4-5kb),当然会根据相应的数据伸缩。也正因为如此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。 一般情况下,一个普通计算机跑几十个线程就有点负载过大了,但是同样的机器却可以轻松地让成百上千个goroutine进行资源竞争。 使用 想要创建一个goroutine,只需要在普通函数的前面加一个go关键字,就可以创建并发执行单元。 在并发编程中,我们通常想将一个过程切分成几块

golang动画等待计算菲波那契结果

倾然丶 夕夏残阳落幕 提交于 2020-03-20 12:31:03
一个小玩意,main goroutine将计算菲波那契数列的第45个元素值。由于计算函数使用低效的递归,所以会运行相当长时间,在此期间我们想让用户看到一个可见的标识来表明程序依然在正常运行,所以来做一个动画的小图标: package main import ( "fmt" "time" ) func main() { go spinner(100 * time.Millisecond) const n = 45 fibN := fib(n) // slow fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN) } func spinner(delay time.Duration) { for { for _, r := range `-\|/` { fmt.Printf("\r%c Caculating...", r) time.Sleep(delay) } } } func fib(x int) int { if x < 2 { return x } return fib(x-1) + fib(x-2) } 来源: https://www.cnblogs.com/janeysj/p/12530702.html

多环境下的配置管理方案

我只是一个虾纸丫 提交于 2020-03-20 12:22:09
3 月,跳不动了?>>> 原文发布在 博客 在开发中,我们需要面对各种各样的环境,开发环境、测试环境、生产环境…… 并且,各个环境的参数和配置各不相同,比如数据库连接,服务器配置等。我们怎样在不同环境中调用正确的配置? 通过配置文件 这是一种常见的思路,通过创建多个配置文件,但根据命名区分,比如开发环境为develop-app.conf,测试环境为testing-app.conf,生产环境为production-app.conf 我们通过在系统中设置环境变量export ENV_MODE=develop等等。在读取配置文件时,根据环境变量读取响应的配置文件。 这个方式易于使用,深得大家喜爱。但这个方案在集群扩大的一定程度时,会遇到一下几个主要问题: 假如有30~40个微服务需要连接数据库运行,这个量级在中小型团队中很常见了,如果我们需要更改数据库密码,我们不得不将数十个project逐个进行更新,非常不灵活。 代码与配置掺杂在一起,代码是许多开发人员都可以看到的,也很容易泄露,而生产环境的各种秘钥应该只有少数人有权限能看到。这对系统的安全有重大影响。 对于大量相同的配置(比如数据库配置),逻辑上我们应该存放在同一个地方,保证只有唯一可靠的数据来源。 对于这些问题,我们认为配置应该集中化管理。 集中化管理带来以下好处: 各个服务间相同的配置只需要维护一分数据,保证唯一性

golang for循环中使用 goroutine 产生的问题

一笑奈何 提交于 2020-03-20 10:29:18
3 月,跳不动了?>>> 最近在开发过程中遇到问题,追踪了很久后发现是golang的经典问题,在for循环中使用了goroutine,在goroutine中使用了for循环的参数。 问题现象: 在使用rabbitmq进行数据传递时,发送端在一次循环中发送了8000条id不同的数据到rabbitmq的队列中,接收端监听该队列并从rabbitmq中取数据。接收到的数据在程序中处理后写入数据库,结果发现数据中并没有写入8000条数据。最后定位原因为:在接收数据时在for循环中使用go协程,导致同时收到两条数据时,协程都是使用的后一条数据,入库因为是同一条数据,导致主键重复,插入失败,所以数据库中没有8000条数据。错误代码大致如下: 1 2 3 4 5 for d := range msgs { go func () { handler(d) }() } 用一个简单的程序模拟该错误为: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 package main import ( "fmt" "time" ) func main () { for i := 0; i < 10; i++ { go func () { fmt.Println(i) }() } time.Sleep( 2 * time.Second) } 输出为: 7 10 10 10 10 10 10 10