ptmalloc:从内存虚拟化说起

news2024/11/28 8:51:09

前言

本文并不局限于ptmalloc的原理,而是从linux的内存虚拟化和系统调用原理出发,结合各种语言实现,讲明内存分配方面的trade off,力图事无巨细,追根究底。本文内容包括但不限于:NIO原理、0拷贝原理、内存虚拟化、GC和内存分配、PCB结构、mmap原理和场景、JVM内存分配细节、缺页异常中断、PTE、物理页分配、驻留内存、malloc分配原理、ptmalloc的设计和缺陷、mimalloc设计。

什么是glibc和ptmalloc

glibc提供了一组在所有Linux发行版上都可用的标准化函数。包括ISO C standard library、POSIX实现、内存管理等。其中的内存分配函数ptmalloc2被包括C++、JVM(Native Heap)、Python在内广泛使用。
ptmalloc2遵循malloc函数的惯例:小内存使用brk分配,大内存使用mmap分配。
同时,ptmalloc2也因为它的内存碎片、内存泄漏和线程间锁争用问题而广受诟病,因此谷歌推出了tcmalloc,facebook推出jemalloc,微软推出mimalloc来取代它。一般在生产环境也建议根据不同的情况选用不同的内存分配库替换掉glibc的实现。

思考题1:ptmalloc2的问题会对哪些语言造成影响?为什么?

答案:
对C++、JVM(Native Heap)、Python都有影响。但是对JVM影响较小。
要理解这个问题首先要理解JVM的内存分配。
首先我们看JVM的内存划分:
image.png
主流GC的中,GC要么是Partial GC要么是Full GC:

  • Partial GC:并不收集整个GC堆的模式
    • Young GC:只收集young gen的GC
    • Old GC:只收集old gen的GC。只有CMS的concurrent collection是这个模式
    • Mixed GC:收集整个young gen以及部分old gen的GC。只有G1有这个模式
  • Full GC:收集整个堆,包括young gen、old gen、perm gen(如果存在的话)等所有部分的模式。

所以内存回收的粒度是比较大的,对于mimalloc并非像C++和Python那么敏感。这个影响跟G1的-XX:MaxGCPAuseMillS参数也有关系。如果这个jvm参数设置的太低的话,就会导致每次G1 回收的内存很少,受到的底层回收算法影响更大。
更多的关于GC的细节,可以参考下面的简单聊聊GC场景下的内存释放。
那么什么时候知道glibc对JVM产生了影响呢?通过RSS(进程驻留内存)来看:
image.png

内存增长模型

虚拟地址空间由地址总线宽度决定:

64位linux进程虚拟地址空间的内存增长模型如下所示:
image.png
思考题2: NIO bytebuffer分配的内存属于堆内内存还是堆外内存?属于user space还是kernal space?

答案:既可能是堆内内存也可能是堆外内存,既可能属于user space也可能属于kernal space。
image.png

虚拟地址空间、brk和mmap

分配小于128k内存时虚拟地址空间的情况
image.png
分配内存大于128k时的情况:
image.png
brk分配的内存需要等到高地址内存释放以后才能释放(例如,在B释放之前,A是不可能释放的,这就是内存碎片产生的原因),而mmap分配的内存可以单独释放。
mmap函数的作用就是分配/映射一段虚拟地址空间:
image.png

这两种方式分配的都是虚拟内存,没有分配物理内存。在第一次访问已分配的虚拟地址空间的时候,发生缺页中断,操作系统负责分配物理内存,然后建立虚拟内存和物理内存之间的映射关系(写时复制)。
mmap能解决那些性能问题?

  1. 用户态到内核态的过程中,内存拷贝问题
  2. 内核态把脏数据写回到块设备的过程中,内存拷贝的问题
  3. 4K对齐问题
  4. 零拷贝(实际上是通过映射)问题


通过下面的流程图可以很直观的明白mmap的作用:
image.png
image.png

思考题:

  1. swap space大小影响虚拟空间地址大小吗?
  2. malloc调用的mmap会设置flags为MAP_ANONYMOUS吗?也就是说malloc会映射到文件吗?
  3. mmap什么时候回写到文件?
  4. mmap在文件映射时导致的脏页在回写之后怎么再次标记为脏页触发回写?为什么跟write不一样?
  5. mmap在文件映射时物理内存+swap spcae不够怎么办?
  6. mmap怎么保证文件页缓存一致性?

