心血来潮之 栈 和 堆 的思考

源头

常常看到一些编程语言的书上有说,程序运行过程当中,会涉及到 栈(Stack)和 堆(Heap)。尤为遇到涉及调查内存泄漏的场景时,书本会提醒让开发人员查看 堆 的状况javascript

基于上述现象,来了两个疑问:java

  • 程序执行过程当中,为何常常会涉及到 栈 和 堆 两个数据结构,他们一般的做用是什么?c++

  • 选用 栈 能够理解,但为何选用 堆 呢?对比其余的数据结构,在程序执行过程当中,堆 的优点是什么?编程

相关资料

(资料主要来源于 stackoverflow,进行消化以及整理)数据结构

关于 堆栈,首先引入两个解释:多线程

  • 不管是 堆 仍是 栈,它们都是底层操做系统分配的内存区域
  • 在多线程环境中,每一个线程拥有独立的栈,但它们会共享一个堆。面向堆数据的并发访问,将会受到严格的控制

关于堆

  • 堆包含着一个列表,记录着哪些区块是空闲的,哪些是被使用的。在堆上分配新的内容,意味着从空闲的区块中,选择合适的区块进行分配。这个过程须要堆更新自身的区块记录列表。区块记录列表做为元数据,会在堆上被一直记录着
  • 随着堆越长越大,堆的大小可能会超过已经分配的大小,这时候应用程序须要向底层系统申请内存空间对堆进行扩展
  • 在堆上进行屡次内存分配或者回收,会致使堆的连续内存空间上有许多空闲的区块分散在被应用的区块之间,这时候,若是须要堆分配一个大块区块,可能一会儿没法分配,须要对堆进行区块的从新整理
  • 若是堆上一个使用中的区块临近一个空闲区块,当使用中的区块被释放,该区块会直接与相邻的空闲区块进行合并,下降堆上的内存进行碎片化整理的频率
  • 堆 的内存划分在不一样的系统或者 runtime 上并不彻底一致,因此,编程语言书中提到的 堆(heap)并非彻底相同的东西,只是抽象概念统一
  • 重要:堆内存 和 堆结构 其实指的是两个东西,此 Heap 非彼 heap。操做系统 和 runtime 上常常提到的 堆 其实是指 堆内存:一段特别组织的内存,而不是指 堆 数据结构。参考自 stackoverflow 的回答 why are two different concepts both called "heap?"

关于栈

  • 计算机 cpu 有专门的硬件结构针对栈进行操做,栈的操做在 cpu 上会很是高效快速
  • 当一个函数被 cpu 调用时,调用该函数的指令地址会被压栈,而后 cpu 的执行指针会指向函数的开始指令的地址
  • 当 cpu 进入一个函数前,函数的对应的传参也会被进行压栈,进入函数后,传参出栈
  • 当 cpu 离开函数前,函数的返回会被压栈,离开后,返回结果出栈,此时函数的调用方能够得到函数的返回数据
  • 根据上述的过程,计算机能够支持函数的互相调用 或者 递归调用,固然,整个过程须要消耗栈的空间,而栈的大小并非无限大的,函数调用层次过深,就会出现咱们一般说的爆栈现象

回答疑问

根据上述资料可知,上述的两个疑问提到的 堆 其实都不是对应数据结构中的堆的概念,因此问题自己是不许确的~架构

至于 栈 和 堆 在程序运行过程当中起到什么样的做用,详细过程又是怎么样的,上述资料已经能够窥探一二,而其中的细节,就不是这个篇章能够容纳的了(并且不一样的系统架构,不一样的 runtime 细节上又不同),就不在这里展开细述了。并发

追加思考:堆在 javascript runtime 中是怎么表现的呢?

参考 runtime:v8 引擎

查看源码

  1. 先看一下 v8 源码中的 heap 头文件,能够看到有个 HeapStats 类,对 heap 进行描述
class HeapStats {
 public:
  static const int kStartMarker = 0xDECADE00;
  static const int kEndMarker = 0xDECADE01;

