内存碎片

链表知识总结

岁酱吖の 提交于 2020-01-08 09:34:39
一、什么是链表? 和数组一样,链表也是一种线性表。 从内存结构来看,链表的内存结构是不连续的内存空间,是将一组零散的内存块串联起来,从而进行数据存储的数据结构。 链表中的每一个内存块被称为节点Node。节点除了存储数据外,还需记录链上下一个节点的地址,即后继指针next。 二、为什么使用链表?即链表的特点 插入、删除数据效率高O(1)级别(只需更改指针指向即可),随机访问效率低O(n)级别(需要从链头至链尾进行遍历)。 和数组相比,内存空间消耗更大,因为每个存储数据的节点都需要额外的空间存储后继指针。 三、常用链表:单链表、循环链表和双向链表 1.单链表 每个节点只包含一个指针,即后继指针。 单链表有两个特殊的节点,即首节点和尾节点。为什么特殊?用首节点地址表示整条链表,尾节点的后继指针指向空地址null。 性能特点:插入和删除节点的时间复杂度为O(1),查找的时间复杂度为O(n)。 2.循环链表 除了尾节点的后继指针指向首节点的地址外均与单链表一致。 适用于存储有循环特点的数据,比如约瑟夫问题。 3.双向链表 节点除了存储数据外,还有两个指针分别指向前一个节点地址(前驱指针prev)和下一个节点地址(后继指针next)。 首节点的前驱指针prev和尾节点的后继指针均指向空地址。 性能特点: 和单链表相比,存储相同的数据,需要消耗更多的存储空间。 插入、删除操作比单链表效率更高O

[go]内存对齐

余生长醉 提交于 2020-01-07 18:29:31
参考 参考-校对版 理解类型的本质 当我使用 C/C++ 编写代码时,理解类型(type)是非常有必要的。如果不理解类型,你就会在编译或者运行代码的时候,碰到一大堆麻烦。无论什么语言,类型都涉及到了编程语法的方方面面。 加强对于类型和指针的理解,对于提高编程水平十分关键。 本文会主要讲解类型。 我们首先来看看这几个字节的内存: 内存地址: FFE4 FFE3 FFE2 FFE1 存储的值: 00000000 11001011 01100101 00001010 请问地址 FFE1 上字节的值是多少? 如果你试图回答一个结果,那就是错的。为什么?因为我还没有告诉你这个字节表示什么。(不同类型的含义不同) 回答 10,那么你又错了。为什么?因为当我说这是数字的时候,你认为我是指十进制的数字。 基数(number base): 所有编号系统(numbering system)要发挥作用,都要有一个基(base)。 从你出生的时候开始,人们就教你用基数 10 来数数了。 这可能是因为我们大多数人都有 10 个手指和 10 个脚趾。另外,用基数 10 来进行数学计算也很自然。 基定义了编号系统所包含的符号数。基数 10 会有 10 个不同的符号,用以表示我们可以计量的无限事物。 基数 10 的编号系统为 0、1、2、3、4、5、6、7、8、9。一旦超过了 9,我们需要增加数的长度。 例如

Linux 虚拟内存和物理内存的理解