答案:

  1. 不会
  2. 不会
  3. image.png

dirty pages的物理页可以通过address_space 中的Radix树快速找到并且被pdflush回写。
image.png
struct mm_struct
struct mm_struct 包含所有与进程相关的内存区域。 The mm field of struct task_struct is a pointer to the struct mm_struct of the current process.
struct vm_area_struct
A struct vm_area_struct is created at each mmap() call issued from user space. A driver that supports the mmap() operation must complete and initialize the associated struct vm_area_struct. The most important fields of this structure are:

  • vm_start, vm_end - the beginning and the end of the memory area, respectively (these fields also appear in /proc//maps);
  • vm_file - the pointer to the associated file structure (if any);
  • vm_pgoff - the offset of the area within the file;
  • vm_flags - a set of flags;
  • vm_ops - a set of working functions for this area
  • vm_next, vm_prev - the areas of the same process are chained by a list structure

TCB结构图
image.png

  1. 回写的页会变成写保护,写会再次触发缺页

PTE中R/W位标志是否写保护
image.png
为什么跟write不一样?我们先看看write的过程:
image.png
根本原因当然是再次写不能切换到内核态无法修改PTE,只能通过写保护再次触发写时复制缺页异常,标记为脏页。

  1. mmap触发的缺页异常并不会一次将所有文件内容读到内存中。

linux将缺页异常分为几种情况分别分配内存,包括PTE是否为空,是匿名映射还是文件映射、是读文件还是写文件、页面是否换出、是否满足COW等等情况。
如果是文件映射导致的缺页异常,最终的调用是这样的:

	//如果map_pages函数不为空并且fault_around_bytes有效,
	//map_pages就是之前讲过的预读的操作函数,fault_around_bytes控制预读长度,一般64k
	if (vma->vm_ops->map_pages && fault_around_bytes >> PAGE_SHIFT > 1) {
		//调用do_fault_around预读几个页的文件内容读取到vmf->page,为了减少页错误异常的次数
		ret = do_fault_around(vmf);
		if (ret)
			return ret;
	}

可以通过MADV_SEQUENTIAL来更激进地申请mmap内存,也可以通过MAP_POPULATE直接将文件全部加载到内存中,这也意味着内存会被更快的释放。

  1. 内核态申请的PTE是共享的 PTE中的G位表示是否共享

ptmalloc

image.png
memory
应用地址空间,由RAM或swap提供
chunk
可以在应用中分配、在glibc中释放或与相邻chunk组合成较大范围的小范围内存。请注意,chunk是给定给应用的memory的包装器。每个chunk存在于一个heap中,属于一个arena。
heap
memory中的一个连续区域,它被细分为要分配的chunk。每个heap恰好属于一个arena。
arena
一种在一个或多个线程之间共享的结构,其中包含对一个或更多heap的引用,以及这些heap中“空闲”的chunk的链表。分配给每个arena的线程将从该arena的空闲列表(bins)中分配内存。

chunk

image.png
使用中的chunk
1、heap中有chunk指针和mem指针 chunk指针指向chunk开始的地址;mem指针指向用户内存块开始的地址。
2、 p=0时,表示前一个chunk为空闲,prev_size才有效
3、p=1时,表示前一个chunk正在使用,prev_size无效 p主要用于内存块的合并操作;ptmalloc 分配的第一个块总是将p设为1, 以防止程序引用到不存在的区域
4、M=1 为mmap映射区域分配;M=0为heap区域分配
5、 A=0 为主分配区分配;A=1 为非主分配区分配。
空闲的chunk
1、当chunk空闲时,其M状态是不存在的,只有AP状态,
2、原本是用户数据区的地方存储了四个指针,
指针fd指向后一个空闲的chunk,而bk指向前一个空闲的chunk,malloc通过这两个指针将大小相近的chunk连成一个双向链表。
在large bin中的空闲chunk,还有两个指针,fd_nextsize和bk_nextsize,用于加快在large bin中查找最近匹配的空闲chunk。不同的chunk链表又是通过bins或者fastbins来组织的。

arenas和heaps

image.png
为了有效地处理多线程应用程序,glibc的malloc允许一次活动多个内存区域。因此,不同的线程可以访问存储器的不同区域,而不会相互干扰。这些记忆区域统称为“arena”。有一个主mrena,即“main arena”,对应于应用程序的初始heap。malloc代码中有一个静态变量指向这个arena,每个arena都有一个下一个指针来链接其他arena。
随着线程碰撞的压力增加,glibc通过mmap创建了额外的arena来缓解压力。arena的数量上限为系统中CPU数量的八倍,这意味着重线程应用程序仍会出现一些争用,但代价是碎片会减少。
每个arena中都有一个mutex,用于控制对该arena的访问。一些操作,例如访问fastbins,可以使用原子操作来完成,并且不需要锁定arena。所有其他操作都要求线程锁定arena。对这个mutex的争用是创建多个arena的原因——分配给不同arena的线程不需要相互等待。如果有争用,线程将自动切换到未使用(未锁定)的arenas。
每个arenas都从一个或多个堆中获得内存。main arenas使用程序的初始堆(从.bss之后开始),可以使用mmap和brk分配内存。其它的arenas只能通过mmap为它们的堆分配内存,每个竞技场都会跟踪一个特殊的“顶部”chunk,这通常是最大的可用chunk,同时指向最近分配的heap。
总结:
1. 主分配区和非主分配区形成一个环形链表进行管理。
2. 每一个分配区利用互斥锁使线程对于该分配区的访问互斥。
3. 每个进程只有一个主分配区,也可以允许有多个非主分配区。
4. ptmalloc根据系统对分配区的争用动态增加分配区的大小
5. 主分配区可以使用brk和mmap来分配,而非主分配区只能使用mmap来映射内存块
6. 申请小内存时会产生很多内存碎片,ptmalloc在整理时也需要对分配区做加锁操作

bins

为了避免频繁的系统调用,应用free的内存块,ptmalloc会根据size和历史存储在不同的bins中。
image.png

fast bins

fast bins是bins的高速缓冲区,大约有10个定长队列。每个fast bin都记录着一条free chunk的单链表(称为binlist ,采用单链表是出于fast bin中链表中部的chunk不会被摘除的特点),增删chunk都发生在链表的前端。
fastbin中的chunks可以根据需要移动到其他bins中。fast bins 记录着大小以8字节递增的bin链表。当用户释放一块不大于max_fast(默认值64B)的chunk的时候,会默认会被放到fast bins上。当需要给用户分配的 chunk 小于或等于 max_fast 时,malloc 首先会到fast bins上寻找是否有合适的chunk,除非特定情况,两个毗连的空闲chunk并不会被合并成一个空闲chunk。不合并可能会导致碎片化问题,但是却可以大大加速释放的过程。

image.png

unsorted bins

unsorted bin 的队列使用 bins 数组的第一个,是bins的一个缓冲区,加快分配的速度。当用户释放的内存大于max_fast或者fast bins合并后的chunk都会首先进入unsorted bin上。unsorted bins无尺寸限制,任何大小chunk都可以添加进这里。unsorted bins的设计主要是为了一个最近释放的复用。
用户malloc时,如果在 fast bins 中没有找到合适的 chunk,则malloc 会先在 unsorted bin 中查找合适的空闲 chunk,如果没有合适的bin,ptmalloc会将unsorted bin上的chunk放入bins上,然后到bins上查找合适的空闲chunk。

small bins

大小小于512字节的chunk被称为small chunk,而保存small chunks的bin被称为small bin。数组从2开始编号,前64个bin为small bins,small bin每个bin之间相差8个字节,同一个small bin中的chunk具有相同大小。每个small bin都包括一个空闲区块的双向循环链表(也称binlist)。
free掉的chunk添加在链表的前端,而所需chunk则从链表后端摘除。两个毗连的空闲chunk会被合并成一个空闲chunk。合并消除了碎片化的影响但是减慢了free的速度。分配时,当samll bin非空后,相应的bin会摘除binlist中最后一个chunk并返回给用户。在free一个chunk的时候,检查其前或其后的chunk是否空闲,若是则合并,也即把它们从所属的链表中摘除并合并成一个新的chunk,新chunk会添加在unsorted bin链表的前端。

large bins

大小大于等于512字节的chunk被称为large chunk,而保存large chunks的bin被称为large bin,位于small bins后面。large bins中的每一个bin分别包含了一个给定范围内的chunk,其中的chunk按大小递减排序,大小相同则按照最近使用时间排列。
两个毗连的空闲chunk会被合并成一个空闲chunk。
分配时,遵循原则“smallest-first , best-fit”,从顶部遍历到底部以找到一个大小最接近用户需求的chunk。一旦找到,相应chunk就会分成两块User chunk(用户请求大小)返回给用户。Remainder chunk(剩余大小添加到unsorted bin。free时和small bin 类似。

三种特殊chunks

有三种特殊chunks不会存储到bins中:

  1. Top chunk
    top chunk相当于分配区的顶部空闲内存,当bins上都不能满足内存分配要求的时候,就会来top chunk上分配。
    当top chunk大小比用户所请求大小还大的时候,top chunk会分为两个部分:User chunk(用户请求大小)和Remainder chunk(剩余大小)。其中Remainder chunk成为新的top chunk。
    当top chunk大小小于用户所请求的大小时,top chunk就通过sbrk(main arena)或mmap(thread arena)系统调用来扩容。
  2. mmaped chunk
    当分配的内存非常大(大于分配阀值,默认128K)的时候,需要被mmap映射,则会放到mmaped chunk上,当释放mmaped chunk上的内存的时候会直接交还给操作系统。
    3、Last remainder chunk
    当在small bins中找不到合适的chunk,如果last remainder chunk的大小大于所需要的small chunk大小,last remainder chunk就会被分裂成两个chunk,其中一个chunk返回给用户,另一个chunk变成新的last remainder chunk。

tccache

线程会在thread local中记住用过的main arenas,如果这个arenas被占用,那么就会阻塞等待其释放。
线程也有自己的cache,被称为_tcache,这个块大小受到限制,_分配时不需要使用arenas,而回退时需要使用arenas。
image.png

内存分配算法

image.png

  1. 如果在tcache中有一个合适的(精确匹配)块,它就会返回给调用者。没有则尝试使用来自较大大小的bins的可用块。
  2. 如果请求足够大,则使用mmap()直接从操作系统请求内存。请注意,mmap的阈值是动态的,可以通过M_mmap_threshold参数修改,并且同时可以有多少mmap是有限制的。
  3. 如果合适的fastbin中有一个chunk,请使用它。如果有其他chunk可用,也可以预填充tcache。
  4. 如果适当的smallbin中有一个chunk,请使用它,可能还会在此处预填充tcache。
  5. 如果请求“很大”,花点时间把fast bins里的所有东西都拿走,然后把它们移到unsorted bins里,边走边合并。
  6. 开始从unsorted bins中取出块,并将它们移到小/大的bins中,边走边合并(注意,这是代码中唯一将块放入小/大bins的地方)。如果看到一个合适大小的chunk,则使用它。
  7. 如果请求是“大”的,则搜索相应的大bin,然后依次搜索更大的bin,直到找到足够大的chunk。
  8. 如果我们在fastbin中仍然有块(这可能发生在“小”请求中),请合并这些块并重复前两个步骤。

内存释放算法

free()调用并不会真正将内存返还到操作系统,而仅标记为可被应用程序重用。如果top chunk内存足够大,那么可能会取消映射。

  1. 如果tcache中有空间,则将块存储在那里并返回。
  2. 如果区块足够小,请将其放入适当的fast bins中。
  3. 如果这个区块是mmap的,就对它进行munmap。
  4. 查看此bins是否与另一个可用bins相邻,如果相邻则合并。
  5. 将区块放在unsorted bins中,除非它现在是top trunk。
  6. 如果thunk足够大,合并所有fastbin,然后如果顶部的thunk是否足够大,将会在os中取消映射。出于性能原因,这一步骤可能会被推迟,并在malloc或其他调用期间发生。

简单聊聊GC场景下的内存释放

结合上面文章,主要是考虑GC一次回收的粒度问题。
以目前jdk默认的G1为例。
按照分代收集理论,新生代会比老年代有更频繁的gc调用。

参考资料

https://ionutbalosin.com/2020/01/hotspot-jvm-performance-tuning-guidelines/
https://man7.org/linux/man-pages/man2/mmap.2.html
https://zhuanlan.zhihu.com/p/166576293
https://www.microsoft.com/en-us/research/uploads/prod/2019/06/mimalloc-tr-v1.pdf
https://zhuanlan.zhihu.com/p/658307892
https://openjdk.org/groups/hotspot/docs/RuntimeOverview.html#Thread%20Management|outline
https://www.oracle.com/technetwork/java/javase/memorymanagement-whitepaper-150215.pdf
https://tldp.org/LDP/lki/lki-4.html
https://keys961.github.io/2019/04/10/Linux%E5%86%85%E6%A0%B8-%E9%A1%B5%E9%AB%98%E9%80%9F%E7%BC%93%E5%AD%98%E4%B8%8E%E9%A1%B5%E5%9B%9E%E5%86%99/
https://www.kernel.org/doc/gorman/html/understand/understand005.html
https://www.infradead.org/~mchehab/rst_conversion/filesystems/vfs.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1300368.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AirServer 5.63中文破解2024最新图文安装激活教程含许可证

AirServer是一款便捷式投屏软件,它的主要功能在于实时地将移动设备上的图像画面内容投放到电脑设备上,让电脑成为iPad、iPhone等iOS系统设备的大屏显示器。 在设备之间建立局域网内的信号发送与接收通道,确保数据可以稳定安全地进行传输。下面…

大模型对数据分布变化的鲁棒性研究综述

摘要: 标准机器学习的一个主要限制是它产生的模型对分布变化不鲁棒,其中训练分布与测试分布不匹配(对于下游任务)。现有的工作表明,调整在广泛的未标记数据上训练的基础模型可以提高适应模型在各种变化中的鲁棒性。这为…

【Java 基础】28 字符编码

文章目录 1.字符集2.编码方式1)ASCII2)UTF-8 编码3)UTF-16 编码4)ISO-8859-1 编码 3.使用样例Charset 类String 类 4.注意事项结语 字符编码(Character Encoding)是一种将字符映射为二进制数据的规则或算法…

