什么是内存管理

内存管理是控制协调应用程序访问电脑内存的过程。这个过程是复杂的,对于我们来说,可以说相当于一个黑匣子。

当咱们的应用程序运行在某个操作系统中的时候,它访问电脑内存(RAM)来达成下列几个功能:

  1. 运行需要执行的字节码(代码)
  2. 存储程序运行时候所需的数据
  3. 加载程序运行所需的运行时环境

上面用来存储程序运行时所需的数据,就是下面要说的堆(heap)和栈(stack)。

栈(stack)

顾名思义,是一种先进后出的结构,参考一下餐盘的取和放。

俄罗斯套娃,我这不禁

栈的特点

  • 由于先进后出性质,在数据的处理上栈有着很好的速度,因为只需从最顶部压栈和出栈就好了,简单明了。
  • 不过,存储在栈中的数据必须是大小有限,生存期确定。
  • 函数执行的时候会创建一个明确的栈,并压入,而当执行期间会存储函数内的所有数据,这就是栈帧。个人感觉可以理解为当前执行函数的快照。
  • 多线程应用程序有多个栈。
  • 栈的操作系统自动分配或释放。
  • 存储在栈中的常见类型有:局部变量(值类型、基本类型、常量)、指针和函数
  • 还记得平常偶尔遇到的stack overflow error吗?这是因为与堆相比,栈的大小受到了限制。大多属语言都是都是这样。

堆(heap)

堆常用来动态内存分配,程序在堆中寻找数据需要使用指针。

堆的特点

  • 效率不如栈,但是可以存储更多的数据
  • 可存储大小不确定的数据,如运行时确定
  • 应用程序中多线程共享堆数据
  • 堆由人工操作,故管理起来很棘手,可能会引起内存泄漏等问题,所以有很多语言有gc机制
  • 存储在堆中的常见类型有:全局变量、引用类型和其他复杂的数据结构
  • 这就是为什么你会遇到out of memory errors这类问题,因为用户的胡乱分配或者未销毁
  • 我们分配给堆的数据其实并没有大小限制,理论上来说你可以分配无穷大的数据。当然如果这样你也得为应用程序分配这么多的内存。 -_-

内存堆栈 与 数据结构堆栈的区别

内存 数据结构
new一个对象的引用或地址存储在栈区,指向该对象存储在堆区中的真实数据。由程序员分配和回收 是一棵完全二叉树结构
存储运行方法的形参、局部变量、返回值。由系统自动分配和回收。 是一种连续存储的数据结构,特点是存储的数据先进后出

内存管理的重要性

与硬盘不同,内存大小有限的,并且往往不是很大。所以说,如果在没有释放无用内存的情况下,无休止的继续消耗内存空间,那么就会造成内存不足甚至操作系统崩溃。。。

所以,以为这种情况,许多语言都提供自动内存管理。并且由于栈是由系统操控,所以我们接下来讨论的自动内存管理主要是堆。

写到这,我想说,我有点不认识堆这个字了。。。

不同的内存管理方法

因为现在语言开发者们不想给使用者增加内存管理的负担(或者说是不相信他们能处理好。。),所以设计出自动管理内存的方式。并且很多语言设计出多种方法去自动管理内存,以供开发者选择。下面一一介绍。

手动内存管理

语言默认不管理内存,比如C、C++,他们提供malloc、realloc、calloc和free等,但是这不是适用于所有人。

GC (Garbage collection)

通过释放无用的内存空间去管理堆内存,gc是当前编程中最常见的一种内存自动管理方法,不过大量处理会造成线程卡主,所以利用碎片时间进行。

使用gc的语言有JVM、JavaScript、C#、Golang、OCaml和ruby。

Mark & Sweep GC

又名: tracing GC。有两个阶段,第一阶段标记那些应该处于活跃状态的数据,下个阶段释放不被引用的数据。

例如JVM、C#、RubyJavaScript和Golang都采用这种方法。

V8引擎还会使用Reference counting GC去弥补,这种gc也可以用于C、C++作为外部库,

Reference counting GC

引用计数内存管理。使用数字代表当前数据是否可回收,数字会根据引用和失去引用而改变,当为0的时候即被回收。但是这种无法处理循环引用。

Resource Acquisition is Initialization (RAII)

