返回顶部
首页 > 资讯 > 后端开发 > GO >Golang中运行与Plan9汇编的示例分析
  • 234
分享到

Golang中运行与Plan9汇编的示例分析

2023-06-16 17:06:42 234人浏览 安东尼
摘要

小编给大家分享一下golang中运行与Plan9汇编的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Golang的运行环境当我们把编译后的Go代码运行起来

小编给大家分享一下golang中运行与Plan9汇编的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!

Golang的运行环境

当我们把编译后的Go代码运行起来,它会以进程的方式出现在系统中。然后开始处理请求、数据,我们会看到这个进程占用了内存消耗、cpu占比等等信息。本文就是要来解释在程序的运行过程中,内存、CPU、操作系统(当然还有其它的硬件,文中关系不大,就不说了)是如何进行配合,完成了我们代码所指定的事情。

内存

首先,我们先来说说内存。先来看一个我们运行的go进程。

代码如下:

package main  import (      "fmt"      "log"      "net/Http"  )  func main() {      http.HandleFunc("/", sayHello)      err := http.ListenAndServe(":9999", nil)      if err != nil {          log.Fatal("ListenAndServe: ", err)      }  }  func sayHello(w http.ResponseWriter, r *http.Request) {      fmt.Printf("fibonacci: %d\n", fibonacci(1000))      _, _ = fmt.Fprint(w, "Hello World!")  }  func fibonacci(num int) int {      if num < 2 {          return 1      }      return fibonacci(num-1) + fibonacci(num-2)  }

来看一下执行情况:

dayu.com >ps aux  USER               PID   %CPU  %MEM      VSZ     RSS    TT  STAT   STARTED    TIME     COMMAND  xxxxx              3584  99.2  0.1     4380456  4376   s003  R+    8:33下午   0:05.81  ./myhttp

这里我们先来不关注其它指标,先来看 VSZ 与 RSS。

  •  VSZ: 是指虚拟地址,他是程序实际操作的内存。包含了分配还没有使用的内存。

  •  RSS: 是实际的物理内存,包含了栈内存与堆内存。

每一个进程都是运行在自己的内存沙盒里,程序被分配的地址都是 “虚拟内存”,物理内存对程序开发者来说实际是不可见的,而且虚拟地址比进程实际的物理地址要大的多。我们经常编程中取指针对应的地址实际就是虚拟地址。这里一定要注意区分虚拟内存与物理内存。来一张图感受一下。

Golang中运行与Plan9汇编的示例分析

这张图主要是为了说明两个问题:

  1. 鸿蒙官方战略合作共建——HarmonyOS技术社区

  2.  程序使用的是虚拟内存,但是操作系统会把虚拟内存映射到物理内存;你会发现自己机器上所有进程的VSZ要大得多;

  3.  物理内存可以被多个进程共享,甚至一个进程内的不同地址可能映射的都是同一个物理内存地址。

上面搞明白了程序中的内存具体是指什么,接下来说明程序是如何使用内存的(虚拟内存),内存说白了就是比硬盘存取速度更快的一个硬件,为了方便内存的管理,操作系统把分配给进程的内存划分成了不同的功能块。像我们经常说的:代码区,静态数据区,堆区,栈区等。

这里借用一张网络上的图来看一下。

Golang中运行与Plan9汇编的示例分析

这里就是我们程序(进程)在虚拟内存中的分布。

代码区:存放的就是我们编译后的机器码,一般来说这个区域只能是只读。

静态数据区:存放的是全局变量与常量。这些变量的地址编译的时候就确定了(这也是使用虚拟地址的好处,如果是物理地址,这些地址编译的时候是不可能确定的)。Data与BSS都属于这一部分。这部分只有程序中止(kill掉、crasg掉等)才会被销毁。

栈区:主要是 Golang 里边的函数、方法以及其本地变量存储的地方。这部分伴随函数、方法开始执行而分配,运行完后就被释放,特别注意这里的释放并不会清空内存。后面文章讲内存分配的时候再详细说;还有一个点需要记住栈一般是从高地址向低地址方向分配,换句话说:高地址属于栈低,低地址属于栈底,它分配方向与堆是相反的。

堆区:像 C/C++ 语言,堆完全是程序员自己控制的。但是 Golang 里边由于有GC机制,我们写代码的时候并不需要关心内存是在栈还是堆上分配。Golang 会自己判断如果变量的生命周期在函数退出后还不能销毁或者栈上资源不够分配等等情况,就会被放到堆上。堆的性能会比栈要差一些。原因也留到内存分配相关的文章再给大家介绍。