如何理解 RPC 远程服务调用?

本文主要讲解 RPC 远程服务调用相关的知识。 RPC 远程服务调用是分布式服务架构的基础,无论微服务设计上层如何发展,讨论服务治理都绕不开远程服务调用,那么如何理解 RPC、有哪些常见的 RPC 框架、实现一款 RPC 框架需要哪些技术呢&#xff…

macos下安装科研绘图软件Origin

科研人必备软件Origin,主要是考虑到很多期刊都要求绘制origin可编辑的图,所以有些时候必须用这个软件,但是这个软件macos并不支持,所以必须考虑其他的方案,我没有安装虚拟机,而是使用crossover 安装crosso…

Vue 静态渲染 v-pre

v-pre 指令&#xff1a;用于阻止 Vue 解析这个标签&#xff0c;直接渲染到页面中。 语法格式&#xff1a; <div v-pre> {{ 数据 }} </div> 基础使用&#xff1a; <template><h3>静态渲染 v-pre</h3><p v-pre>静态渲染&#xff1a;{{ n…

Java中的IO流②——字符集、字符流、缓冲流

主要的编码规范&#xff1a; GBK编码 Windows自带 Unicode编码 为什么会有乱码&#xff1f; 原因一 原因二 编码和解码的代码实现 字符流 FileReader read()细节&#xff1a; 一&#xff1a;默认也是一个字节一个字节的读取&#xff0c;如果遇到中文就会一次读取多个 二&a…

