大概简述内容:堆栈、进程、虚拟地址、逃逸、锁、临界区

1.堆内存大但是执行速度较慢,栈内存较小但是执行速度快。二进制包存储的有地址,但是在加载到内存后分配堆栈生成虚拟地址。临界区内存用于锁相关时,频繁的互斥锁会导致性能的低下。

2.二进制包就是0和1,而汇编指令是二进制的高级描述

3.堆内存一般存在复杂数据,如函数以及非临时变量/全局变量等,栈内存主要是临时变量/局部变量,会自动gc回收销毁,例如函数运行时的临时变量放在栈里。堆地址是大于还是小于栈地址这个和不同语言不同操作系统以及x32 x64有关

4.每个进程都是独立的内存,线程是共享进程内存,进程中的地址都是虚拟的,由外部物理内存映射得来,进程的虚拟地址区间都是从0x00000000-0x7fffffff

5.一个进程下可有多个线程协程等,进程可拥有一个或多个子进程,主进程关闭线程协程会被回收kill掉,而子进程会被独立给到系统管理,想要关闭子进程可以通过信号结束子进程。关系类似【系统进程为爷爷-》用户进程为爸爸-》用户进程下的进程是儿子】只是爸爸die了,爷爷就成了爸爸

6.进程可利用一个核的cpu资源。线程共享进程可利用的cpu资源,和进程内存。协程一般是用户态的,所以只能利用当前进程的。如果协程是系统底层的线程可能另当别论了。具体和语言设计的程调度方式有关

7.如果希望更好的压榨性能,除了多进程多线程协程,还有GC的频次,逃逸(尽量避免逃逸到堆上或者说避免逃逸,减少堆内存频繁分配),一般场景不用管逃逸,除非是性能要求极致的可以进行逃逸分析进行优化

8.编译器在编译时会通过静态分析决定变量哪些分配到堆内存 哪些分配到栈内存,但是在运行过程中可能还会出现逃逸问题,那就要通过GC进行动态的去处理分配

9.引用部分其他人的总结:

golang的垃圾回收是针对堆的(垃圾回收都是针对堆的,这里只是做一个简单的证明)

引用类型的全局变量内存分配在堆上,值类型的全局变量分配在栈上

局部变量内存分配可能在栈上也可能在堆上

堆和栈的简单说明:

1.栈(操作系统):由操作系统自动分配释放

2.堆(操作系统): 一般由程序员分配释放,例如在c/c++中,在golang,java,python有自动的垃圾回收机制

我们都知道变量占有内存,内存在底层分配上有堆和栈。

值类型变量的内存通常是在栈中分配

引用类型变量的内存通常在堆中分配

注意这里说的是"通常",因为变量又分为局部变量和全局变量。

10.go一般避免逃逸的做法

10.1强烈建议在创建 slice 时带上预估的cap参数 不仅减少了堆内存的频繁分配,在切片变量未逃逸的情况下,在cap容量之下,所有元素都分配在栈上,这将提升运行性能。

func noescape(p unsafe.Pointer) unsafe.Pointer {

x := uintptr(p)

return unsafe.Pointer(x ^ 0)

}

10.2使用(*int)(noescape(unsafe.Pointer(&a)))

11.原子锁,cpu去控制的

12.其他互斥锁等 由操作系统处理

13.临界区,每个进程临界区都是独立的,这样表达并不总是对的,临界区只是上下文的某一段或者说某一个内存段落。互斥是保护程序临界区的一种方式。临界区是程序中需要独占访问共享资源的区域

加锁是为了共享资源的区域保护,是独占访问,如互斥锁进行同步访问

内存临界区是加锁用的?不,临界区属于一个内存堆栈上概念,与上下文的数据有关

内存临界区是每个进程独立的吗?其实每个进程都有自己的独立内存空间,这只是进程标准明确的一些名词而已 如堆栈,所以临界区只是你希望的一个内存地址或者是内存地址段,站在上下文角度来说就是你的代码块,也许是一个片段也许是一个变量,并且你想共享这些数据资源时,且允许同一个时间而不是同一个单位时间内保证只有一个程去读写那么就可以叫做临界区

14.go结构体的定义只是一种内存布局描述,只有结构体实例化时才会分配真正内存,实例化只是创建一份与格式一致的内存区域,每个结构体的实例内存是独立的。

那么结构体是如何存储在内存上的呢?等后面文章再介绍