由c++之父Bjarne Stroustrup提出,中文翻译为资源获取即初始化。

这种类型的内存管理,对象的内存分配与它的声明周期有关,其核心是把资源和对象的生命周期绑定,对象创建获取资源,对象销毁释放资源。在RAII的指导下,C++把底层的资源管理问题提升到了对象生命周期管理的更高层次。在C++中引入,Ada和Rust也有在用。

Automatic Reference Counting(ARC)

自动引用计数。是苹果公司的Objective-C程序的一种自动内存管理机制。

类似于引用计数,但是代替以特定间隔运行,将保留和释放命令插入到代码中,当计数为0时,自动触发, 无需程序暂停。当然ARC依然不能处理循环引用。需要开发者使用某些关键字去处理。

Ownership

所有权是Rust的突破功能. 它使Rust可以完全内存安全且高效,同时避免垃圾回收。

Rust语言的ownership是rust语言的核心,rust语言之所以被称之为安全的面向系统级别的编程语言 正是由此特性决定的。

rust指南

小结

到目前为止,我们简单介绍了内存管理的相关内容,每个语言都有特定的机制,统统不同的算法达到不同的目标,接下里我们来聊聊V8.

V8引擎

V8在运行之前将JavaScript编译成了机器代码,而非字节码或是解释执行它,以此提升性能。V8使用C++书写,可以嵌入到任何C++应用程序中。

V8内存结构

首先,让我们看下V8引擎内存结构长什么样:

由于js是单线程的,所以node依然会为每个js环境提供单线程环境。如果你在服务端使用,他会为每个服务提供一个进程。在V8程序中,应用程序始终被分配的内存代表。这种内存成为常驻集。如上图所示。

V8中的堆内存

这里用来存储对象和动态数据,这是内存中最大的区域,并且是GC工作的地方。不过,并不是所有的堆内存都可以进行GC,只有新生代和老生代被gc管理。堆可以进一步细分为下面这样:

  • 新生代空间:是最新产生的数据存活的地方,这些数据往往都是短暂的。这个空间被一分为二,然后被Scavenger(Minor GC)所管理。稍后会介绍。可以通过V8标志如 --max_semi_space_size 或 --min_semi_space_size 来控制新生代空间大小
  • 老生代空间:是从新生代空间经过至少两轮Minor GC仍然存活下来的数据,该空间被Major GC(Mark-Sweep & Mark-Compact)管理,稍后会介绍。可以通过 --initial_old_space_size 或 --max_old_space_size控制空间大小。
    1. Old pointer space: 存活下来的包含指向其他对象指针的对象
    2. Old data space: 存活下来的只包含数据的对象。
  • 大对象空间: 这是比空间大小还要大的对象,大对象不会被gc处理。
  • 代码空间:这里是JIT所编译的代码。这是除了在大对象空间中分配代码并执行之外的唯一可执行的空间。
  • map空间:存放 Cell 和 Map,每个区域都是存放相同大小的元素,结构简单。

V8内存管理剖析

到目前为止,我们大概了解了内存的各空间组织方式,接下来,让我们看看当程序执行时内存的重要性。

少bb,看代码:

class Employee {
constructor(name, salary, sales) {
this.name = name;
this.salary = salary;
this.sales = sales;
}
} const BONUS_PERCENTAGE = 10; function getBonusPercentage(salary) {
const percentage = (salary * BONUS_PERCENTAGE) / 100;
return percentage;
} function findEmployeeBonus(salary, noOfSales) {
const bonusPercentage = getBonusPercentage(salary);
const bonus = bonusPercentage * noOfSales;
return bonus;
} let john = new Employee("John", 5000, 5);
john.bonus = findEmployeeBonus(john.salary, john.sales);
console.log(john.bonus);

点击此处查看代码执行过程中,内存空间的展示

大白话解释一下:

全局向下文像一个快照一样保存在栈上,每一次函数调用也会将一个快照放到栈中,包括函数的局部变量,参数和返回值。

基本类型保存在栈中,对象、复杂类型或者引用类型保存在堆中。

任何函数的调用都会在栈顶被压入。

一旦主进程完成所有任务,堆中的对象便不会被引用而孤立。

随着我们程序的运行,堆中的数据会越来越多,因为无人看管,栈由系统自动管理,所以无需操心。