Terminator的layout设置(一个新的一键启动思路)

首先你得有terminator&#xff1a; sudo apt install terminator然后就能使用了&#xff0c;我一般喜欢修改它原本的水平和垂直分割&#xff1a;用ctrlshifta和ctrlshifts 把屏幕先分成多块&#xff1a; 比如是这样的&#xff0c;接下来 右键->点击Preference 弹框中上方标…

pdi-ce-9.4.0.0-343.zip和pentaho-server-ce-9.4.0.0-343.zip区别及简单使用

目录 &#x1f351;一、概述&#x1f34a;1.1、pdi-ce-9.4.0.0-343.zip&#x1f34a;1.2、pentaho-server-ce-9.4.0.0-343.zip &#x1f351;二、简单使用&#x1f34a;2.1、pdi-ce-9.4.0.0-343&#x1f34a;2.2、pentaho-server-ce-9.4.0.0-343&#x1f34a;2.3、联合使用 &am…

MongoDB的连接数据库,创建、删除数据库,创建、删除集合命令

本文主要介绍MongoDB的连接数据库&#xff0c;创建、删除数据库&#xff0c;创建、删除集合命令。 目录 MongoDB连接数据库连接到本地 MongoDB 实例连接到远程 MongoDB 实例 MongoDB创建和删除数据库MongoDB创建和删除集合创建集合删除集合 MongoDB连接数据库 连接 MongoDB 数…