血红的双手。 提交于 2020-01-07 12:11:15
【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>> 首先,让我们看下虚拟内存: 第一层理解 1. 每个进程都有自己独立的4G内存空间,各个进程的内存空间具有类似的结构 2. 一个新进程建立的时候,将会建立起自己的内存空间,此进程的数据,代码等从磁盘拷贝到自己的进程空间,哪些数据在哪里,都由进程控制表中的task_struct记录,task_struct中记录中一条链表,记录中内存空间的分配情况,哪些地址有数据,哪些地址无数据,哪些可读,哪些可写,都可以通过这个链表记录 3. 每个进程已经分配的内存空间,都与对应的磁盘空间映射 Linux 使用虚拟地址空间,大大增加了进程的寻址空间, 由低地址到高地址分别为 : 只读段: 该部分空间 只能读,不可写 ;(包括: 代码段、rodata 段(C常量字符串和#define定义的常量) ) 数据段: 保存 全局变量、静态变量 的空间; 堆 : 就是平时所说的动态内存, malloc/new 大部分都来源于此。其中堆顶的位置可通过函数 brk 和 sbrk 进行动态调整。 文件映射区域 : 如 动态库、共享内存 等映射物理空间的内存,一般是 mmap 函数所分配的虚拟地址空间 。 栈: 用于维护函数调用的上下文空间,一般为 8M ,可通过 ulimit –s 查看。 内核虚拟空间: 用户代码不可见的内存区域,由内核管理

《操作系统真象还原》分页

北城余情 提交于 2020-01-06 21:46:06
  本节是阅读第五章的收获。下面将阐述一些分页的相关内容。 分页 什么是分页   分页,顾名思义,就是将内存分成大小相同的页。分页,通过映射的方式,将连续的线性地址转化为不连续的物理地址;这样,在处理器进入分页模式之后,用户直接访问的并不是物理地址,而是分页模式下的虚拟地址。   上面有三个和地址相关的概念,分别为虚拟地址、线性地址和物理地址。   在打开保护模式之前,仅有线性地址和物理地址的概念,物理地址就是CPU最终访问的真正地址,是指令或数据真正保存的数据的地方。而线性地址代表“段基址+段内偏移地址”,由于在实模式下,段基址+段内偏移地址等于物理地址,所以线性地址和物理地址数值上是一样的。   而打开保护模式且打开分页模式之后,用户直接访问的是虚拟地址空间或是线性地址空间,线性地址仍然是段基址+段内偏移地址,虚拟地址数值上与线性地址相同。从概念上线性地址空间和虚拟地址空间有些不同,因为线性地址空间只有段的概念,没有页的概念;通过分页机制,将线性地址空间中大小不等的段转化为虚拟空间中大小相等的页。虚拟地址通过页表和页目录转化为最终的物理地址,分页机制如下图:   总的来说,虚拟地址就是分页后程序或任务访问的地址,线性地址就是段基址+段内偏移地址,物理地址是CPU最终访问的地址。 为什么要分页   那么为什么要分页呢?主要原因是 内存分配的时候存在外部碎片

JVM

我们两清 提交于 2020-01-06 20:28:52
1.运行时数据区域 1.1.1.程序计数器 程序计数器( Program Counter Register)是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。 是线程私有的 是唯一一个在Java虚拟机规范中没有规定任何 OutOfMemory Error情况的区域 1.1.2.Java虚拟机栈 Java虚拟机栈( Java Virtual Machine Stacks)是线程私有的,它的生命周期与线程相同。 每个方法被执行的时候都会同时创建一个栈帧( Stack Frameφ)用于存储局部变量表、操作栈、动态链接、方法出口等信息。 每一个方法被调用直至执行完成的过程,就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。 局部变量表存放了编译期可知的各种基本数据类型、对象引用和 returnAddress类型 在Java虛拟机规范中,对这个区域规定了两种异常状况:如果线程请求的栈深度大于虛拟机所允许的深度,将抛出 Stack Overflow Error异常;如果虚拟机栈可以动态扩展,当扩展时无法申请到足够的内存时会抛出 OutOfMemoryError异常 1.1.3.本地方法栈 本地方法栈( Native Method Stacks)与虚拟机栈所发挥的作用是非常相似的,其区别不过是虚拟机栈为虚拟机执行Java方法(也就是字节码)服务

内存动态分配

限于喜欢 提交于 2020-01-06 18:42:50
在单片机中由于内存资源紧张,不可能给每个任务分配专有的内存区,尤其是涉及到通讯模块的程序,对内存的使用更是敏感。为此开发一个简单的内存管理库,对以后的开发还是有着不小的帮助的。 功能实现: 内存动态分配、内存动态释放、内存碎片回收 heap.c // 内存划分:申请的一块内存分为两部分,一部分用于存储链表结构体,另一部分给用户使用 // 内存管理:链表不存在头节点和尾节点,理论上在内存在可以存在无数个节点,通过空闲标志位来识别该内存块是否可用 #include "heap.h" #define HEAP_TRUE 1 #define HEAP_FALSE 0 #pragma pack(push, 1) typedef struct _sHeapList_t {   unsigned int size; // 内存块大小   unsigned char isfree; // 空闲标志   unsigned char *penter; // 内存入口地址   struct _sHeapList_t *plast; // 指向上一个节点,头节点为NULL   struct _sHeapList_t *pnext; // 指向下一个节点,尾节点尾NULL }sHeapList_t; #pragma pack(pop) // 申请一个内存块,作为基础内存 static unsigned

第 8 章 内存管理策略

二次信任 提交于 2020-01-06 17:49:11
  为了实现性能改进,应将多个进程保存在内存中,也就是说必须共享内存。 8.1 背景   内存是现代计算机运行的核心。内存由一个很大的字节数组来组成,每个字节都有各自的地址。 8.1.1 基础硬件   CPU可以直接访问的通用存储只有内存和处理器内置的寄存器。   每个进程都有一个独立的内存空间,可以保护进程不会互相影响。 基地址寄存器(base register):最小的合法的物理内存地址。 界限地址寄存器(limit register):指定了范围的大小。   合法范围为(base, base + limit)register   内存空间保护的实现是通过CPU硬件对在用户模式下产生的地址与寄存器的地址进行比较来完成的。   只有操作系统可以通过特殊的特权指令,才能加载基地址寄存器和界限地址寄存器。不允许用户程序修改它们。 8.1.2 地址绑定   源程序中的地址通常是用符号表示的,编译器通常将这些符号地址绑定到可重定位的地址。链接程序或加载程序再将这些可重定位的地址绑定到绝对地址。每次绑定都是从一个地址空间到另一个地址空间的映射。   通常,指令和数据绑定到存储器地址可在沿途任何一步中进行: 编译时 加载时 执行时 8.1.3 逻辑地址空间和物理地址空间   CPU生成的地址通常称为逻辑地址,而内存单元看到的地址(即加载到内存地址寄存器)通常称为物理地址。  

java 虚拟机原理

青春壹個敷衍的年華 提交于 2020-01-04 17:59:12
什么是JVM JVM是Java Virtual Machine(Java虚拟机)的缩写,是一个虚构出来的计算机,它屏蔽了与具体操作系统平台相关的信息,使得Java程序只需生成在Java虚拟机上运行的目标代码(字节码,ByteCode), 就可以在多种平台上不加修改地运行。这背后其实就是JVM把字节码翻译成具体平台上的机器指令,从而实现“一次编写,到处运行(Write Once, Run Anywhere)”。 Java为什么能够跨平台? Java引入了字节码的概念,jvm 只能认识字节码,并将它们解释到系统的API调用。针对不同的系统有不同的jvm实现,有 Linux 版本的 jvm 实现,也有 Windows 版本的 jvm 实现,但是同一段代码在编译后的字节码是一样的。在不同的系统平台上运行是通过JAVA解释器将字节码解释为不同平台的机器码,在不同的 jvm 实现上会映射到不同系统的 API 调用,从而实现代码的不加修改即可跨平台运行。 JVM、JRE、JDK的关系 JRE(Java Runtime Environment,Java运行环境),面向Java程序的使用者,而不是开发者。JRE是运行Java程序所必须环境的集合,包含JVM标准实现及 Java核心类库。它包括Java虚拟机、Java平台核心类和支持文件 JDK(Java Development Kit

30天自制操作系统第10天

▼魔方 西西 提交于 2020-01-02 16:26:17
操作系统实验日志10 第10天:叠加处理 30天自制操作系统第10天 操作系统实验日志10 一、实验主要内容 1、 内容1:内存管理 2、 内容2:叠加处理 3、 内容3:提高叠加处理速度1 4、 内容4:提高叠加处理速度2 二、遇到的问题及解决方法 三、程序设计创新点 四、实验心得体会 一、实验主要内容 1、 内容1:内存管理 为了减少内存碎片,我们要编写一些以4kb为单位进行分配和释放的函数,把指定的内存按4kb单位向上舍入。 向下舍入:以某个单位向下舍入就是将不足一单位的的数字舍去,对二进制或者十六进制来说就是将后面几位变为0。可以通过与运算来获取结果。 例如以0x1000为单位向下舍入:i=i&0xfffff000 向上舍入:与向下舍入相反,它是将不足一单位的数据补齐一单位。所以对不是一个单位的整数倍来说,一个数向下舍入就是将其先向上舍入再加上一个单位数据。 例如以0x1000为单位向上舍入: 还有一种方法: 逻辑是一样的,假如size是0x1000的整数倍,那么它加上小于0x1000的数字0xfff再向下舍入就是它本身,和它向上舍入的结果相同;假如size不是0x1000的整数倍,那么它加上0xfff后第四位肯定会进位,然后再向下取舍入到的结果肯定就是它向上舍入的结果。 PS:只有二进制数或者十六进制数可以通过与操作来向上或者向下取整

Java垃圾回收学习笔记

最后都变了- 提交于 2020-01-02 00:31:12
通常来说,要写Java代码,你基本上都没必要听说垃圾回收这个概念的。这不,对于已经写了多年Java代码的我来说,我还没有哪次经历说是需要使用垃圾回收方面的知识来解决问题的。但是,我依然督促自己花了几天时间系统性地(也比较浅显地)学习了Java垃圾回收机制。我认为学习Java垃圾回收机制至少可以得到以下几方面的好处: 对于系统调优有直接帮助 增加和同行聊天或者下一份工作面试时的谈资 在追求技术卓越上更进一步 (一)Java堆内存的分代管理 Java垃圾回收是需要消耗CPU和内存资源的,其速度随着内存的变大而减慢,这将严重影响系统的性能。同时,Java系统中存在着这么一种现象:大多数Java对象都是“短命”的。基于此,Java采用了分代的内存管理方式,并在不同的内存代中采用不同的垃圾回收算法,从而达到对内存更细粒度的管理,最大限度地减小垃圾回收对系统本身的影响。 由上图所示,Java的堆空间被分为了三个区域,分别是新生代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation)。新创建出来的对象首先存放在新生代,经过新生代中多次垃圾回收(在Survivor 0和Survivor 1之间来回复制),存活下来的对象将被转移到老年代。新生代中垃圾回收很频繁,这样多数“短命”的对象将得到及时清理;又由于新生代内存空间通常不大