内存的结构搞明白了,我们的程序被加载到内存还需要操作系统来指挥才能正确运行。

补充一个比较重要的概念:

寻址空间:一般指的是CPU对于内存寻址的能力,通俗地说,就是能最多用到多少内存的一个问题。比如:32条地址线(32位机器),那么总的地址空间就有 2^32 个,如果是64位机器,就是 2^64 个寻址空间。可以使用 uname -a 来查看自己系统支持的位数字。

操作系统、CPU、内存互相配合

为了讲清楚程序运行与调用,我们得先理清楚操作系统、内存、CPU、寄存器这几者之间的关系。

  •  CPU: 计算机的大脑,它才能理解并执行指令;

  •  寄存器:严格讲寄存器是CPU的组成部分,它主要负责CPU在计算时临时存储数据;当然CPU还有多级的高速缓存,与我们这里相关度不大,就略过,大家知道其目的是为了弥补内存与CPU速度的差距即可;

  •  内存:像上面内存被划分成不同区,每一部分存了不同的数据;当然这些区的划分、以及虚拟内存与物理内存的映射都是操作系统来做的;

  •  操作系统:控制各种硬件资源,为其它运行的程序提供操作接口(系统调用)及管理。

这里操作系统是一个软件,CPU、寄存器、内存(物理内存)都是实打实的硬件。操作系统虽然也是一堆代码写出来的。但是她是硬件对其它应用程序的接口。总的来讲操作系统通过系统调用控制所有的硬件资源,他把其它的程序调度到CPU上让其它程序执行,但是为了让每个程序都有机会使用CPU,CPU又通过时间中断把控制权交给操作系统。

让操作系统可以控制我们的程序,我们编写的程序需要遵循操作系统的规定。这样操作系统才能控制程序执行、切换进程等操作。

最后我们的代码被编译成机器码之后,本质就是一条条的指令。我们期望的就是CPU去执行完这些指令进而完成任务。而操作系统又能够帮助我们让CPU来执行代码以及提供所需资源的调用接口(系统调用)。是不是非常简单?

Go程序的调用规约

在上面我们知道整个虚拟内存被我们划分为:代码区、静态数据区、栈区、堆区。接下来要讲的Go程序的调用规约(其实就是函数、方法运行的规则),主要是涉及上面所说的栈部分(堆部分会在内存分配的文章里边去讲)。以及计算机软硬各个部分如何配合。接下来我们就来看一下程序的基本单位函数跟方法是怎么执行与相互调用的。

函数在栈上的分布

这一部分,我们先来了解一些理论,然后接着用一个实际的例子来分析一下。先通过一张图来看一下在 Golang 中函数是如何在栈上分布的。

几个涉及到的专业用语:

  •  栈:这里说的栈跟上面的解释含义一致。无论是进程、线程、goroutine都有自己的调用栈;

  •  栈帧:可以理解是函数调用时在栈上为函数所分配的区域;

  •  调用者:caller,比如:a函数调用了b函数,那么a就是调用者;

  •  被调者:callee,还是上面的例子,b就是被调者。

Golang中运行与Plan9汇编的示例分析

栈帧

这幅图所展示的就是一个 栈帧 的结构。也可以说栈桢是栈给一个函数分配的栈空间,它包括了函数调用者地址、本地变量、返回值地址、调用者参数等信息。

这里有几个注意点,图中的 BP、SP都表示对应的寄存器。

  •  BP:基址指针寄存器(extended base pointer),也叫帧指针,存放着一个指针,表示函数栈开始的地方。

  •  SP:栈指针寄存器(extended stack pointer),存放着一个指针,存储的是函数栈空间的栈顶,也就是函数栈空间分配结束的地方,注意这里是硬件寄存器,不是Plan9中的伪寄存器。

BP 与 SP 放在一起,一个表示开始(栈顶)、一个表示结束(栈低)。

有了上面的基础知识,接着下面用实际的例子来验证一下。

Go的调用实例

才开始,我们就从一个简单的函数开始来分析一下整个函数的调用过程(下面涉及到 Plan9 汇编,请别慌,大部分都能够看懂,并且我也会写注释)。

