(图片来源网络,侵删)
01Delve是什么Delve是Golang的调试工具,能够在Go程序运行时,提供断点调试功能,查看当前数据,甚至切换到不同协程进行数据查看,大部分的IDE都已经集成了使用上面的功能,其实已经可以很方便的调试Go程序了,但它的功能还不止如此,接下来了解一下其中一个对深入了解Go语言有帮助的功能:内存查看02程序内存程序在运行时,被从硬盘加载到内存中,大概长这个样子其中,程序的代码被编译成了机器码,存在了程序代码区,除此以外,最关注的应该就是栈和堆了栈的地址空间从高向低增长,堆的内存一般向上增长,但是由于Go语言自带了内存管理器,以及由于协程的存在,实际获取到的内存地址可能不完全按照一般情况为了接下来的内容,需要了结一下字节序,字节序一般为 大端存储或者小端存储,大端存储的数据高位保存在内存低地址位,小端存储的数据高位保存在内存高地址位linux系统可以通过lscpu来查看Byte Order字段,大部分Intel和amd的cpu应该都是Little Endian(小端)这意味着,假设一个指针指向的地址是0xc0123456,该指针变量保存的值应该从低地址到高地址分别是 0x56 0x34 0x12 0xc0(此处为了简便,使用了4个字节,实际上64位系统上指针变量应该占据8个字节)03如何使用安装好Delve之后,就可以使用Delve来debug main函数所在的文件了,如:dlv debug main.go,这样就进入到了delve的命令行中然后就可以用b main.go:9 来在第9行设置一个断点,如下图所示此时可以看到,dlv提供了一个地址,前面在这里加了一个断点,当程序运行到这个程序所在地址时,就会暂停可以现在进去看看,这个地址有什么因为已经知道,这个地方是程序代码块的地址,所以需要在此处使用反编译方法,将内存中的机器码转化成汇编语言,而这个也是Delve的功能之一运行:disass -a 0x495c75 0x495d75,从刚刚这个地址开始,往后0x100个字节,将其进行反编译,可以得到类似这样的结果(未截全)这里就是在main函数调用println的汇编代码(一部分)现在继续输入c,Delve会运行至下一个断点处,也就是println之前,这里可以看到a,b这2个变量的一些数据使用p命令可以获取到对应变量的值,如:p a或者p b这正是代码中设置的值,使用p &a和p &b就可以看到对应的地址这里得到了2个变量的地址,这时就可以使用Delve的输出内存原始数据的方法来看看这2个地址存的是什么使用x -fmt hex -len 32 0xc00010cef0,这里x表示输出原始数据,-fmt hex表示已16进制的方式格式化, -len 32表示输出指定地址后的32字节,然后可以得到类似这里每8个字节一行,从地址低位到高位依次排列,由于int型在64位系统上占8字节,所以可以到a所在内存起始位置后的8字节,存的其实是一个变量,而它就是1后面的3行,对于开发者来说,并不知道它属于谁或者表示什么意思,因为内存是由Golang在维护同理,再看看b所在的位置此处第一个8字节,明显并不是abc,而后面8字节正好是3,即字符串的长度,这正好与Golang知识相符:字符串是一串指向连续内存的首位地址和长度组成的所以第一个8字节应该是一个地址,所以再次跟踪过去这样,我就发现该地址上的3字节,正好是ascii的abc所对应的值,而由于刚刚的代码是用utf8写的,而utf8作为变长字符编码格式,兼容ascii,所以abc在utf8下正好就是0x61 0x62 0x63而由于上面看到的长度是3,所以只能读取前3字节,后面的字节还是不知道是什么意思04总结通过一个简单的例子了解了Delve的内存查看相关功能,这个功能可以帮助加深对Golang数据结构的理解,并为优化代码运行速度提供参考例如,在上面的例子说明字符串存在连续内存中,而在它长度后面的内存是未知数据,所以也就是说,如果我想要在这个字符串后面追加字符,不能直接在原来的内存后覆盖掉原来的数据此时Golang必须开辟一块足够大的新内存,然后把当前的字符串复制过去,最后再把需要追加的字符写入进来开辟内存就需要考虑GC,复制也会需要时间所以循环中拼接字符串时,就需要考虑是否会产生多次GC影响执行效率作者:裘铖 来源-微信公众号:三七互娱技术团队出处:https://mp.weixin.qq.com/s/svedLIwIzbwtEEsO7kJqAQ
0 评论