报表生成器Stimulsoft用户手册:预览中具有动态数据排序的报告

Stimulsoft Reports 是一款报告编写器&#xff0c;主要用于在桌面和Web上从头开始创建任何复杂的报告。可以在大多数平台上轻松实现部署&#xff0c;如ASP.NET, WinForms, .NET Core, JavaScript, WPF, Angular, Blazor, PHP, Java等&#xff0c;在你的应用程序中嵌入报告设计器…

[香橙派]Orange pi zero 3命令行配网方法——建立ssh连接——Ubuntu配置WIFI自动连接

一、前言 前面我们给Orange Pi安装了Ubuntu系统&#xff0c;并通过MobaXterm进行了串口连接&#xff0c;但其实并不方便&#xff0c;在日常开发中&#xff0c;我们希望能够使用更方便的ssh连接来进行操作&#xff0c;因此配置网络是必要的。 本章介绍的方法无需网线、HDMI线等&…

我的NPI项目之Android 安全系列 -- Android Strongbox 初识

从Android9(Pie)开始,Google强烈建议支持Strongbox. 具体描述如下: 一直到目前的Android14. 对应的内容也一并贴出来: 说人话就是Android开始通过独立于主SoC的单元进行密钥存储了。 通常&#xff0c;这样的单元就是我们通常称作的Secure Element&#xff08;SE&#xff09;&am…