所以,为了管理堆空间,垃圾回收机制进场了。

V8内存管理:GC

我们知道V8如何分配内存空间,接下来让我们看看V8是如何管理堆空间的。

举个简单的例子,V8会释放被孤立的对象,被孤立的对象一般指,不直接或间接被引用的对象,这样就为新对象腾出了空间。

因此,V8垃圾回收机制代表着:回收未使用内存供V8进行复用。

具体的实现如下:

Minor GC (Scavenger)

清道夫GC,主要管理新生代空间,保证新生代空间的紧凑和干净。所有的对象都会分配到新生代空间,新生代空间相对较小。大约在1M ~ 8M,可以通过命令控制。

该GC流程大致是这样:

新生代空间由两个等分的空间组成,to-space和from-space,可以把这两个名字理解为阶段(当前使用当前未使用),而不是名字。当当前使用没有更多内存后,触发Minor GC。

比如,当当前使用没有更多空间了,这时候,新增了一个待分配空间对象,Minor GC会吧当前未使用切换为当前使用,然后将之前已满负荷的空间中的数据,根据是否存活整理到新的、空的当前使用中。存活的就放过去,非存活的就扔掉。并且在移动存活数据的时候,会紧凑摆放,避免空间浪费。最后吧待分配空间对象,分配到新的当前使用中。

当更多的待分配空间进来时,会重复上面流程,但是如果一个数据存活两次以上,它就会被扔到老生代空间。以保证新生代空间的

上面的过程可以用一个例子来概括:

小明去网吧,第一次去,有位置,直接上机。没位置,得等老板清理下到时间的机器,然后再上机。这种到时间的一般是没办卡的(非存活)。

第二次去,还是上面的流程。

第N次去,小明也办了张卡(存活)

如果小明的卡到期了,那么他也就失活了。

上面所述过程,一定要参考下这个流程图!!!

Major GC(Mark-Sweep & Mark-Compact)

该GC主要负责老生代空间的紧凑和清理。当V8知道老生代空间无更多空间的时候就会触发这个GC。上面的清道夫GC对于体积小的数据来说是完美的,但是对于体积大的来说,非也。一种更好的方式是:标记-扫描-紧凑算法,顾名思义,一共是三步。

不多解释。自己看图吧。

总结

以上介绍了内存的堆栈,也对比了数据结构的堆栈。

之后简析了几种GC方式和V8的具体实现。

更多深入的内容欢迎交流,互相学习。

最后,非常欢迎大家关注我的掘金,以后有最新内容会优先推送,方便学习交流。

参考

memory-management

V8内存简析

内存和数据堆栈

