精华内容
下载资源
问答
  • 所谓的内存地址,指的就是在计算机系统中或移动智能设备如智能手机中运行内存的位置,在计算机界,我们通常以十六进制的方式表示这个特殊的内存地址,并称之为内存地址编号。在汇编语言中,内存地址编号经常会被使用...

    所谓的内存地址,指的就是在计算机系统中或移动智能设备如智能手机中运行内存的位置,在计算机界,我们通常以十六进制的方式表示这个特殊的内存地址,并称之为内存地址编号。在汇编语言中,内存地址编号经常会被使用。学习啦小编就在这里给大家详细介绍内存地址。

    内存地址的作用

    既然内存被赋予了内存地址的概念,并使用编号对其进行表示,那么内存地址的作用必然是非常大的。计算机科学中,我们将在8086模式下对某一个寄存器进行向左移动大约4位的操作,内容被送到内存总线的位置,而地址与ADDR相加同时得到相关的引导,我们就能够一个关于内存单元的物理地址,程序中的这个地址也被称为逻辑地址。相反,若计算机设备处在80386的保护模式下,相关的内存内容则不会被送至内存的总线位置,而是被送到了内存管理单元,英文简称为MMU,它能够完成内存工作的地址转换,配合计算机完成内存应当完成的操作和工作。

    652-151103111Z93O.png

    三种不同的地址区分

    说到内存的地址管理知识,我们就不得不提一提关于内存地址的三种不同形式,它们分别是逻辑地址、线性地址和物理地址,当然这是在80386的模式下。

    物理地址:顾名思义,物理地址就是实际中内存的地址和位置,它是最直观的表示方式,物理地址也是一个32位的无符号整数。物理地址和逻辑地址是计算机科学中最重要的地址表示方式,也是汇编语言中经常涉及到的概念。

    逻辑地址:最底层最原始的机器语言会经常使用逻辑地址完成工作。它独特的寻址方式在目前主流的各个处理器中表现的非常详细具体,Windows程序员能够使用这种寻址方式将程序进行拆分。而逻辑地址的组成元素则是段和偏移量。

    线性地址:线性地址比较特殊,它不同于其他内存地址,而是使用无符号的整数构成的,位数为32位。线性地址最多能够表达容量达到4GB的内存空间。当然,为了减少表示的难度和字符长度,在进行对线性地址的表示的时候,我们也一般采用的是十六进制表示方式。

    内存地址的相关概念现在都已经说得差不多了,熟悉计算机编程语言的人应该都知道,汇编语言的表示方式和书写格式就是根据寄存器偏移和内存地址的表示进行书写进而实现对计算机的命令和控制的,汇编语言非常直观的体现了计算机内存地址的重要作用。

    展开全文
  • jemalloc 内存分配器 是什么

    万次阅读 2021-08-04 13:39:40
    jemalloc 内存分配器 是什么? 内存池 所谓内存池,是指应用程序向操作系统(或 JVM)申请一块内存,自己管理这一块内存,对象的创建和销毁都从这块内存中分配和回收,这么一块内存就可以称作内存池, 对应地,管理...

    jemalloc 内存分配器 是什么?

    内存池
    所谓内存池,是指应用程序向操作系统(或 JVM)申请一块内存,自己管理这一块内存,对象的创建和销毁都从这块内存中分配和回收,这么一块内存就可以称作内存池,

    对应地,管理这块内存的工具就称作内存分配器。
    同时,对于申请对象的不同又可以分为堆内存池和直接内存池,
    1 如果是向 JVM 申请的内存,那就是堆内存池,
    2 如果是向操作系统申请的内存,那就是直接内存池。

    那么,有哪些内存分配器呢?

    业界比较著名的有三个内存分配器:
    1 ptmalloc,Doug Lea 编写的分配器,支持每个线程(per-thread,简称 pt)的 arena,glibc 的默认分配器。
    Doug Lea 大神还有个分配器叫作 dlmalloc,dl 即其名之缩写。
    2 tcmalloc,Google 的分配器,它加入了线程缓存(thread cache,简称 tc),Google 声称其比 ptmalloc 快 6 倍。

    3 jemalloc,Jason Evans 的分配器,je 即其名之缩写,借鉴了很多 tcmalloc 的优秀设计,声称比 tcmalloc 更快,且 CPU 核数越多优势越大,当然,算法也更复杂。

    目前,jemalloc 已经广泛运用在 facebook、Mozilla、FreeBSD 等公司的产品上,那么,它有怎样的优势呢?

    简单总结一下,主要有三大优势:
    1 快速分配和回收
    2 内存碎片少
    3 支持性能分析

    当然了,以上说的都是原生的 jemalloc,我们今天要讲的是 Netty 中的 jemalloc,它是原生 jemalloc 在 Java 中的一种实现方式,并根据 Java 语言自身的特点做了一些删减和优化。

    在这里插入图片描述
    我们先从宏观方面对 Netty 中的内存池有个全面的了解,在 Netty 中,主要包含上面这些组件:

    PoolArena
    PoolChunkList
    PoolChunk
    PoolSubpage
    PoolThreadCache

    PoolArena
    根据内存方式的不同,PoolArena 分成 HeapArena 和 DirectArena 两个子类,在创建 PooledByteBufAllocator 的时候会分别初始化这两种类型的 PoolArena 数组,数组默认大小为核数的 2 倍,同时也会根据可以使用的内存大小动态调整。
    public class PooledByteBufAllocator extends AbstractByteBufAllocator implements ByteBufAllocatorMetricProvider {
    private final PoolArena<byte[]>[] heapArenas;
    private final PoolArena[] directArenas;
    }

    核数也可以通过 JVM 启动参数 io.netty.availableProcessors 配置,因为如果使用低版本的 JDK 且部署在 docker 容器中,获取的是主机的核数,而不是 docker 容器分配的核数。

    PoolArena 中存储着 2 种类型的数据结构,分别为 2 个 PoolSubPage [] 数组和 6 个 PoolChunkList:

    abstract class PoolArena<T> implements PoolArenaMetric {
    	private final PoolSubpage<T>[] tinySubpagePools;
        private final PoolSubpage<T>[] smallSubpagePools;
        private final PoolChunkList<T> q050;
        private final PoolChunkList<T> q025;
        private final PoolChunkList<T> q000;
        private final PoolChunkList<T> qInit;
        private final PoolChunkList<T> q075;
        private final PoolChunkList<T> q100;
    }
    

    为什么这么复杂呢?一切都是为了更好地利用内存。

    实际上,所有的数据都存储在叫作 PoolChunk 的对象中,默认每个 PoolChunk 可以存储 16MB 的数据(chunkSize),每个 PoolChunk 内部又使用伙伴算法将这 16MB 拆分成 2048 个 Page,每个 Page 的大小(pageSize)为 16MB/2048=8KB。

    如果分配的内存(规范化后的内存)小于 8KB,则把 Page 拆分成更小的内存块,并使用 PoolSubpage 管理这些更小的内存,每个 Page 的拆分标准根据这个 Page 首次被分配时的请求的大小决定:

    1 如果小于 512B,则按照 16B 规范化,比如请求的大小为 30B,则规范化到 32B,然后 PoolSubpage 中的元素大小就是 32B,那么,这个 Page 就被拆分成了 8KB/32B=256 个更小的内存块。

    2 如果大于等于 512B,则按照 512B*(2^n) 规范化,比如请求的大小为 996B,那就规范化到 1024B,也就是 1KB,然后这个 Page 就被拆分成了 8KB/1KB=8 个更小的内存块。

    如果分配的内存大于等于 8KB,且小于等于 16MB,则按照 Page 的大小,也就是 8KB,进行规范化,然后再根据伙伴算法的规则进行内存的分配,什么是伙伴算法呢?我们待会讲。
    如果分配的内存大于 16MB,则按照非池化的方式分配内存。
    所以,为了区分以上几种情况,Netty 中定义了一个 SizeClass 类型的枚举,把这几种情况分别叫作 Tiny、Small、Normal、Huge,其中 Huge 不在这个枚举中。
    在这里插入图片描述
    对于 Tiny 和 Small 类型,Netty 为了快速定位,定义了两个数组放在 PoolArena 中,分别是 tinySubpagePools 和 smallSubpagePools,它们的大小分别为 32 和 4,如果这两个数组对应的位置有值,说明之前出现过相同大小的内存块,那就快速定位到那个 PoolSubpage,使用它直接分配内存,而不用再从头查找,加快分配内存的速度。

    前面我们说了,实际上,所有的数据都位于 PoolChunk 中,为了更好地管理这些 PoolChunk,Netty 将它们以双向链表的形式存储在 PoolChunkList 中,同时 PoolChunkList 本身也以双向链表的形式呈现。

    在 PoolArena 中,定义了 6 个 PoolChunkList,分别是 qInit、q000、q025、q050、q075、q100,Netty 根据 PoolChunk 的使用率将它们放到不同类型的 PoolChunkList 中,它们代表的使用率分别为:

    qInit,内存使用率为 Integer.MIN_VALUE ~ 25%,当然不可能有负的使用率,所以最小应该是 0
    q000,内存使用率为 0 ~ 50%
    q025,内存使用率为 25% ~ 75%
    q050,内存使用率为 50% ~ 100%
    q075,内存使用率为 75% ~ 100%
    q100,内存使用率为 100% ~ Integer.MAX_VALUE,当然不可能有超过 100% 的使用率,所以最大应该是 100%

    举个例子来说明,比如一个 Chunk 首次分配了大小为 512B 的内存,那么它的内存使用率就是 512B/16MB 不足 1%,向上取整为 1%,初始时放在 qInit 中,当其分配的总内存超过了 4MB 的时候,也就是达到 25% 了,这个 PoolChunk 就被移动到 q000 中,同样地,当其分配的内存超过 8MB 的时候,就移动到了 q025 中。反过来也是一样,当有对象释放内存时,这部分内存又会被回收到 PoolChunk 中待分配,这时候内存使用会降低,当降低到 4MB 时,也就是 q025 的下限,则会将这个 PoolChunk 移动到 q000 中。

    PoolChunkList

    正如前面所说,PoolChunkList 就是相近内存使用率的 PoolChunk 的集合,这些 PoolChunk 以双链表的形式存储在 PoolChunkList 中,而 PoolChunkList 本身也以双向链表的形式连在一起,为什么要以双向链表的形式存在呢?
    在这里插入图片描述
    其实,这包含两个问题:
    1 PoolChunk 以双向链表的形式存在,是为了删除元素(移动 PoolChunk)的时候更快,比如,要删除 chunk2,只要把它的 prev 和 next 连一起就行了,时间复杂度更低;
    2 PoolChunkList 以双向链表的形式存在,是为了让 PoolChunk 在 PoolChunkList 之间移动更快,比如,一个 PoolChunk 不管是从 q025 到 q050,还是从 q050 回到 q025,都很快,时间复杂度都很低;

    另外,在 Netty 中,当分配内存时,优先从 q050 中寻找合适的 PoolChunk 来分配内存,为什么先从 q050 开始呢?

    private void allocateNormal(PooledByteBuf<T> buf, int reqCapacity, int normCapacity) {
        if (q050.allocate(buf, reqCapacity, normCapacity) 
            || q025.allocate(buf, reqCapacity, normCapacity) 
            || q000.allocate(buf, reqCapacity, normCapacity) 
            || qInit.allocate(buf, reqCapacity, normCapacity) 
            || q075.allocate(buf, reqCapacity, normCapacity)) {
            return;
        }
        // 省略其它代码
    }
    

    因为 q050 中的 PoolChunk 的内存使用率都比 50% 多一点,这样更容易找到符合条件的 PoolChunk,又不至于使 PoolChunk 的利用率偏低。

    因为 q050 中的 PoolChunk 的内存使用率都比 50% 多一点,这样更容易找到符合条件的 PoolChunk,又不至于使 PoolChunk 的利用率偏低。

    我们举个例子,假如从 q075 中先寻找,如果要分配 4M 以上的内存就无法找到合适的 PoolChunk;假如从 q025 中先寻找,可能正好有内存使用率在 25% 以上的 PoolChunk,这时候就直接使用了,那么 q050 中的 PoolChunk 就很难被利用起来,也就是 q050 中的 PoolChunk 的剩余空间很难被利用起来,进而导致整体的利用率偏低,也就是内存碎片会变高。

    那么,如果先从 q050 寻找合适的 PoolChunk 呢?这时 q025 和 q075 中的 PoolChunk 可能永远都不会被使用到,不过没关系,对于 q025 中的 PoolChunk 的内存使用率变为 0 的时候,它们自然就被释放了,而 q075 中的 PoolChunk 本身内存使用率就已经很高了,不用到它们反而更好,等它们的内存使用率降低的时候就又回到 q050 中了,此时就又来很容易地被利用起来。

    因此,从 q050 开始寻找,能很大程度上增大整体的内存使用率,降低内存碎片的存在。

    PoolChunk
    前面我们说了,默认地,一个 PoolChunk 可以存储 16MB 的数据,PoolChunk 是真正存储数据的地方,何以见得?

    final class PoolChunk<T> implements PoolChunkMetric {
        // 数据存储的地方
        final T memory;
        // 满二叉树对应节点是否被分配,数组大小为4096
        private final byte[] memoryMap;
        // 满二叉树原始节点高度,数组大小为4096
        private final byte[] depthMap;
        // 管理更小的内存,数组大小为2048
        private final PoolSubpage<T>[] subpages;
        // 剩余的内存
        private int freeBytes;
        PoolChunk<T> prev;
        PoolChunk<T> next;
    }
    

    PoolChunk 本身是一个泛型类型,内部保存了一个叫作 memory 的变量,这个 memory 会根据分配的是堆内存还是直接内存而变换类型:
    对于堆内存,memory 的类型为 byte []
    对于直接内存,memory 的类型为 ByteBuffer,实际上为 DirectByteBuffer。

    所有的数据都存储在 memory 中,至于更小粒度的划分,比如 PoolSubpage,它们使用各种偏移量对 memory 进行分段处理,数据本身并不会复制到这些细粒度的类中。

    Netty 中,并没有 PoolPage 或者 Page 这个类,Page 是一种抽象的说法,
    它表示的是 PoolChunk 中每 8KB 的数据块,它同样使用 PoolSubpage 来表示。
    

    默认地,Netty 使用伙伴算法将 PoolChunk 分成 2048 个 Page,这些 Page 又向上形成一颗满二叉树:
    在这里插入图片描述
    结合上图,我们先来简单介绍一下 PoolChunk 中的几个变量:

    depthMap,保存着满二叉树原始的高度信息,比如 depthMap [1024]=10

    memoryMap,初始值等于 depthMap,随着节点的被分配,它的值会不断变化,更新子节点的值时,会同时更新其父节点的值,其父节点的值等于两个子节点值中的最小者。

    subpages,对应于上图中的 Page0、Page1、…、Page2047,在 Netty 中并没有 Page 的具体代码实现,它同样使用 PoolSubpage 来表示。只有分配的内存小于 8KB,才会使用 PoolSubpage 进行管理,在 PoolSubpage 创建之后,会加入到 PoolArena 中 tinySubpagePools [] 或 smallSubpagePools [] 对应位置的链表中,同时,在 PoolSubpage 代表的内存被分配完之后,会从对应的链表中删除,也就是说,在同一时刻,head 最多只会与一个 PoolSubpage 形成双向链表。

    freeBytes,PoolChunk 中剩余的内存,即可被使用的内存。

    如果分配的内存大于等于 8KB,由 PoolChunk 自己管理。

    为了更好地理解伙伴分配算法,我们来假想一种分配内存的情况,如果分配内存的顺序分别为 8KB、16KB、8KB,则会按以下顺序进行:

    8KB,符合一个 Page 大小,所以从第 11 层(12-8KB/8KB)寻找节点,这里找到了 2048 这个节点,发现其 memoryMap [2048]=11=depthMap [2048],可以被分配,然后到其对应的 Page [0] 中分配内存,分配之后将其 memoryMap [2048]=12,memoryMap [1024]=11=(2048 和 2049 中的最小者 11),memoryMap [512]=10=(1024 和 1025 中的最小者 10),…,memoryMap [1]=1;

    16KB,符合两个 Page 大小,所以从第 10 层寻找节点(12-16KB/8KB),找到 1024 节点,发现其 memoryMap [1024]=11!=depthMap [1024],不符合条件,继续寻找到 1025 节点,发现其 memoryMap [1025]=10=depthMap [1025],符合条件,所以,到其对应的叶子节点 2050/2051 对应的 Page [2]/Page [3] 中分配内存,分配之后 memoryMap [2050]=12,memoryMap [2051]=12,memoryMap [1025]=12=(2050 和 2051 中的最小值 12),memoryMap [512]=11=(1024 和 1025 中的最小者 11),…,memoryMap [1]=1;

    8KB,符合一个 Page 大小,所以从第 11 层(12-8KB/8KB)寻找节点,2048 已经不符合条件了,所以找到了 2049 这个节点,到其对应的 Page [1] 中分配内存,然后更新 memoryMap [2049]=12,memoryMap [1024]=12=(2048 和 2049 中的最小者 12),memoryMap [512]=12=(1024 和 1025 中的最小者 12),…,memoryMap [1]=1;

    至此,三次内存都分配完毕,总共分配了 Page0~Page3 共 4 个 Page,从分配结果也可以看出,使用伙伴分配算法,能极大地保证分配连续的内存空间,并减少内存碎片的诞生。

    PoolSubpage
    前面我们说过,只有当分配的内存小于一个 Page 大小,即 8KB 时,才会使用 PoolSubpage 来进行管理,那么它是怎么管理的呢?

    让我们先来看看它的几个关键字段:

    final class PoolSubpage<T> implements PoolSubpageMetric {
        // 对应满二叉树中的哪个节点
        private final int memoryMapIdx;
        // 在PoolChunk的memory中的偏移量
        private final int runOffset;
        // 表示每个小块的状态
        private final long[] bitmap;
        // 每个小块(元素)的大小
        int elemSize;
        // 最大的元素个数=8KB/elemSize
        private int maxNumElems;
        // 需要使用到几个long
        private int bitmapLength;
        // 可用的元素个数
        private int numAvail;
        // 双向链表的指针
        // 与PoolArena中的tinySubpagePoos或smallSubpagePools中的元素形成双向链表
        PoolSubpage<T> prev;
        PoolSubpage<T> next;
    }
    

    elemSize 表示每个元素的大小,这个大小是根据这个 Page 接收到的第一个请求的大小决定的。
    比如,首次分配 30B 的内存,则会经历以下几个步骤:

    1判断小于 512B,按 16B 向上规范化到 32B;
    2 在满二叉树的第 11 层寻找一个可用的节点,假如是 2049,即 memoryMapIdx=2049,它代表的是 Page1,Page1 这个节点在 PoolChunk 中对应到 memory 上的偏移量就是 8192(前面有个 Page0),所以,runOffset=8192;
    3 此时,会把 Page0 按 32B 分成(8KB/32B=256)个小块,所以,elemSize=32B,maxNumElems=256,numAvail=256;
    4 同时,这 256 个小块就需要 256 个 bit(位)来表示其每个小块的状态,也就是需要(256/64=4)个 long 类型来表示,所以,bitmapLength=4;
    5 然后,把这个 PoolSubpage 与 PoolArena 的 tinySubpagePools [1](相当于 head)形成双向链表,因为 tinySubpagePools [0] 代表的是 16B 的内存,tinySubpagePools [1] 代表的是 32B 的内存;
    6 当分配完这 32B 之后,可用节点数减一,所以,numAvail=255;

    当再次分配规范为 32B 内存的时候,就看 PoolArena 的 tinySubpagePools [1] 的 next 中有没有值,有值,就直接使用其分配内存了,而不用再重新走一遍上面的过程,从而加快分配内存的速度。

    PoolThreadCache
    前面讲了这么多,分配内存的速度已经足够快了,但是,还可以更快,那就是加入线程缓存 PoolThreadCache,那么,PoolThreadCache 在何时使用呢?

    其实,这要结合回收内存一起使用,当回收内存时,先不还给 PoolChunk,而是使用本地线程缓存起来,当下一次再分配同样大小(规范化后的大小)的内存时,先尝试从本地线程缓存里面取,如果取到了就可以直接使用了。

    那么,PoolThreadCache 可以缓存哪些类型的缓存呢?

    在 Netty 中,除了 Huge,其它类型的内存都可以缓存,即 Tiny、Small、Normal,当然,根据堆内存和直接内存的不同,PoolThreadCache 中又分成了两大类:

    final class PoolThreadCache {
        // 堆内存的缓存
        private final MemoryRegionCache<byte[]>[] tinySubPageHeapCaches;
        private final MemoryRegionCache<byte[]>[] smallSubPageHeapCaches;
        private final MemoryRegionCache<byte[]>[] normalHeapCaches;
        // 直接内存的缓存
        private final MemoryRegionCache<ByteBuffer>[] tinySubPageDirectCaches;
        private final MemoryRegionCache<ByteBuffer>[] smallSubPageDirectCaches;
        private final MemoryRegionCache<ByteBuffer>[] normalDirectCaches;
    }
    

    PoolThreadCache 中使用了一个叫作 MemoryRegionCache 的类来做缓存,它内部维护了一个队列,当回收内存时,这块内存进入到这个队列中,当下次再分配同样大小(规范化后的大小)的内存时,从这个队列中取,关于 PoolThreadCache 的使用,我们下一节结合代码一起学习。

    展开全文
  • 内存什么

    千次阅读 2020-02-05 19:05:25
    文章目录内存的物理机制指针数组的定义: 内存的物理机制 内存实际上是一种名为内存 IC 的电子元件。虽然内存 IC 包 括 DRAM、SRAM、ROMA等多种形式,但从外部来看,其基本机制都是一样的 补充: ROM(Read Only ...

    内存的物理机制

    内存实际上是一种名为内存 IC 的电子元件。虽然内存 IC 包 括 DRAM、SRAM、ROMA等多种形式,但从外部来看,其基本机制都是一样的

    补充: ROM(Read Only Memory)是一种只能用来读取的内存。
    RAM(Random Access Memory)是可被读取和写入的内存,分为需要经常
    刷新(refresh)以保存数据的 DRAM(Dynamic RAM),以及不需要刷新电
    路即能保存数据的 SRAM(Static RAM)。

    图:
    在这里插入图片描述
    1、内存中1个地址只能够存储1个字节
    2、数据信号引脚有 D0~D7共八个,表示一次可以输入输出 8 位(= 1 字节)的数据

    存储容量
    而地址用来表示数据的存储场所,因此我们可以得出这个内存 IC 中可以存储 1024 个 1 字节的数据

    内存写入数据过程
    我们假设要往该内存 IC 中写入 1 字节的数据。为了实现该目的,可以给 VCC接入+5V,给 GND 接入 0V 的电源,并使用 A0~A9 的地址信号来指定数据的存储场所,然后再把数据的值输入给 D0~D7 的数据信号,并把 WR(write = 写入的简写)信号设定成 1。执行完这些操作,就可以在内存 IC 内部写入数据
    在这里插入图片描述

    内存读取数据过程
    读出数据时,只需通过 A0~A9 的地址信号指定数据的存储场所,然后再将 RD(read = 读出的简写)信号设成 1 即可。执行完这些操作,指定地址中存储的数据就会被输出到 D0~D7 的数据信号引脚

    在这里插入图片描述

    指针

    指针是什么?
    指针也是一种变量,它所表示的不是数据的值,而是存储着数据的内存的地址

    地址与指针的比较
    一个地址标记的是一个字节的位置
    指针:可以对应多个地址,比如 int *a,这个指针指向的是一个四字节的地址空间,当然这个指针的值(即地址)表示的是这个空间的首个字节的地址

    图:
    在这里插入图片描述

    补充:当程序运行时,在实际分配地址(在程序运行时,Windows 等操作系统会自动决定变量的物理地址)

    Java 中不同的数据类型在内存中的具体表现是什么?

    基本数据类型字节数
    byte1byte = 8bit
    short2byte
    int4byte
    long8byte
    float4byte
    double8byte
    boolean1byte
    char2byte

    数组的定义

    数组与内存之间的模型关系
    之所以说数组是内存的使用方法的基础,是因为数组和内存的物理构造是一样的,虽然是通过指定索引来使用数组,但这和内存的物理读写并没有特别大的区别
    图:
    在这里插入图片描述
    以数组为基础的相应数据模型
    栈、队列、链表、串、树、图 等等
    – 请各位看官移步楼主的 数据结构系列。
    – https://blog.csdn.net/weixin_39966065/article/details/103991009

    展开全文
  • 什么计算机内存数值存储方式是补码?1. 原码2. 反码3. 补码4. 补码的意义 首先我们得先把源码、反码、补码的概念搞清楚,然后对比三码的区别,再总结为什么计算机内存数值存储方式是补码。 1. 原码 一个数的原码...

    为什么计算机内存数值存储方式是补码?


    首先我们得先把源码、反码、补码的概念搞清楚,然后对比三码的区别,再总结为什么计算机内存数值存储方式是补码。

    1. 原码

    一个数的原码(原始的二进制码)有如下特点:

    • 最高位做为符号位,0表示正,为1表示负
    • 其它数值部分就是数值本身绝对值的二进制数
    • 负数的原码是在其绝对值的基础上,最高位变为1

    下面数值以1字节的大小描述:

    十进制数原码
    +150000 1111
    -151000 1111
    +00000 0000
    -01000 0000

    原码表示法简单易懂,与带符号数本身转换方便,只要符号还原即可,但当两个正数相减或不同符号数相加时,必须比较两个数哪个绝对值大,才能决定谁减谁,才能确定结果是正还是负,所以原码不便于加减运算。

    2. 反码

    • 对于正数,反码与原码相同
    • 对于负数,符号位不变,其它部分取反(1变0,0变1)
    十进制数反码
    +150000 1111
    -151111 0000
    +00000 0000
    -01111 1111

    反码运算也不方便,通常用来作为求补码的中间过渡。

    3. 补码

    补码特点:

    • 对于正数,原码、反码、补码相同
    • 对于负数,其补码为它的反码加1
    • 补码符号位不动,其他位求反,最后整个数加1,得到原码
    十进制数补码
    +150000 1111
    -151111 0001
    +00000 0000
    -00000 0000

    4. 补码的意义

    示例1:用8位二进制数分别表示+0和-0

    十进制数原码
    +00000 0000
    -01000 0000
    十进制数反码
    +00000 0000
    -01111 1111

    不管以原码方式存储,还是以反码方式存储,0也有两种表示形式。为什么同样一个0有两种不同的表示方法呢?
    但是如果以补码方式存储,补码统一了零的编码:

    十进制数补码
    +00000 0000
    -010000 0000 由于只用8位描述,最高位1丢弃,变为0000 0000

    示例2:计算9-6的结果
    以原码方式相加:

    十进制数原码
    90000 1001
    -61000 0110

    在这里插入图片描述
    结果为-15,不正确。
    以补码方式相加:

    十进制数补码
    90000 1001
    -61111 1010

    在这里插入图片描述
    最高位的1溢出,剩余8位二进制表示的是3,正确。

    在计算机系统中,数值一律用补码来存储,主要原因是

    • 统一了零的编码
    • 将符号位和其它位统一处理
    • 将减法运算转变为加法运算
    • 两个用补码表示的数相加时,如果最高位(符号位)有进位,则进位被舍弃
    展开全文
  • 我们来了解一下内存的物理构造,一般内存的外形图片如图1。一个内存是由若干个黑色的内存颗粒构成的。每一个内存颗粒叫做一个chip。 图1.内存外形图 上面这个内存条有8个chip。每一个chip内部,是由8个bank组成...
  • null:这个关键词大家都不陌生,但是大家一定不太明白它是什么类型的,或者它在内存中有什么作用,又或者它是不是一个空指针等等,我在这总结了下null 的“意义”。 当我们为一个引用变量初始化=null例如: ...
  •  本文中使用的apache是 Event 模式,在访问量上来之后,linux服务器总共60G的内存占用持续升高,于是将内存容量扩大,升高至120G。经过一段时间后发现,内存占用继续升高,几乎将120G占满,网站访问速度急速下降...
  • 内存分配方式内存分配算法

    千次阅读 2018-03-14 20:24:56
    内存分配方式有两种,连续内存分配方式和离散内存分配方式。不同的分配方式又有不同的分配算法。 内存分配算法,其实就是:有一大块空闲的资源,如何合理地分配资源?内存分配的思想可以用到很多其他的领域。比如...
  • 什么是对齐,以及为什么要对齐:  现代计算机中内存空间都是按照byte划分的,从理论上讲似乎对任何类型的变量的访问可以从任何地址开始,但实际情况是在访问特定变量的时候经常在特定的内存地址访问,这就需要各...
  • Java - null在内存中到底是什么

    千次阅读 2018-09-26 16:44:11
    回想什么是变量,什么是值。一个常见的隐喻是一个变量类似于一个盒子。就像你可以用一个盒子来存储东西一样,你可以使用变量来存储一个值。在声明变量时,我们需要设置变量的类型。 Java中有两大类类型:原始类型和...
  • 大端模式,是指数据的高位,保存在内存的低地址中,而数据的低位,保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放; 例子: ...
  • 内存泄漏:由于疏忽或者错误造成程序未能释放已经不再使用的情况,...检测内存泄漏的方法: 1.使用调试器和C运行库(CRT)调试堆函数。 具体函数:_CrtSetDbgFlag()可以在作用于结束位置,自动调用_CrtDumpMe...
  • 什么是内存泄露?怎么检测

    千次阅读 2019-03-20 17:32:03
    什么是内存泄露? 简单地说就是申请了一块内存空间,使用完毕后没有释放掉。它的一般表现方式是程序运行时间越长,占用内存越多,最终用尽全部内存,整个系统崩溃。由程序申请的一块内存,且没有任何一个指针指向它...
  • C++检测内存泄漏方法

    千次阅读 2018-11-23 18:25:29
    1.检测内存泄漏方法 这里先介绍一下windows下VS运行C/C++工程检测内存泄漏的方法。 Visual Studio 调试器和 C 运行时 (CRT) 库为我们提供了检测和识别内存泄漏的有效方法。VC++ IDE 的默认状态是没有启用内存泄漏...
  • 0x12345678在内存中是什么样子的

    千次阅读 2015-09-23 11:25:39
    内存的存储的最小单元是一个字节,对于多字节存储的方式一般采用低位优先, #include "iostream" using namespace std; union { int a ; long b; unsigned char c; } m; int main(){ cout
  • xcode检测内存泄露方法

    千次阅读 2014-10-29 16:20:36
    xcode检测内存泄露方法
  • C语言对于-0和+0在内存中的表示方法

    千次阅读 2013-07-31 09:27:58
    如题目所示,负数在计算机内存中的表示方法为相反数取反码,再对反码加1,那么对于-0来说就是 0 取反码为 0xFFFFFFFF 在对反码加1,得到 0x00000000,32位最高位舍弃,所以-0和+0在内存中的表示均为0x00000000,(仅...
  • Redis 内存优化方式

    千次阅读 2018-09-28 09:46:19
    内存优化方式与参数 关闭 Redis 的虚拟内存[VM]功能,即 redis.conf 中 vm-enabled = no 设置 redis.conf 中 maxmemory ,用于告知 Redis 当使用了多少物理内存后拒绝继续写入的请求,可防止 Redis 性能降低甚至...
  • uboot 中内存测试,内存检测方法

    千次阅读 2016-05-03 19:16:00
    在 U-Boot中,Denx(U-Boot的开发商)针对常见的DDR内存故障进行了严格的检测处理,下图描述了该检测处理过程的三个步骤:检测数据线、地址线和DDR物理存储部件,主要涉及这三个步骤的处理过程和方法,对于DDR子系统...
  • CDH交换内存警告解决方法

    万次阅读 2018-08-21 18:19:17
    交换内存定义:Linux中Swap(即:交换分区),类似于Windows的虚拟内存,就是当...1、设置swappiness值为0,表示尽可能不使用交换内存 (1)临时设置方案,重启后设置不生效 [root@cdh-001 ~]# sysctl vm.swappine...
  • float和double类型的内存分布和比较方法收藏 Comparing floating point numbers  总结几点: 0. float占4byte,精度是6~7位;double占8byte,精度是15~16位。 1. C/C++的浮点数据类型有float和double两种。...
  • char型常量(字符),在计算机中是按其ASCII值进行存储,ASCII是"整型类"数据,在内存中全部以补码形式进行存放。补码是一种二进制数据表示形式。整数分为正数、负数和零,计算机设计初期,规定,以字节的最高位表示...
  • 今天学习在C语言中浮点数在内存中的表示方法 文章目录1 浮点数在内存中的存储方式1.1 浮点数的转换步骤1.2 浮点数的转换实际例子分析1.3 编程验证测试2 int与float类型的范围的比较2.1 float数不精确的编码案例3 ...
  • 对于软件开发者而言,理解和熟悉计算机内存知识是最为基础的了。今天我就来翻翻旧账,回顾回顾看看我有哪些点遗漏了,在此共同学习。 提起内存,我们常常想到三个区域: 1,静态区,静态变量 static variables / ...
  • 负零和正零在内存中的表示方法

    千次阅读 2014-12-20 22:58:14
    我们都知道,不管是负数和正数,在计算机内存中都是以补码来表示的,下面先介绍原码、反码和补码的概念和联系: 所谓原码就是前面所介绍的二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负...
  • 如何对JVM进行内存调优?调优需要遵从什么样的原则或者说方法? 下面我们来说叨说叨,希望能帮到大家,同时自己也学习、记录。
  • Android 查看进程内存方式

    千次阅读 2018-02-06 22:28:27
    查看Android系统,单个进程、多个进程内存的使用情况,通常有那些方法: 1,通过Android API函数 使用ActivityManager查看可用内存,具体是其内部类MemoryInfo。 ActivityManager.MemoryInfo memInfo = new ...
  • android app内存查看方法

    千次阅读 2013-11-26 10:02:07
    查看内存使用的方式有很多种,但是各个方式查看到的结果可能会有微略不同。 方式一,Running services 通过手机上Running services的Activity查看,可以通过Setting->Applications->Running services进。 关于...
  • C++共享内存交互方式

    千次阅读 2019-07-04 11:03:48
    一、什么是共享内存 文件映射是一种实现进程间单向或双向通信的机制。它允许两个或多个本地进程间相互通信。为了共享文件或内存,所有的进程必须使用相同的文件映射的名字或是句柄。 说白了就是把内存当成磁盘,在...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,448,682
精华内容 579,472
关键字:

内存表示方式是什么