package main  func main() {      a := 3      b := 2      returnTwo(a, b)  }  func returnTwo(a, b int) (c, d int) {      tmp := 1 // 这一行的主要目的是保证栈桢不为0,方便分析      c = a + b      d = b - tmp      return  }

上面有两个函数,main 定义了两个本地变量,然后调用 returnTwo 函数。returnTwo 函数有两个参数与两个返回值。设计两个返回值主要是一起来看一下 golang 的多返回值是如何实现的。接下来我们把上面的代码对应的汇编代码展示出来。

Golang中运行与Plan9汇编的示例分析

有几行代码需要特别解释下,

0x0000 00000 (test1.go:3) TEXT "".main(SB), ABIInternal, $56-0

这一行中的重点信息:$56-0。56 表示的该函数栈桢大小(两个本地变量,两个参数是int类型,两个返回值是int类型,1个保存base pointer,合计7 * 8 = 56);0表示 mian 函数的参数与返回值大小。待会可以在 returnTwo 中去看一下它的返回值又是多少。

接下来在看一下计算机是怎么在栈上分配大小的。

0x000f 00015 (test1.go:3)       SUBQ    $56, SP // 分配,56的大小在上面第一行定义了  ... ...  0x004b 00075 (test1.go:7)       ADDQ    $56, SP // 释放掉,但是并未清空

这两行,一个是分配,一个是释放。为什么用了 SUBQ 指令就能进行分配呢?而 ADDQ 是释放?记得我们前面说过吗? SP 是一个指针寄存器,并且指向栈顶,栈又是从高地址向低地址分配。那么对它做一次减法,是不是表示从高地址向低地址方向移动指针了呢?释放也是同样的道理,一次加法操作又把 SP 恢复到初始状态。

再来看一下对 BP 寄存器的操作。

0x0013 00019 (test1.go:3)       MOVQ    BP, 48(SP) // 保存BP  0x0018 00024 (test1.go:3)       LEAQ    48(SP), BP // BP存放了新的地址  ... ...  0x0046 00070 (test1.go:7)       MOVQ    48(SP), BP // 恢复BP的地址

这三行代码是不是感觉很变扭?写来写去让人云里雾里的。我先用文字描述一下,后面再用图来解释。

  我们先做如下假设:此时 BP 指向的 值 是:0x00ff,48(SP) 的 地址 是:0x0008。

  •  第一条指令 MOVQ BP, 48(SP) 是把 0x00ff 写入到 48(SP)的位置;

  •  第二条指令 LEAQ 48(SP), BP 是更新寄存器指针,让 BP 保存 48(SP) 这个位置的地址,也就是 0x00ff 这个值。

  •  第三条指令 MOVQ 48(SP), BP ,因为一开始 48(SP) 保存了最开始 BP 的所存的值 0x00ff,所以这里是又把 BP 恢复回去了。

这几行代码的作用至关重要,正因为如此在执行的时候,我们才能找到函数开始的地方以及回到调用函数的位置,它才可以继续往下执行(如果觉得饶,先放过,后面有图,看完后再回来理解)。接着来看一下 returnTwo 函数。

Golang中运行与Plan9汇编的示例分析

这里 NOSPLIT|ABIInternal, $0-32 说明,该函数的栈桢大小是0,由于有两个int参数,以及2个int返回值,合计为 4*8 = 32 字节大小,是不是跟上面的 main 函数对上了?。

这里有没有对 returnTwo 函数的栈桢大小是0表示迷惑呢?难道这个函数不需要栈空间吗?其实主要原因是:golang的参数传递与返回值都是要求使用栈来进行的(这也是为什么go能够支持多参数返回的原因)。所以参数与返回值所需空间都由 caller 来提供。

接下来,我们用完整的图来演示一下这个调用过程。

Golang中运行与Plan9汇编的示例分析

这个图就画了将近1个小时,希望对大家理解有帮助。

整个的流程是:初始化 ----> call main function ----> call returnTwo function ----> returnTwo return ----> main return。

通过这张图,在结合我上面的文字解释,相信大家能够理解了。不过这里还有几个注意点:

  •  BP 与 SP 是寄存器,它保存的是栈上的地址,所以执行中可以对 SP 做运算找到下一个指令的位置;

  •  栈被回收 ADDQ $56, SP ,只是改变了 SP 指向的位置,内存中的数据并不会清空,只有下次被分配使用的时候才会清空;

  •  callee的参数、返回值内存都是caller分配的;

  •  returnTwo ret的时候,call returnTwo的next指令 所在栈位置会被弹出,也就是图中 0x0d00 地址所保存的指令,所以 returnTwo 函数返回后,SP 又指向了 0x0d08 地址。

由于上面涉及到一些 Plan9 的知识,就顺带一起介绍一些它的语法,如果直接讲语法会很枯燥,下面会结合一些实际中会用到的情况来介绍。既有收获又能学会语法。

Go的汇编plan9

我们整个程序的编译最终会被翻译成机器码,而汇编可以算是机器码的文本形式,他们之间可以一一对应。所以如果我们能够看懂汇编一点点就能够分析出很多实际问题。

开发go语言的都是当前世界最TOP的那群程序员,他们选择了持续装逼,不用标准的 AT&T 也不用 Intel 汇编器,偏要自己搞一套,没办法,谁让人家牛呢!Golang的汇编是基于 Plan9 汇编的,个人觉得要完全学懂太复杂了,因为这涉及到很多底层知识。不过如果只是要求看懂还是能够做到的。下面我们就举一些例子来试试看。

PS: 这东西完全学懂也没有必要,投入产出比太低了,对于一个应用工程师能够看懂就行。

在正式开始前,我们还是补充一些必要信息,上文已经涉及过一些,为了完整这里在整体介绍一下。

几个重要的伪寄存器

  •  SB:是一个虚拟寄存器,保存了静态基地址(static-base) 指针,即我们程序地址空间的开始地址;

  •  NOSPLIT:向编译器表明不应该插入 stack-split 的用来检查栈需要扩张的前导指令;

  •  FP:使用形如 symbol+offset(FP) 的方式,引用函数的输入参数;

  •  SP:plan9 的这个 SP 寄存器指向当前栈帧的局部变量的开始位置,使用形如 symbol+offset(SP) 的方式,引用函数的局部变量,注意:这个寄存器与上文的寄存器是不一样的,这里是伪寄存器,而我们展示出来的都是硬件寄存器。

其它还有一些操作指令,根据名字多半都能够看出来,就不再介绍,直接开始干。

查看go应用代码对应的翻译函数

package main  func main() {  } func test() []string {      a := make([]string, 10)      return a  }  --------  "".test STEXT size=151 args=0x18 locals=0x40          0x0000 00000 (test1.go:6)       TEXT    "".test(SB), ABIInternal, $64-24 // 栈帧大小,与参数、返回值大小          0x0000 00000 (test1.go:6)       MOVQ    (TLS), CX          0x0009 00009 (test1.go:6)       CMPQ    SP, 16(CX)          0x000d 00013 (test1.go:6)       JLS     141          0x000f 00015 (test1.go:6)       SUBQ    $64, SP          0x0013 00019 (test1.go:6)       MOVQ    BP, 56(SP)          0x0018 00024 (test1.go:6)       LEAQ    56(SP), BP         ... ...          0x001d 00029 (test1.go:6)       MOVQ    $0, "".~r0+72(SP)          0x0026 00038 (test1.go:6)       XORPS   X0, X0          0x0029 00041 (test1.go:6)       MOVUPS  X0, "".~r0+80(SP)          0x002e 00046 (test1.go:7)       PCDATA  $2, $1          0x002e 00046 (test1.go:7)       LEAQ    type.string(SB), AX          0x0035 00053 (test1.go:7)       PCDATA  $2, $0          0x0035 00053 (test1.go:7)       MOVQ    AX, (SP)          0x0039 00057 (test1.go:7)       MOVQ    $10, 8(SP)          0x0042 00066 (test1.go:7)       MOVQ    $10, 16(SP)          0x004b 00075 (test1.go:7)       CALL    runtime.makeslice(SB) // 对应的底层runtime function         ... ...          0x008c 00140 (test1.go:8)       RET          0x008d 00141 (test1.go:8)       NOP          0x008d 00141 (test1.go:6)       PCDATA  $0, $-1          0x008d 00141 (test1.go:6)       PCDATA  $2, $-1          0x008d 00141 (test1.go:6)       CALL    runtime.morestack_noctxt(SB)          0x0092 00146 (test1.go:6)       JMP     0

根据对应的代码行数与名字,很明显的可以看到应用层写的 make 对应底层是 makeslice。

逃逸分析

这里先说一下逃逸分析的概念。这里牵扯到栈、堆分配的问题。如果变量被分配到栈上,会伴随函数调用结束自动回收,并且分配效率很高;其次分配到堆上,则需要GC进行标记回收。所谓逃逸就是指变量从栈上逃到了堆上(很多人对这个概念都不清楚就在谈逃逸分析,面试遇到了好几次?)。

package main  func main() {  }  func test() *int {      t := 3      return &t  }  ------  "".test STEXT size=98 args=0x8 locals=0x20          0x0000 00000 (test1.go:6)       TEXT    "".test(SB), ABIInternal, $32-8          0x0000 00000 (test1.go:6)       MOVQ    (TLS), CX          0x0009 00009 (test1.go:6)       CMPQ    SP, 16(CX)          0x000d 00013 (test1.go:6)       JLS     91          0x000f 00015 (test1.go:6)       SUBQ    $32, SP          0x0013 00019 (test1.go:6)       MOVQ    BP, 24(SP)          0x0018 00024 (test1.go:6)       LEAQ    24(SP), BP          ... ...          0x001d 00029 (test1.go:6)       MOVQ    $0, "".~r0+40(SP)          0x0026 00038 (test1.go:7)       PCDATA  $2, $1          0x0026 00038 (test1.go:7)       LEAQ    type.int(SB), AX          0x002d 00045 (test1.go:7)       PCDATA  $2, $0          0x002d 00045 (test1.go:7)       MOVQ    AX, (SP)          0x0031 00049 (test1.go:7)       CALL    runtime.newobject(SB) // 堆上分配空间,表示逃逸了          ... ...

这里如果是对 slice 使用汇编进行逃逸分析,并不会很直观。因为只会看到调用了 runtime.makeslice 函数,该函数内部其实又调用了 runtime.mallocgc 函数,这个函数会分配的内存其实就是堆上的内存(如果栈上足够保存,是不会看到对 runtime.makslice 函数的调用)。

实际go也提供了更方便的命令来进行逃逸分析:go build -gcflags="-m" ,如果真的是做逃逸分析,建议使用该命令,别折腾用汇编。

传值还是传指针

对于golang中的基本类型:字符串、整型、布尔类型就不多说了,肯定是值传递,那么对于结构体、指针到底是值传递还是指针传递呢?

package main    type Student struct {        name string        age  int    }    func main() {        jack := &Student{"jack", 30}        test(jack)    }    func test(s *Student) *Student {        return s    }    -------    "".test STEXT nosplit size=20 args=0x10 locals=0x0            0x0000 00000 (test1.go:14)      TEXT    "".test(SB), NOSPLIT|ABIInternal, $0-16           ... ...           0x0000 00000 (test1.go:14)      MOVQ    $0, "".~r1+16(SP) // 初始返回值为0           0x0009 00009 (test1.go:15)      PCDATA  $2, $1           0x0009 00009 (test1.go:15)      PCDATA  $0, $1           0x0009 00009 (test1.go:15)      MOVQ    "".s+8(SP), AX // 将引用地址复制到 AX 寄存器          0x000e 00014 (test1.go:15)      PCDATA  $2, $0           0x000e 00014 (test1.go:15)      PCDATA  $0, $2           0x000e 00014 (test1.go:15)      MOVQ    AX, "".~r1+16(SP) // 将 AX 的引用地址又复制到返回地址           0x0013 00019 (test1.go:15)      RET

通过这里可以看到在go里边,只有值传递,因为它底层还是通过拷贝对应的值。

以上是“Golang中运行与Plan9汇编的示例分析”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注编程网GO频道!

您可能感兴趣的文档:

--结束END--

本文标题: Golang中运行与Plan9汇编的示例分析

本文链接: https://lsjlt.com/news/284467.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Golang中运行与Plan9汇编的示例分析
    小编给大家分享一下Golang中运行与Plan9汇编的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!Golang的运行环境当我们把编译后的Go代码运行起来...
    99+
    2023-06-16
  • C++内嵌汇编的示例分析
    这篇文章主要为大家展示了“C++内嵌汇编的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“C++内嵌汇编的示例分析”这篇文章吧。汇编语言汇编语言是一种功能很强的程序设计语言,也是利用了计算...
    99+
    2023-06-28
  • JavaScript运行的示例分析
    这篇文章给大家分享的是有关JavaScript运行的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1.1 语法分析预编译之前,先通篇扫描看看有没有语法错误1.2 预编译 1.2.1 函数声明整体...
    99+
    2023-06-29
  • linux中shell脚本编写和运行的示例分析
    这篇文章主要介绍了linux中shell脚本编写和运行的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。编写第一个shell脚本在gedit中编写.sh格式的文件,保存...
    99+
    2023-06-09
  • SparkSQl中运行原理的示例分析
    这篇文章将为大家详细讲解有关SparkSQl中运行原理的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。一:什么是SparkSQL?(一)SparkSQL简介Spark SQL是Spark的一个模块...
    99+
    2023-06-20
  • Linux中运行级别与重新启动的示例分析
    这篇文章将为大家详细讲解有关Linux中运行级别与重新启动的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。概述在本文中,学习关闭或重启您的 Linux 系统,警告用户系统正在关闭,请切换到单用户模...
    99+
    2023-06-16
  • kubernetes中kubelet运行机制的示例分析
    这篇文章给大家分享的是有关kubernetes中kubelet运行机制的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。一:简介在Kubernetes集群中,每个Node节点上都会启动一个Kubelet服务...
    99+
    2023-06-04
  • golang中并发和并行的示例分析
    这篇文章主要介绍了golang中并发和并行的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。什么是golanggolang 是Google开发的一种静态强类型、编译型、...
    99+
    2023-06-15
  • WCF编程运行举例分析
    这篇文章主要介绍“WCF编程运行举例分析”,在日常操作中,相信很多人在WCF编程运行举例分析问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”WCF编程运行举例分析”的疑惑有所帮助!接下来,请跟着小编一起来学习吧...
    99+
    2023-06-17
  • JavaScript运行原理的示例分析
    这篇文章主要介绍JavaScript运行原理的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!JavaScript是一种基于对象的动态、弱类型脚本语言(以下简称JS),是一种解...
    99+
    2024-04-02
  • Java线程运行的示例分析
    这篇文章将为大家详细讲解有关Java线程运行的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。栈与栈帧JVM中由堆、栈、方法区所组成,其中栈内存就是分配给线程使用的,每个线程启动后,虚拟机都会为其分...
    99+
    2023-06-29
  • golang中极简流式编程的示例分析
    这篇文章主要为大家展示了“golang中极简流式编程的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“golang中极简流式编程的示例分析”这篇文章吧。传统的过程编码方式带来的弊端是显而易...
    99+
    2023-06-20
  • JVM系列之从汇编角度分析NullCheck的示例分析
    这篇文章主要介绍了JVM系列之从汇编角度分析NullCheck的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一个普通的virtual call我们来分析一下在方法中...
    99+
    2023-06-15
  • PHP中CLI命令行运行模式的示例分析
    这篇文章将为大家详细讲解有关PHP中CLI命令行运行模式的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。PHP的CLI命令行运行模式浅析在做开发的时候,我们不仅仅只是做各种网站或者接口,也经常需要...
    99+
    2023-06-15
  • PYPL6月编程语言排行中Kotlin 与 PHP的示例分析
    本篇文章为大家展示了PYPL6月编程语言排行中Kotlin 与 PHP的示例分析,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。PYPL 发布了 6 月份编程语言排行...
    99+
    2024-04-02
  • mongodb中运维的示例分析
    这篇文章主要介绍了mongodb中运维的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。我们以管理员的视角来看mongodb,作为一名...
    99+
    2024-04-02
  • webpackcjs运行时分析示例详解
    目录1. 在index.js文件中引入任一js文件2. sum文件3. build.js文件4. 命令行执行node ./build.js 生成打包产物main.js。5. 什么是运...
    99+
    2022-12-28
    webpack cjs运行时 webpack cjs
  • PHP中CI框架运行模式的示例分析
    小编给大家分享一下PHP中CI框架运行模式的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!php有什么用php是一个嵌套的缩写名称,是英文超级文本预处理语言,它的语法混合了C、Java、Perl以及php自创新的语...
    99+
    2023-06-14
  • Flink on yarn运行原理的示例分析
    小编给大家分享一下Flink on yarn运行原理的示例分析,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!Flink运行时由两种类型的进程组成:1),JobManager也叫master协调分布式执行。他们调度任务,协调...
    99+
    2023-06-19
  • python中DataFrame运算的示例分析
    这篇文章给大家分享的是有关python中DataFrame运算的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。Python的优点有哪些1、简单易用,与C/C++、Java、C# 等传统语言相比,Pytho...
    99+
    2023-06-15
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作