V8垃圾回收?看这篇就够了!的更多相关文章

  1. 面试中关于Java虚拟机(jvm)的问题看这篇就够了

    最近看书的过程中整理了一些面试题,面试题以及答案都在我的文章中有所提到,希望你能在以问题为导向的过程中掌握虚拟机的核心知识.面试毕竟是面试,核心知识我们还是要掌握的,加油~~~ 下面是按jvm虚拟机知 ...

  2. ASP.NET Core WebApi使用Swagger生成api说明文档看这篇就够了

    引言 在使用asp.net core 进行api开发完成后,书写api说明文档对于程序员来说想必是件很痛苦的事情吧,但文档又必须写,而且文档的格式如果没有具体要求的话,最终完成的文档则完全取决于开发者 ...

  3. .NET Core实战项目之CMS 第二章 入门篇-快速入门ASP.NET Core看这篇就够了

    作者:依乐祝 原文链接:https://www.cnblogs.com/yilezhu/p/9985451.html 本来这篇只是想简单介绍下ASP.NET Core MVC项目的(毕竟要照顾到很多新 ...

  4. 想了解SAW,BAW,FBAR滤波器的原理?看这篇就够了!

    想了解SAW,BAW,FBAR滤波器的原理?看这篇就够了!   很多通信系统发展到某种程度都会有小型化的趋势.一方面小型化可以让系统更加轻便和有效,另一方面,日益发展的IC**技术可以用更低的成本生产 ...

  5. [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了

    [译]ASP.NET Core Web API 中使用Oracle数据库和Dapper看这篇就够了 本文首发自:博客园 文章地址: https://www.cnblogs.com/yilezhu/p/ ...

  6. ExpandoObject与DynamicObject的使用 RabbitMQ与.net core(一)安装 RabbitMQ与.net core(二)Producer与Exchange ASP.NET Core 2.1 : 十五.图解路由(2.1 or earler) .NET Core中的一个接口多种实现的依赖注入与动态选择看这篇就够了

    ExpandoObject与DynamicObject的使用   using ImpromptuInterface; using System; using System.Dynamic; names ...

  7. Vue学习看这篇就够

    Vue -渐进式JavaScript框架 介绍 vue 中文网 vue github Vue.js 是一套构建用户界面(UI)的渐进式JavaScript框架 库和框架的区别 我们所说的前端框架与库的 ...

  8. 【转】ASP.NET Core WebApi使用Swagger生成api说明文档看这篇就够了

    原文链接:https://www.cnblogs.com/yilezhu/p/9241261.html 引言 在使用asp.net core 进行api开发完成后,书写api说明文档对于程序员来说想必 ...

  9. Pycharm新手教程,只需要看这篇就够了

    pycharm是一款高效的python IDE工具,它非常强大,且可以跨平台,是新手首选工具!下面我给第一次使用这款软件的朋友做一个简单的使用教程,希望能给你带来帮助! 目前pycharm一共有两个版 ...

随机推荐

  1. 【ZooKeeper系列】3.ZooKeeper源码环境搭建

    前文阅读: [ZooKeeper系列]1.ZooKeeper单机版.伪集群和集群环境搭建 [ZooKeeper系列]2.用Java实现ZooKeeper API的调用 在系列的前两篇文章中,介绍了Zo ...

  2. node.js UDP NAT 穿透实现

    源码:https://github.com/zhongchengyi/node-udp-trunnel-demo 1.原理 A登录Server, NAT A 分配端口11000,Server得到A的地 ...

  3. [梁山好汉说IT] 边缘计算在梁山的应用

    [梁山好汉说IT] 边缘计算在梁山的应用 0x00 摘要 梁山泊下四个酒店就是边缘计算在梁山的应用,以朱贵南山酒店为例能看出其"计算实时/省流量/具备智能"等各种优点. 0x01 ...

  4. 面试官刁难:Java字符串可以引用传递吗?

    老读者都知道了,六年前,我从苏州回到洛阳,抱着一幅"海归"的心态,投了不少简历,也"约谈"了不少面试官,但仅有两三个令我感到满意.其中有一位叫老马,至今还活在我 ...

  5. $Poj1723/AcWing123\ Soldiers$ 排序

    $Poj$ $AcWing$ $Description$ $Sol$ 分别处理$x$坐标和$y$坐标.$y$坐标显然很好处理,就是排个序然后取中位数就好了.$x$没有$y$那么直接叭.所以我首先写了个 ...

  6. JAVA字节码文件之结构

    开发工具:IEDA.JDK1.8.WinHex 一.字节码文件结构 源代码 package com.jalja.java.bytecode; /** * @Auther: XL * @Date: 20 ...

  7. Vuex入门实践(上)

    一.前言 vuex被称为是专为vue应用程序开发的的状态管理模式.它的作用使用一句话描述就是:让组件之间可以共享数据 话不多少,先抛开概念,我们写一个简单的示例感受一波. 二.项目开发环境 项目开发环 ...

  8. 查看磁盘型号和内存及raid信息

    1.查看磁盘型号 工具:smartmontools #smartctl --help #smartctl --all /dev/sda -d megarid,1 (第一块磁盘的信息) #smartct ...

  9. java小项目之:扫雷,这游戏没有你想的那么简单!

    扫雷 我之前分享的小项目和小游戏,电影购票.坦克大战.捕鱼达人.贪吃蛇等,虽然已经是耳熟能详人尽皆知的项目和游戏,但是保不齐真的有人没接触过. 今天分享的这个项目,我不相信没人接触过(仅限80后-00 ...

  10. 比特币学习笔记(一)---在windows下编译搭建比特币环境

    最近打算研究下比特币源码,却发现这套源码正常情况下得在linux下编译运行,而我的机器是windows的. 怎么办呢? 起初打算用mingw和cygwin搞搞看,试了许久后发现行不通,必须转到linu ...