  intptr_t* start_marker;                  // 0
  size_t* ro_space_size;                   // 1
  size_t* ro_space_capacity;               // 2
  size_t* new_space_size;                  // 3
  size_t* new_space_capacity;              // 4
  size_t* old_space_size;                  // 5
  size_t* old_space_capacity;              // 6
  size_t* code_space_size;                 // 7
  size_t* code_space_capacity;             // 8
  size_t* map_space_size;                  // 9
  size_t* map_space_capacity;              // 10
  size_t* lo_space_size;                   // 11
  size_t* code_lo_space_size;              // 12
  size_t* global_handle_count;             // 13
  size_t* weak_global_handle_count;        // 14
  size_t* pending_global_handle_count;     // 15
  size_t* near_death_global_handle_count;  // 16
  size_t* free_global_handle_count;        // 17
  size_t* memory_allocator_size;           // 18
  size_t* memory_allocator_capacity;       // 19
  size_t* malloced_memory;                 // 20
  size_t* malloced_peak_memory;            // 21
  size_t* objects_per_type;                // 22
  size_t* size_per_type;                   // 23
  int* os_error;                           // 24
  char* last_few_messages;                 // 25
  char* js_stacktrace;                     // 26
  intptr_t* end_marker;                    // 27
};
复制代码

从状态类上,咱们能够看到,有针对 新生代 和 老生代 的描述:new_space_size,new_space_capcacity,old_space_size,old_space_capacity,可知 javascript 的堆内存参考过 jvm 的堆内存管理模式,以便优化内存回收的效率app

  1. 查看 objects.h 文件,从该文件的注释描述中,能够看到有对 HeapObject 的描述,以下:
//
// Most object types in the V8 JavaScript are described in this file.
//
// Inheritance hierarchy:
// - Object
// - Smi (immediate small integer)
// - HeapObject (superclass for everything allocated in the heap)
// - JSReceiver (suitable for property access)
// - JSObject
// - JSArray
// - JSArrayBuffer
// - JSArrayBufferView
// - JSTypedArray
// - JSDataView
// - JSBoundFunction
// - JSCollection
// - JSSet
// - JSMap
// - JSDate
// - JSFunction
// - JSGeneratorObject
// - JSGlobalObject
// - JSGlobalProxy
// - JSMapIterator
// - JSMessageObject
// - JSModuleNamespace
// - JSPrimitiveWrapper
// - JSRegExp
// - JSSetIterator
// - JSStringIterator
// - JSWeakCollection
// - JSWeakMap
// - JSWeakSet
// - JSCollator // If V8_INTL_SUPPORT enabled.
// - JSDateTimeFormat // If V8_INTL_SUPPORT enabled.
// - JSListFormat // If V8_INTL_SUPPORT enabled.
// - JSLocale // If V8_INTL_SUPPORT enabled.
// - JSNumberFormat // If V8_INTL_SUPPORT enabled.
// - JSPluralRules // If V8_INTL_SUPPORT enabled.
// - JSRelativeTimeFormat // If V8_INTL_SUPPORT enabled.
// - JSSegmenter // If V8_INTL_SUPPORT enabled.
// - JSSegmentIterator // If V8_INTL_SUPPORT enabled.
// - JSV8BreakIterator // If V8_INTL_SUPPORT enabled.
// - WasmExceptionObject
// - WasmGlobalObject
// - WasmInstanceObject
// - WasmMemoryObject
// - WasmModuleObject
// - WasmTableObject
// - JSProxy
复制代码

能够看到,v8 在执行 js 过程当中,runtime 内部产生的 js 对象会继承自 HeapObject 这个类,而 HeapObject 会根据策略从 runtime 的 堆 中进行 存储、移动、释放等操做jvm

咱们也知道,javascript 的数据类型会区分 基本类型(String,Number,Boolean,Null,Undfined,Symbol) 和 引用数据类型(Object,Array,Function)

从上面的注释能够看出来,基本数据类型 并无继承自 HeapObject,在 js 的执行过程当中,它们会被记录到 栈(Stack)中,当函数执行完毕后,即从栈中退出。而引用数据类型 则继承自 HeapObject,在执行过程当中,从堆中存储,再也不被引用后,将会等待 GC 依据策略将其清理

相关文章
相关标签/搜索