关于优雅的使用SQL多行转多列的记录(doris)

文章目录 应用需求场景记录过程1. 准备数据2. 给数据根据姓名分组&#xff0c;加上序号.3. 根据name分组成map结构4. 拆分map 应用需求场景 准备的数据是这样的&#xff1a; 需要将每个人的成绩显示在一行上&#xff0c;需要的结果如下&#xff0c;但是我的情况是课程有非常…

【MySQL进阶】索引使用

一、索引使用 1.验证索引效率 tb_sku 这张表中准备了 1000w 的记录。 我用夸克网盘分享了「1000w的模拟数据」链接&#xff1a;https://pan.quark.cn/s/15cf665202b2 这张表中id为主键&#xff0c;有主键索引&#xff0c;而其他字段是没有建立索引的。 我们先来查询其中的…

阿里内部教程Jmeter 性能测试常用图表、服务器资源监控

性能测试常用图表 插件安装 步骤 1&#xff1a;安装插件管理器 在 Jmeter 官网上下载插件管理器 Plugins-manager-1.3.jar将 jar 包放入到 lib\ext 目录下重启 Jmeter&#xff0c;可以在选项下看到 Plugins Manager 选项 步骤 2&#xff1a;安装指定的插件 打开 Plugins Ma…

常见web漏洞的流量分析

常见web漏洞的流量分析 文章目录 常见web漏洞的流量分析工具sql注入的流量分析XSS注入的流量分析文件上传漏洞流量分析文件包含漏洞流量分析文件读取漏洞流量分析ssrf流量分析shiro反序列化流量分析jwt流量分析暴力破解流量分析命令执行流量分析反弹shell 工具 攻击机受害机wi…

【论文笔记】Gemini: A Family of Highly Capable Multimodal Models——细看Gemini

Gemini 【一句话总结&#xff0c;对标GPT4&#xff0c;模型还是transformer的docoder部分&#xff0c;提出三个不同版本的Gemini模型&#xff0c;Ultra的最牛逼&#xff0c;Nano的可以用在手机上。】 谷歌提出了一个新系列多模态模型——Gemini家族模型&#xff0c;包括Ultra…

SSD数据在写入NAND之前为何要随机化?-part2

接part1介绍&#xff1a; 如何达到这个目的&#xff1f;业内常用的是对写入数据的数据进行随机化处理&#xff0c;这部分主要在SSD控制器中通过硬件实现。 上图b/c&#xff1a;在控制器芯片通过硬件方式实现随机化的读写流程&#xff0c;这个也是业内通常做法。随机化处理是在写…

TypeScript 之 console的使用

语言&#xff1a; TypeScript 在线工具&#xff1a; PlayGround console console 对象是一个非常强大的控制台日志显示工具&#xff0c; 可以帮助我们在浏览器中调试代码。 注&#xff1a; console不属于TypeScript的语法&#xff0c;而是由JavaScript封装的内置对象。 简单的…