精华内容
下载资源
问答
  • 深入理解js内存机制

    千次阅读 2018-11-30 14:30:02
    原文链接:深入理解js内存机制 js的内存机制在很多前端开发者看来并不是那么重要,但是如果你想深入学习js,并将它利用好,打造高质量高性能的前端应用,就必须要了解js的内存机制。对于内存机制理解了以后,一些...

    原文链接:深入理解js内存机制

    js的内存机制在很多前端开发者看来并不是那么重要,但是如果你想深入学习js,并将它利用好,打造高质量高性能的前端应用,就必须要了解js的内存机制。对于内存机制理解了以后,一些基本的问题比如最基本的引用数据类型和引用传递到底是怎么回事儿?比如浅复制与深复制有什么不同?还有闭包,原型等等就迎刃而解了。

    js类型

    在js中,js的类型分为两个大类,分别是基本数据类型引用数据类型。我们暂时先抛开ES6不说,先只说在ES5中的类型。在ES5中有5中简单数据类型(也就是上面说的基本数据类型):Undefined、Null、Boolean、Number和String。还有1种复杂的数据类型————Object,Object本质上是由一组无序的名值对组成的。其中可以算在object中的还有Array和Function。

    在内存当中,基本数据类型存放在栈中,引用数据类型存放在堆中。说到这里就要说一下内存空间了,一般来说,js的内存空间分为栈(stack)、堆(heap)、池(一般也会归类栈中)。其中栈存放变量,堆存放复杂对象,池存放常量,所以也叫常量池。

    但是有一个变量是特殊的,那就是闭包中的变量,闭包中的变量并不保存中栈内存中,而是保存在堆内存中,这也就解释了函数之后之后为什么闭包还能引用到函数内的变量。

    function A() {
      let a = 1
      function B() {
          console.log(a)
      }
      return B
    }
    

    闭包的简单定义是:函数 A 返回了一个函数 B,并且函数 B 中使用了函数 A 的变量,函数 B 就被称为闭包。

    函数 A 弹出调用栈后,函数 A 中的变量这时候是存储在堆上的,所以函数B依旧能引用到函数A中的变量。现在的 JS 引擎可以通过逃逸分析辨别出哪些变量需要存储在堆上,哪些需要存储在栈上。

    引用数据类型与堆内存

    与其他语言不同,JS的引用数据类型,比如数组Array,它们值的大小是不固定的。引用数据类型的值是保存在堆内存中的对象。JavaScript不允许直接访问堆内存中的位置,因此我们不能直接操作对象的堆内存空间。在操作对象时,实际上是在操作对象的引用而不是实际的对象。因此,引用类型的值都是按引用访问的。这里的引用,我们可以粗浅地理解为保存在变量对象中的一个地址,该地址与堆内存的实际值相关联。

    为了更好的搞懂变量对象与堆内存,我们可以结合以下例子与图解进行理解.

    var b = { m: 20 }; // 变量b存在栈中,对应的值就是一个索引指向对象{m: 20},{m:20}作为对象存在于堆内存中.
    深入理解js内存机制

    因此当我们要访问堆内存中的引用数据类型时,实际上我们首先是从变量对象中获取了该对象的地址引用(或者地址指针),然后再从堆内存中取得我们需要的数据。

    如此,就会出现我们经常被问到的关于引用数据类型的值问题了

    var x =30;
    var b = x;
    x+=10;
    console.log(b);
    

    上面的问题我们很容易解答,答案就是会输出30,x的操作不会对b有什么影响,因为在变量对象中的数据发生复制行为时,系统会自动为新的变量分配一个新值。var b = a执行之后,a与b虽然值都等于20,但是他们其实已经是相互独立互不影响的值了。但是下面这个问题就有意思了

    var x={m:1}
    var y = x;
    x.m++;
    console.log(y.m);
    

    通过输出我们发现答案是2。这是因为我们通过var y = x是执行一次复制引用类型的操作。引用类型的复制同样也会为新的变量自动分配一个新的值保存在变量对象中,但不同的是,这个新的值,仅仅只是引用类型的一个地址指针。当地址指针相同时,尽管他们相互独立,但是在变量对象中访问到的具体对象实际上是同一个。

    垃圾回收

    在js中有垃圾回收机制,其作用是回收过期无效的变量,以防止内存泄漏。这些工作不需要我们去管理什么时候进行垃圾回收,js会自动进行,这让我们写起代码来感觉超级爽,哈哈。

    下面来看一下js垃圾回收机制什么时候会回收变量。我们写代码的时候是区分全局变量和局部变量的,在此,我们看一下局部变量和全局变量的销毁。

    • 局部变量:局部作用域中,当函数执行完毕,局部变量也就没有存在的必要了,因此垃圾收集器很容易做出判断并回收。
    • 全局变量:全局变量什么时候需要自动释放内存空间则很难判断,所以在开发中尽量避免使用全局变量,以提高内存有效使用率

    垃圾回收算法

    现在各大浏览器通常用采用的垃圾回收有两种方法:标记清除、引用计数。

    引用计数

    现代浏览器基本上已经不再使用了,在这里我们做一下简单的介绍。引用计数的含义是跟踪记录每个值被引用的次数。当声明了一个变量并将一个引用类型赋值给该变量时,则这个值的引用次数就是1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数就减1。当这个引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其所占的内存空间给收回来。这样,垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占的内存。简单来说就是看一个对象是否有指向它的引用。如果没有其他对象指向它了,说明该对象已经不再需要了。

    // 创建一个对象person,他有两个指向属性age和name的引用
    var person = {
        age: 12,
        name: 'aaaa'
    };
    person.name = null; // 虽然name设置为null,但因为person对象还有指向name的引用,因此name不会回收
    var p = person;
    person = 1;         //原来的person对象被赋值为1,但因为有新引用p指向原person对象,因此它不会被回收
    p = null;           //原person对象已经没有引用,很快会被回收
    

    但是,如果出现了循环引用,那么这种方式就会存在一个大bug,看一下下面这个例子

    function bigBug(){
        var objA = new Object();
        var objB = new Object();
        objA.bug1 = objB;
        objB.bug2 = objA;
    }
    

    在上面这个例子中,ab两个对象是互相引用的,也就是说他们的引用次数永远为2,如果不进行其他操作的话,这样的互相引用如果大量使用的话,就会造成内存泄漏问题。虽然说现在主流的浏览器都已经不在使用了,但是之前的IE版本还是那样,所以在写代码时我们应该尽量避免。避免的方法就是在不使用的时候进行手动解除循环绑定

    objA.bug1 = null;
    objB.bug2 = null;
    

    标记清除

    标记清除算法将“不再使用的对象”定义为“无法到达的对象”。即从根部(在JS中就是全局对象)出发定时扫描内存中的对象,凡是能从根部到达的对象,保留。那些从根部出发无法触及到的对象被标记为不再使用,稍后进行回收。每一个变量都有自己的使用环境,当进入环境以后,垃圾回收机制就会给他打上一个“进入环境”的标签,从逻辑上来将,系统不能清除处于环境中的变量,因为只要是在环境中就有可能会使用到。当其离开环境时,会给其打上“离开环境”标签,这时候便可以进行回收了。

    内存泄漏

    内存泄漏可能对于前端开发着来说比较陌生,但是你肯定遇到过浏览器卡死的现象,卡死的原因就可能是因为一个死循环导致的内存爆满泄漏。所以对于持续运行的服务进程(daemon),必须及时释放不再用到的内存。否则,内存占用越来越高,轻则影响系统性能,重则导致进程崩溃。 对于不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)
    如果想模拟的话,可以按下面的步骤来进行操作:

    • 打开开发者工具,选择 Memory
    • 在右侧的Select profiling type字段里面勾选 timeline
    • 点击左上角的录制按钮。
    • 在页面上进行各种操作,模拟用户的使用情况。
    • 一段时间后,点击左上角的 stop 按钮,面板上就会显示这段时间的内存占用情况。

    深入理解js内存机制

    展开全文
  • Java的内存机制

    千次阅读 2018-12-21 14:28:40
    Java的内存机制 Java 把内存划分成两种:一种是栈内存,另一种是堆内存。在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量...

    Java的内存机制

    Java 把内存划分成两种:一种是栈内存,另一种是堆内存。在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量分配内存空间,当超过变量的作用域后(比如,在函数A中调用函数B,在函数B中定义变量a,变量a的作用域只是函数B,在函数B运行完以后,变量a会自动被销毁。分配给它的内存会被回收),Java 会自动释放掉为该变量分配的内存空间,该内存空间可以立即被另作它用。

    堆内存用来存放由 new 创建的对象和数组,在堆中分配的内存,由 Java 虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或者对象之后,还可以在栈中定义一个特殊的变量,让栈中的这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或者对象,引用变量就相当于是为数组或者对象起的一个名称。引用变量是普通的变量,定义时在栈中分配,引用变量在程序运行到其作用域之外后被释放。而数组和对象本身在堆中分配,即使程序运行到使用 new 产生数组或者对象的语句所在的代码块之外,数组和对象本身占据的内存不会被释放,数组和对象在没有引用变量指向它的时候,才变为垃圾,不能在被使用,但仍然占据内存空间不放,在随后的一个不确定的时间被垃圾回收器收走(释放掉)。这也是 Java 比较占内存的原因,实际上,栈中的变量指向堆内存中的变量,这就是 Java 中的指针!

    代码实例Test01:单个对象创建

    在上述程序中实例化了一个对象per,在实例化对象的过程中需要在内存中开辟空间,这其中就包括栈内存和对内存。具体的内存分配如下图所示:

    我们可以从上图中发现,对象名称per被保存在了栈内存中(更加准确的说法是,在栈内存中保存的是堆内存空间的访问地址),而对象的具体内容,比如属性name和age,被保存在了堆内存中。因为per对象只是被实例化,还没有具体被赋值,所以都是默认值。字符串的默认值为null,int类型的默认值为0。前面也已经提到,堆内存空间必须使用new关键字才能开辟。

    代码实例Test02:多个对象创建

    关键概念:类跟数组一样,都是属于引用类型,引用类型就是指一堆对内存可以同时被多个栈内存指向。下面来看一下引用传递的简单实例。

    代码实例Test03:对象引用传递1

    程序运行结果为:

    姓名:张三,年龄:33

    姓名:张三,年龄:33

    从程序的运行结果可以发现,两个对象输出的内容一样,实际上所谓的引用传递,就是将一个堆内存空间的使用权交个多个栈内存空间,每个栈内存空间都可以修改堆内存空间的内容,此程序的内存分配图如下所示:

    注意:上述实例中对象per2没有堆内存空间,这是因为对象per2只进行了声明操作,也没有进行实例化操作。只有使用new关键字实例化以后才会有对内存空间。

    代码实例Test04:对象引用传递2

    上述程序运行结果为:

    姓名:张三,年龄:30

    姓名:张三,年龄:30

    从程序的输出结果可以发现可Test03一样。不过内存分配发生了一些变化,具体如下所示:

    java中常用的内存区域

    注意点:

    Java本身提供垃圾收集机制(Garbage Collection,GC),会不定期施放不用的内存空间,只要对象不用了,就会等待GC释放空间,如上面堆内存中的name="李四";age=33。

    一个栈内存只能指向一个对内存空间,如果要想再指向其他的堆内存空间,则必须先断开已有的指向才能分配新的指向。

    在java中主要存在4块内存空间,这些内存的名称及作用如下:

    栈内存空间:保存所有的对象名称(更准确地说是保存了引用的堆内存空间的地址)

    堆内存空间:保存每个对象的具体属性内容。

    全局数据区:保存static类型的属性。

    全局代码区:保存所有的方法定义。

    展开全文
  • Windows 内存机制说明

    千次阅读 2018-11-06 22:41:29
    1.内存分布概述  每个进程都有自己的虚拟地址空间,对于32位进程来说,这个地址空间的大小为4GB,这是因为32位指针可以表示从0x00000000到0xFFFFFFFF之间的任意一值。对于64位进程来说,由于64位指针可以表示从0x...

    1.内存分布概述

           每个进程都有自己的虚拟地址空间,对于32位进程来说,这个地址空间的大小为4GB,这是因为32位指针可以表示从0x00000000到0xFFFFFFFF之间的任意一值。对于64位进程来说,由于64位指针可以表示从0x00000000’00000000到0xFFFFFFFF’FFFFFFFF之间的任一值,因此这个地址空间大小为16EB。因为每个进程都有自己专用的地址空间,当进程中的各线程运行时,它们只能访问属于该进程的内存。线程既看不到属于其他进程的内存,也无法访问它们。

    2.虚拟地址空间分区

           应用程序虽然有这么大的地址空间可用,但是这只是虚拟地址空间,不是物理存储器。这个地址空间只不过是一个内存的地址区间。

           每个进虚拟地址空间被划分为许多分区(partion)。由于地址空间的分区依赖于操作系统的底层实现。因此会随着Windows内核的不同而略有变化。

    分区

    x86 32

    Windows

    3G用户模式下的

    x86 32位Windows

    x64 64位Windows

    IA-64 64位的 Windows

    空指针

    赋值分区

    0x00000000

    0xFFFFFFFF

    0x00000000

    0xFFFFFFFF

    0x00000000’00000000

    0x00000000’0000FFFF

    0x00000000’00000000

    0x00000000’0000FFFF

    用户模式

    分区

    0x00010000

    0x7FFEFFFF

    0x00010000

    0xBFFEFFFF

    0x00000000’00010000

    0x000007FF’FFFFFFFF

    0x00000000’00010000

    0x000006FB’FFFEFFFF

    64K禁入

    分区

    0x7FFF0000

    0x7FFFFFFF

    0xBFFF0000

    0xBFFFFFFF

    0x000007FF’FFFF0000

    0x000007FF’FFFFFFFF

    0x000006FB’FFFF0000

    0x000006FB’FFFFFFFF

    内核模式

    分区

    0x80000000

    0xFFFFFFFF

    0xC0000000

    0xFFFFFFFF

    0x00000800’00000000

    0xFFFFFFFF’FFFFFFFF

    0x000006FC’00000000

    0xFFFFFFFF’FFFFFFFF

          从上表中可能看到,32位Winows内核和64位Windows内核的分区基本一致,唯一的不同在于分区的大小和分区的位置。

    内存使用

     

    2.1.空指针赋值分区

           这一分区是进程地址空间中从0x00000000到0x0000FFFF的闭区间,保留该分区的目的是为了帮助程序捕获对于空指针的赋值。没有任何办法可以让我们分配到位于这一地址空间的虚拟内存,即使是使用Win32的应用程序编程接口(appliction programming interface,通常简称API)也不例外。如果进程中的线程试图读取或写入于这一分区内的内存地址,就会引发访问违规。

            如果malloc无法分配足够的内在,那么它会返回NULL。地址空间中的这一分区是禁止访问的,所以会引发内存访问违规并导致进程被终止。这一特性可以帮助开发人员发现应用程序中的缺陷。

    2.2.用户模式分区

            这一分区是进程地址空间的驻地,可用的地址空间和用户模式分区的大小取决于CPU体系结构。进程无法通过指针来读取、写入或以任何方式,访问驻留在这一分区中的其他进程的数据。对所有应用程序来说,进程的大部分数据都保存在这一分区。由于每个进程都有自己的数据分区。因此一个应用程序破另一个应用程序的可能性就非常小,从而使得整个系统更加坚固。

           在早期版本的Windows中,Microsoft不允许用户程序访问2GB以上的地址空间,为了让此类应用程序即使在用模式分区大于2GB的环境下仍能正常运行,Microsoft提供代了一种模式来增大用户模式分区,最多不超过3GB。

           当前系统即将运行一个应用程序时,它会检查应用程序在链接时是否使用了/LARGEADDRESSAWARE链接器开关。如果是,则相应于应用程序在声明它会充分利用大用户模式地址空间,而不会对内在地址进行任何不当的操作。反之,如果应用程序在链接时没有使用/LARGEADDRESSAWARE开关,那么操作系统会保留用户模式分区中2GB以上到内核模式开始处的整个部分。

    2.2.1.程序内存使用分布

    栈

    可参考https://en.wikipedia.org/wiki/Data_segment#Program_memory

    2.2.1.1.Text

    【原文】

    The code segment, also known as a text segment or simply as text, is where a portion of an object file or the corresponding section of the program's virtual address space that contains executable instructions is stored and is generally read-only and fixed size.

           代码段,通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读。

          在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等。

           代码段,也称为文本段,或简称为文本,是指存储有可执行指令的对象文件或程序的虚拟地址空间的相应部分的一部分,并且通常是只读的和固定大小的。

    2.2.1.2.Data

    【原文】

    The .data segment contains any global or static variables which have a pre-defined value and can be modified. That is any variables that are not defined within a function (and thus can be accessed from anywhere) or are defined in a function but are defined as static so they retain their address across subsequent calls. Examples, in C, include:

    int val = 3;

    char string[] = "Hello World";

    The values for these variables are initially stored within the read-only memory (typically within .text) and are copied into the .data segment during the start-up routine of the program.

           数据段包含任何具有预定义值并可被修改的全局变量或静态变量。这是任何函数中未定义的变量(因此可以从任何地方访问)或在函数中定义,但被定义为静态的,因此它们在随后的调用中保留它们的地址。C中的例子包括:

    int val = 3;

    char string[] = "Hello World";

           这些变量的值最初存储在只读存储器(通常在.text中),并在程序的启动例程期间复制到.DATA段。

    2.2.1.3.BSS

    【原文】

    The BSS segment, also known as uninitialized data, is usually adjacent to the data segment. The BSS segment contains all global variables and static variables that are initialized to zero or do not have explicit initialization in source code. For instance, a variable defined as static int i; would be contained in the BSS segment.

            BBS段,也称为未初始化数据段,通常与数据段相邻,包含初始化为或在源码中未进行显式初始化的全局变量和静态变量。例如,定义为静态整型变量i将被包含在BSS段中。

    2.2.1.4.Heap

    【原文】

    The heap area commonly begins at the end of the .bss and .data segments and grows to larger addresses from there. The heap area is managed by malloc, calloc, realloc, and free, which may use the brk and sbrk system calls to adjust its size (note that the use of brk/sbrk and a single "heap area" is not required to fulfill the contract of malloc/calloc/realloc/free; they may also be implemented using mmap/munmap to reserve/unreserve potentially non-contiguous regions of virtual memory into the process' virtual address space). The heap area is shared by all threads, shared libraries, and dynamically loaded modules in a process.

            堆区域通常在.BSS和.DATA段的末尾开始,并从那里扩展到更大的地址。堆区域由Maloc、CaloC、ReLoLc和空闲管理,可以使用BRK和SBRK系统调用来调整其大小(注意使用BRK/SBRK和单个堆区)不需要履行MalC/CaloC/ReAlOLC/FILE的合同;也可以使用MMAP/MunMMAP来实现。将虚拟内存的潜在非相邻区域保留/不保留到进程的“虚拟地址空间”中。堆区域由进程中的所有线程、共享库和动态加载模块共享。

    2.2.1.5.Stack

    【原文】

    Main article: Call stack

    The stack area contains the program stack, a LIFO structure, typically located in the higher parts of memory. A "stack pointer" register tracks the top of the stack; it is adjusted each time a value is "pushed" onto the stack. The set of values pushed for one function call is termed a "stack frame". A stack frame consists at minimum of a return address. Automatic variables are also allocated on the stack.

    The stack area traditionally adjoined the heap area and they grew towards each other; when the stack pointer met the heap pointer, free memory was exhausted. With large address spaces and virtual memory techniques they tend to be placed more freely, but they still typically grow in a converging direction. On the standard PC x86 architecture the stack grows toward address zero, meaning that more recent items, deeper in the call chain, are at numerically lower addresses and closer to the heap. On some other architectures it grows the opposite direction.

     主要文章:调用堆栈

              堆栈区域包含程序栈,即LIFO结构,通常位于内存的较高部分。一个“堆栈指针”寄存器跟踪堆栈的顶部;每当一个值被“推”到堆栈时,它就被调整。一个函数调用所推的值集称为“堆栈框架”。堆栈帧包括返回地址的最小值。自动变量也被分配到堆栈上。

           堆栈区域传统上毗邻堆区域,它们彼此生长;当堆栈指针遇到堆指针时,空闲内存被耗尽。随着大的地址空间和虚拟内存技术,它们倾向于更自由地放置,但它们仍然通常在收敛的方向上生长。在标准的PC x86架构上,堆栈向地址零增长,这意味着在调用链中较近的项在数值较低的地址上更接近堆。在其他一些架构上,它生长了相反的方向。

    2.3.内核模式分区

            这一分区是操作系统代码的驻地,与线程调度、内存管理、文件系统支持、网络支持以及设备驱动程序相关的代码都载入到该分区。在这一分区内的任何东西为所有进程共有。虽然这一分区就在每个进程中用户模式分区的上方,但该分区中的所有代码和数据都被完全保护起来。如果一个应用程序试图读取或写入这一分区中的内存地址,会引发违规。在默认情况下,访问违规会导致系统先向用户显示一个消息框,然后结束应用程序。

    3.堆栈段说明
    3.1.栈(Stack)
           当系统创建线程时,会为线程栈预订一块空间区域(每个线程都有自己的栈),并给区域调拨一些物理存储器。默认情况下,系统会预订1MB的地址空间并调拨两个页面的存储器。但是,在构建应用程序时开发人员可以通过两种方法来改变该默认值,一种方法是使用Microsoft C++编译器的/F选项,另一种方法是使用Microsoft C++链接器的/STACK选项。在构建应用程序时,链接器会把想要的栈的大小写入到.exe或.dll文件的PE文件头中。当前系统创线栈的时候,会根据PE文件头中的大小来预订地址空间的区域。但是在调用CreateThread或_beginthreadex函数时,开发人员也可以另外指定需要在一开始就调拨的存储器数量。这两个函数都有一个参数,可以用来指定一开始要调拨给线程栈的地址空间区域的存储器的大小。如果该参数设为0,那么系统会使用PE文件头中指定的大小,所使用的都是默认值(即区域大小为1MB),每次调拨一个存储页面。
    3.2.堆(Heap)
           堆非常适用分配大量的小型数据。堆是用来管理链表和树的最佳方式。堆的优点是它能让我们专心解决手头上的问题,而不必理会分配粒度和页面边界这类事情。堆的缺点是分配和释放内存块的速度比其他方式慢,而且也无法对物理存储器的调拨和摊销进行直接控制。
           进程初始化时候,系统会在进程的地址空间中创建一个堆,这个堆被称为进程的默认堆(Default heep)。在默认情况下,这个堆的地址空间区域的大小是1MB。但是系统可以增大进程的默认堆,使它大于1MB。我们也可以在创建应用程序的时候用/HEAP链接器开关来改变默认区域大小。由于动态链接库(.DLL)没有与之关联的堆,因此在创建DLL的时候不应使用/HEAP开关。

    展开全文
  • 图解VMware内存机制

    千次阅读 2017-09-27 09:16:25
    在写《VMware内存机制初探》之后,原本是计划写一篇《VMware内存机制再探》的,讲一讲VMware内存机制中的另外几个重要内容,比如透明内存共享(TPS, Transparent Page Sharing), Relaim Memory, Ballooning, swapping...

    原文出处: http://delxu.blog.51cto.com/975660/288682

    在写《 VMware内存机制初探》之后,原本是计划写一篇《VMware内存机制再探》的,讲一讲VMware内存机制中的另外几个重要内容,比如透明内存共享(TPS, Transparent Page Sharing), Relaim Memory, Ballooning, swapping等等。但有网友反映说前面的文章还是不好懂。于是想,如果如同官方文档那样条条框框地列出来,那还不如大家都去看原版手册呢,所以有了这么一篇东西。

    首先,大家要记住,在内存没有过量配置(Memory Overcommitment)的情况下,内存的调度机制完全不会启动,就没有Reclaim内存。很明显嘛,主机总的物理内存(Host Physical Memory)大于所有虚机配置内存的总额的情况下,每台虚机想要多少内存,都能得到满足,当然不需要调度。

    所以,以下探讨的VMware的内存机制,都是在内存过量配置的情况下发生的。

    我的故事发生在一个有智慧的水池(Host)中,水池有不少水(4GB物理内存),里面还有2个水箱(配置了2台VM),水箱有一定的容量(配置内存是4GB),原本是空的(没有开机)。
    snap0049

    但是现在水箱1里面要养鱼了,必须放点水进去以便鱼可以存活(开机了)。最少需要1GB内存。于是水箱1(VM1)就向水池(Host)要水(物理内存),水池里面有足够的水,就满足了水箱1的要求。现在水箱1有1GB的活水,而水池里面只剩下3GB的水了。
    snap0050
     
    现在我们又向水箱1里面多丢了些鱼(启动新的应用),原来1GB的水不够用了,于是水箱1就继续向水池要水,水池里面还有足够的水,就又满足了水箱1的要求。现在水箱1里面有3GB的水,而水池里面只剩下1GB的水了。


    解释:要注意的是,此时VM1里面的应用程序都是活动的(active),所以这些内存都属于活跃状态,叫做活动内存(Active Memory)。
    snap0051
    经过了一段时间以后,水箱1里面的鱼被捕走了,现在水箱1不需要那么多水也足够养活剩下的鱼了。但是水池并不知道水箱1的状况。于是水箱1还是有那么多水。

    解释:
    VM1的某些应用结束后,释放了部分内存,但是这些内存释放动作是在VM的Guest OS层面释放的,因此Host并不知道有内存被释放了,这些内存没有归还Host,仍然由VM1霸占着。VM1中就有一部分内存属于idle,另外一些正在使用的内存就是active memory。当然,就VM1自己的GOS看起来,有3GB空闲内存(idle memory),1GB的活动内存;而此时就Host看来,只看见有3GB内存是分配给了VM1的。Host通过VMware Tools中的驱动,每隔一定的时间(ESX4中默认是60秒,ESX3中默认是30秒)去扫描一下VM1的内存使用状态,以便了解它到底有多少活动内存(active memory)

    snap0052
     
    此时,我们开始在水箱2中养鱼了(VM2开机),水箱2也开始从水池中抽水。但是水池里面的水不能枯竭,因此水池有警戒水位,第一条警戒水位是6%,当下降到第一警戒水位以下并仍然在不停下降时,就要开动其他机制从其他水箱反抽水。

    解释:
    VM2开机时也需要1GB内存,在启动时,它也不断向Host请求内存。Host则将自己的内存源源不断地分配给VM2,直到下降到第一条警戒位6%。Host内存有4种状态,分别是High, Soft, Hard和Low,它们间的分界线分别是6%, 4%, 2%和1%。可用内存高于6%时,不会启动Balloon或Swap机制。当低于6%并往4%逼近的时候(soft状态),Balloon机制就启动了。(关于内存的4种状态的更多解释,请参阅官方文档《Understanding Memory Resource Management in VMware ESX Server》。如何查看这4种状态?可以用esxtop或者resxtop)

    image
    那到底向哪个水箱抽水呢?谁的水最富裕就向谁抽。

    解释:
    如何判断呢?刚才我们说过,Host每隔一定时间就会扫描Guest OS的内存使用状况,此时,Host会计算每个VM的份额内存比ρρ和VM的空闲内存还有空闲内存税(IMT, Idel Memory Tax)密切相关,对ρ最小的那台虚机启动气球驱动(balloon driver),气球开始膨胀(inflating)。关于ρ和IMT的算法,请参见本人博文《 空闲内存税的算法 》。

    snap0054
    于是,气球开始膨胀,并将多余的水挤出水箱。

    解释:Balloon驱动如同普通驱动那样,不断向Guest OS索取内存,Guest OS尽自己可能满足气球驱动,并优先将空闲部分的内存分配给它,注意,此时可能出现Guest OS自己的物理内存不足,并引起Guest OS的paging机制,将一部分内存page out到自己的swap中。这个例子中,VM1还有很多空闲内存,因此足够让主机回收并满足VM2的需求。

    主机将比较气球膨胀获得的这部分内存的地址,如果原先是分配给VM1独享的,(也就是没有TPS共享),那么就可以收回。主机将不停的通过气球驱动收回内存,收回的目标是保持至少6%的内存。这个回收内存的过程就叫Reclaim。

    注意:此时内存没有出现争用情况,因此shares仍然没有起作用,但是Overcommitment是存在的,所以会有reclaim,会有ballooning。
    snap0056
    Ballooning回收内存是比较慢的,通常需要几分钟。

    如果主机可用内存池的内存减少速度大于气球驱动返还主机的内存,那么可用内存会进一步下降,当突破4%的第2警戒线时,进入到hard状态,主机就会启用第2种reclaim机制——swapping,将VM1的一部分物理内存交换到swap文件中,以加快回收内存的速度。目的是将可用内存保持在4%的警戒线以上。

    如果可用内存继续下降到2%以下,进入到low状态的时候,ESX不仅会继续加速Swapping,还会阻止其上所有VM的内存请求。

    现在,情况又发生了变化,水箱2中的鱼越来越多了,它不停地向水池要水,而水池也通过气球驱动,不停地将水箱1中的空闲内存挤出来,直到水箱1和2的总需求量大于了水池能供给的水量。水池再也不能提供更多的水了,只能部分满足2个水箱的要求了。不够的部分怎么办?用一些脏水(swap)来满足。脏水虽然脏(swap内存速度很慢),鱼在脏水里面生存的很困难,但毕竟还是有水的,不至于因为没有足够的水而导致鱼死掉。
    snap0058

    此时,VM1和VM2的active memory由2部分组成,1部分是获得的主机物理内存,另一部分是swap。请注意,Guest OS是不知道自己的一部分物理内存是硬盘上的swap文件的。

    当机器内存不足竞争开始的时候,shares开始起作用。但是,请注意,如果IMT是0,对空闲内存不征税,由于4GB的VM1和4GB的VM2的份额都是40960,因此它们最终会拿到相同的物理内存2GB。但是如果IMT是默认值,那么空闲内存的代价更大,那么这2台VM会根据active内存数量的不同,获得不同数量的物理内存。
     
    【本文的知识要点回顾】

    (1) 在内存没有过量配置(Memory Overcommitment)的情况下,不需要Reclaim内存
    (2) 什么时候开始Reclaim内存?当突破6%的警戒线,内存状态从High变成了Soft的时候
    (3) Reclaim优先用Ballooning,只有Ballooning不够用的时候,才会用Swapping
    (4) 什么时候开始swapping? 当主机可用内存跌破4%警戒线,内存状态变成Hard的时候
    (5) Host无法知道VM内的哪些内存块已经处于空闲(idle)状态。必须用Ballooning才能收回空闲内存。
    (6) 尽量避免资源争用,否则造成的Chasing-the-tail效应,会导致更严重的性能负面影响。

    【参考文档】

    (1) Carl, A. Waldspurger, 2002, Memory Resource Management in VMware ESX Server
    http://waldspurger.org/carl/papers/esx-mem-osdi02.pdf
    (2) VMware Inc.  Understanding Memory Resource Management in VMware ESX Server
    http://www.vmware.com/files/pdf/perf-vsphere-memory_management.pdf
    (3
    ) VMware Inc.  Understanding Host & Guest Memory Usage (2007)
    http://mylearn.vmware.com/courseware/12400/PS_TA21_288707_166-1_FIN_v3.pdf
    (4) Idel memory tax
    http://www.boche.net/blog/index.php/2009/01/29/idle-memory-tax/

    本文出自 “三角阳台的技术笔记本” 博客,请务必保留此出处http://delxu.blog.51cto.com/975660/288682

    展开全文
  • 理解虚拟内存机制

    千次阅读 2016-12-21 13:11:08
    Linux虚拟内存机制 Linux把虚拟内存划分成区域area的集合,每个存在的虚拟页面都属于一个area。一个area包含了连续的多个页。Linux通过area相关的数据结构来灵活地管理虚拟内存。 内核为每个进程维护...
  • C#内存机制

    千次阅读 2018-03-25 19:17:52
    由于C#是一种托管语言,它的垃圾回收机制(GC)是由.net平台负责的,加之C#语言并没有指针,所以我们在使用过程中极少会考虑到内存使用状况以及项目在运行过程中是如何进行内存管理的。但是,C#只是在内存管理方面对...
  • Linux 虚拟内存机制

    万次阅读 2016-08-09 21:31:01
    华为面试官问了我一个问题就是关于Linux虚拟内存机制,虽然我多少是回答上来,感觉好久没看操作系统的我是时候将其拿起来重温一遍 。 每个进程都有自己独立的4G内存空间,各个进程的内存空间具有类似的结构。 ...
  • 深入理解虚拟内存机制

    万次阅读 2018-09-11 18:14:10
    现代操作系统了提供了一种对主存的抽象概念,叫做虚拟内存。它为每个进程提供了一个非常大的,一致的和私有的地址空间。虚拟内存提供了以下的三个关键能力: 它将主存看成是一个存储在磁盘空间上的地址空间的高速...
  • JVM内存机制(面试)

    千次阅读 2018-04-16 11:03:17
    JVM的内存区域划分 学过C语言的朋友都知道C编译器在划分内存区域的时候经常将管理的区域划分为数据段和代码段,数据段包括堆、栈以及静态数据区。那么在Java语言当中,内存又是如何划分的呢? 由于Java程序是交由...
  • 浅谈CPU寻址内存机制

    千次阅读 2018-10-07 10:49:57
    本文讲解的内容是Processor如何访问内存,TLB Cache和MMU的在Processor中扮演的角色。涉及的硬件平台是Xilinx Zynq-7000,dual-core ARM® Cortex-A9 MPCore,架构是armv7,下面分别对TLB、MMU、Processor如何访问...
  • 虚拟内存机制

    万次阅读 2017-11-13 14:23:24
    虚拟内存机制 1.为什么要有虚拟内存 在早期的计算机中,是没有虚拟内存的概念的。我们要运行一个程序,会把程序全部装入内存,然后运行。 当运行多个程序时,经常会出现以下问题: 1)进程地址空间不隔离,...
  • 浅析Java中的内存机制

    千次阅读 2016-11-26 20:01:25
    要了解Java中对象在内存中的位置,首先要分清楚两个概念,对象和引用。在Java中,使用new Object()来产生一个新的对象,这时,对象存在于堆内存中。而使用Object object仅仅产生了一个Object引用,引用存在于栈内存...
  • documentlinux内存机制CPU内存虚拟内存硬盘物理内存内存和虚拟内存跟 Windows 完全不同的 Linux 内存机制Swap配置对性能的影响 linux内存机制 Linux支持虚拟内存(Virtual Mmemory),虚拟内存是指使用磁盘当作RAM的...
  • 1.Java的内存机制  Java 把内存划分成两种:一种是栈内存,另一种是堆内存。在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配,当在一段代码块定义一个变量时,Java 就在栈中为这个变量...
  • 计算机底层知识拾遗(一)理解虚拟内存机制 http://blog.csdn.net/iter_zc/article/details/42644229 这个系列会总结计算机,网络相关的一些重要的底层原理。很多底层原理大家上学的时候都学过,但是在...
  • Android内存机制—堆和栈

    千次阅读 2017-01-18 16:07:27
    Android 内存机制-堆和栈
  • 图解VMware内存机制(VMware的重要知识)

    千次阅读 2013-11-07 11:36:17
    在写《VMware内存机制初探》之后,原本是 计划写一篇《VMware内存机制再探》的,讲一讲VMware内存机制中的另外几个重要内容,比如透明内存共享(TPS, Transparent Page Sharing), Relaim Memory, Ballooning, ...
  • 关于 python 的内存机制

    万次阅读 2019-02-21 13:46:51
    先看一段代码 L = [1,2,3] dic_ = {} L2 = [] for i in L: dic_['sn'] = i L2.append(dic_) ...因为这个 dic_ 自始至终 都只用了一块内存来存储这个值,而对 dic_ 这个存储的变量进行操作时,必然会影响到...
  • Java内存机制学习和使用Java内存机制学习和使用Java内存机制学习和使用
  • Android 内存管理机制

    千次阅读 2018-09-03 14:41:45
    Linux的内存管理机制:Android毕竟是基于Linux内核实现的操作系统,因此有必要了解一下Linux的内存管理机制。 Android的内存管理相关知识:Android又不同于Linux,它是一个移动操作系统,因此其内存管理上也有...
  • Android内存管理机制官方详解文档

    千次阅读 2020-12-09 12:40:54
    很早之前写过一篇《Android内存管理机制详解》点击量已7万+,现把Google官方文档整理输出一下,供各位参考。 一、内存管理概览 Android 运行时 (ART) 和 Dalvik 虚拟机使用分页和内存映射来管理内存。这意味着应用...
  • Linux内存管理机制(最透彻的一篇)

    万次阅读 多人点赞 2018-08-05 14:10:09
    摘要:本章首先以应用程序开发者的角度审视Linux的进程内存管理,在此基础上逐步深入到内核中讨论系统物理内存管理和内核内存的使用方法。力求从外到内、水到渠成地引导网友分析Linux的内存管理与使用。在本章最后,...
  • vector内存机制和性能分析

    万次阅读 热门讨论 2013-04-01 17:14:26
    关于vector,简单地讲就是一个动态数组,里面有一个指针指向一片连续的内存空间,当空间不够装下数据时会自动申请另一片更大的空间,然后把原有数据拷贝过去,接着释放原来的那片空间;当释放或
  • Linux之用信号量实现的共享内存机制

    千次阅读 2018-03-09 16:59:19
    今天要介绍的共享内存机制就是IPC三大机制之一。 一.共享内存先知 共享内存是在两个正在运行的进程之间传递数据的一种非常有效的方法,它允许两个不想管的进程访问同意逻辑内存,虽然X/Open标准并没有对它做出要求...
  • spark 的内存管理机制

    千次阅读 2018-08-06 09:56:55
    1. 堆内和堆外内存规划 作为一个 JVM 进程,Executor 的内存管理建立在 JVM 的内存管理之上,Spark 对 JVM 的堆内(On-heap)空间进行了更为详细的分配,以充分利用内存。同时,Spark 引入了堆外(Off-heap)内存,...
  • Android内存管理机制

    千次阅读 2018-06-29 12:29:42
    解读 Andriod 内存管理机制 一、进程类型 1、前台进程(foreground):目前正在屏幕上显示的进程和一些系统进程。举例来说, Dialer Storage,Google Search
  • 什么是Python的 “内存管理机制

    千次阅读 2020-07-27 17:48:00
    什么是内存管理器(what)Python作为一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言,与大多数编程语言不同,Python中的变量无需事先申明,变量无需指定类型,程序员...
  • C语言的内存管理机制

    万次阅读 多人点赞 2018-07-22 15:39:30
    内存资源是非常有限的。尤其对于移动端开发者来说,硬件资源的限制使得其在程序设计中首要考虑的问题就是如何有效地管理内存资源。本文是作者在学习C语言内存管理的过程中做的一个总结。 变量概念: 全局变量...
  • python内存管理机制

    千次阅读 2019-04-09 00:17:58
    1. 内存管理架构 第0层: 是操作系统提供的内存管理接口,比如c运行时提供的malloc和free接口。这一层是由操作系统实现并管理的,python不能干涉这一层的行为。 第1层:基于第0层操作系统提供的内存管理接口包装...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,160,552
精华内容 464,220
关键字:

内存机制