零拷贝原理

零拷贝原理

零拷贝(Zero-copy)技术指在计算机执行操作时,CPU 不需要先将数据从一个内存区域复制到另一个内存区域,从而可以减少上下文切换以及 CPU 的拷贝时间。它的作用是在数据报从网络设备到用户程序空间传递的过程中,减少数据拷贝次数,减少系统调用,实现 CPU 的零参与,彻底消除 CPU 在这方面的负载。实现零拷贝用到的最主要技术是 DMA 数据传输技术和内存区域映射技术。 零拷贝机制可以减少数据在内核缓冲区和用户进程缓冲区之间反复的 I/O 拷贝操作。 零拷贝机制可以减少用户进程地址空间和内核地址空间之间因为上下文切换而带来的 CPU 开销。 1. 物理内存和虚拟内存 由于操作系统的进程与进程之间是共享 CPU 和内存资源的,因此需要一套完善的内存管理机制防止进程之间内存泄漏的问题。为了更加有效地管理内存并减少出错,现代操作系统提供了一种对主存的抽象概念,即是虚拟内存(Virtual Memory)。虚拟内存为每个进程提供了一个一致的、私有的地址空间,它让每个进程产生了一种自己在独享主存的错觉(每个进程拥有一片连续完整的内存空间)。 1.1. 物理内存 物理内存(Physical memory)是相对于虚拟内存(Virtual Memory)而言的。物理内存指通过物理内存条而获得的内存空间,而虚拟内存则是指将硬盘的一块区域划分来作为内存。内存主要作用是在计算机运行时为操作系统和各种程序提供临时储存。在应用中,自然是顾名思义,物理上,真实存在的插在主板内存槽上的内存条的容量的大小。 1.2. 虚拟内存 虚拟内存是计算机系统内存管理的一种技术。 它使得应用程序认为它拥有连续的可用的内存(一个连续完整的地址空间)。而实际上,虚拟内存通常是被分隔成多个物理内存碎片,还有部分暂时存储在外部磁盘存储器上,在需要时进行数据交换,加载到物理内存中来。 目前,大多数操作系统都使用了虚拟内存,如 Windows 系统的虚拟内存、Linux 系统的交换空间等等。 虚拟内存地址和用户进程紧密相关,一般来说不同进程里的同一个虚拟地址指向的物理地址是不一样的,所以离开进程谈虚拟内存没有任何意义。每个进程所能使用的虚拟地址大小和 CPU 位数有关。在 32 位的系统上,虚拟地址空间大小是 2 ^ 32 = 4G,在 64位系统上,虚拟地址空间大小是 2 ^ 64= 2 ^ 34G,而实际的物理内存可能远远小于虚拟内存的大小。每个用户进程维护了一个单独的页表(Page Table),虚拟内存和物理内存就是通过这个页表实现地址空间的映射的。下面给出两个进程 A、B 各自的虚拟

小米miui12.5加入了很多app启动广告

小米miui12.5加入了很多app启动广告

空闲喜欢用掌阅听听书,以前miui12时很早就改了miad文件夹,没感觉到广告多. 上两升级miui12.5,发现掌阅的启动广告简直变态,切换到后台再切回都会有广告,实在不能忍,就找出以前的ZUK装上掌阅看看. 竟然只有首次启动才有广告,后续怎么切后台都不会有广告了!原来一直都是MIUI添加的启动广告,且锅还让APP来背,MIUI实在高!! 手上有小米系和非小米的手机可以多比较一下,抽出更多被坑的APP!! 这里介绍一个解决的方法: 文件夹找到下面路经Android/data/com.miui.systemAdSolution把miad文件夹删除,另找一个非文件夹类型文件改下成miad 你会发现小米加入的烦人的启动广告全消失了,下图这种样式的广告就是miui添加的广告 

decimal(numeric )、float 和 real 数据类型的区别

decimal(numeric )、float 和 real 数据类型的区别

decimal(numeric ) 同义,用于精确存储数值 float 和 real 不能精确存储数值 decimal数据类型最多可存储 38 个数字,所有数字都能够放到小数点的右边。decimal数据类型存储了一个准确(精确)的数字表达法;不存储值的近似值。 定义decimal的列、变量和参数的两种特性如下: p 小数点左边和右边数字之和,不包括小数点。如 123.45,则 p=5,s=2。 指定精度或对象能够控制的数字个数。 s 指定可放到小数点右边的小数位数或数字个数。 p 和 s 必须遵守以下规则:0 <=s<=p<= 38。 numeric和decimal数据类型的默认最大精度值是 38。在 Transact-SQL 中,numeric与decimal数据类型在功能上等效。 当数据值一定要按照指定精确存储时,可以用带有小数的decimal数据类型来存储数字。 float 和 real 数据 float和real数据类型被称为近似的数据类型。在近似数字数据类型方面,float和real数据的使用遵循 IEEE 754 标准。 近似数字数据类型并不存储为多数数字指定的精确值,它们只储存这些值的最近似值。在很多应用程序中,指定值与存储值之间的微小差异并不明显。但有时这些差异也值得引起注意。由于float和real数据类型的这种近似性,当要求精确的数字状态时,比如在财务应用程序中,在那些需要舍入的操作中,或在等值核对的操作中,就不使用这些数据类型。这时就要用integer、decimal、money或smallmone数据类型。 在 WHERE 子句搜索条件中(特别是 = 和 <> 运算符),应避免使用float或real列。最好限制使用float和real列做> 或 < 的比较。 IEEE 754 规格提供了四种舍入模式:舍入到最接近的值、上舍入、下舍入和舍入到零。Microsoft® SQL Server™ 使用上舍入。所有的数值必须精确到确定的精度,但会产生细小的浮点值变化。因为浮点数字的二进制表示法可以采用很多合法舍入规则中的任意一条,因此我们不可能可靠地量化一个浮点值。 转换 decimal 和 numeric 数据 对于decimal和numeric数据类型,Microsoft® SQL Server™ 将精度和小数位数的每个特定组合看作是不同的数据类型。例如,decimal(5,5) 和decimal(5,0) 被当作不同的数据类型。 在 Transact-SQL 语句中,带有小数点的常量自动转换为numeric数据值,且必然使用最小的精度和小数位数。例如,常量 12.345 被转换为numeric值,其精度为 5,小数位为 3。 从decimal或numeric向float或real转换会导致精度损失。从int、smallint、tinyint、float、real、money或smallmoney向decimal或numeric转换会导致溢

Reference 、ReferenceQueue 详解

Reference 、ReferenceQueue 详解

ReferenceQueue 引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到该队列中 实现了一个队列的入队(enqueue)和出队(poll还有remove)操作,内部元素就是泛型的Reference,并且Queue的实现,是由Reference自身的链表结构( 单向循环链表 )所实现的。 ReferenceQueue名义上是一个队列,但实际内部并非有实际的存储结构,它的存储是依赖于内部节点之间的关系来表达。可以理解为queue是一个类似于链表的结构,这里的节点其实就是reference本身。可以理解为queue为一个链表的容器,其自己仅存储当前的head节点,而后面的节点由每个reference节点自己通过next来保持即可。 属性 head:始终保存当前队列中最新要被处理的节点,可以认为queue为一个后进先出的队列。当新的节点进入时,采取以下的逻辑: r.next = (head == null) ? r : head; head = r; 然后,在获取的时候,采取相应的逻辑: Reference<? extends T> r = head; if (r != null) { head = (r.next == r) ? null : r.next; // Unchecked due to the next field having a raw type in Reference r.queue = NULL; r.next = r; 方法 enqueue():待处理引用入队 boolean enqueue(Reference<? extends T> r) { /* Called only by Reference class */ synchronized (lock) { // Check that since getting the lock this reference hasn't already been // enqueued (and even then removed) ReferenceQueue<?> queue = r.queue; if ((queue == NULL) || (queue == ENQUEUED)) { return false; } assert queue == this; r.queue = ENQUEUED; r.next = (head == null) ? r : head; head = r; queueLength++; if (r instanceof FinalReference) { sun.misc.VM.addFinalRefCount(1); } lock.notifyAll(); // ① return true; } } ① lock.notifyAll(); 👈通知外部程序之前阻塞在当前队列之上的情况。( 即之前一直没有拿到待处理的对象,如ReferenceQueue的remove()方法 ) Reference java.lang.ref.Reference 为 软(soft)引用

高频面试题:手写一个LRU

高频面试题:手写一个LRU

之前暑期实习面试的时候也被问到了,需要我们说一下思路,然后实现。如果让我们简单来实现一下的话,有很多的方式。比如Java就有自带的LinkedHashMap来实现,但是面试官既然问了那便是不想让你直接调用接口了。我们一般都是用哈希+双向链表来实现。下面给出三种实现方式,参考leetcode题解,leetcode上面也有类似的题。 它应该支持以下操作: 获取数据 get 和 写入数据 put 。 获取数据 get(key) - 如果关键字 (key) 存在于缓存中,则获取关键字的值(总是正数),否则返回 -1。 写入数据 put(key, value) - 如果关键字已经存在,则变更其数据值;如果关键字不存在,则插入该组「关键字/值」。当缓存容量达到上限时,它应该在写入新数据之前删除最久未使用的数据值,从而为新的数据值留出空间。 LinkedHashMap 最容易实现的,但也是面试官应该最不想看到的。毕竟这样完全考察不到你的coding能力。 public class LRUCache<K, V> extends LinkedHashMap<K, V> { private int initialCapacity; public LRUCache(int initialCapacity) { //true表示按照访问顺序排序 super(initialCapacity, 0.75f, true); this.initialCapacity = initialCapacity; } public int get(int key) { return super.getOrDefault(key, -1); } public void put(int key, int value) { super.put(key, value); } @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest) { if (size() > initialCapacity) { return true; } return false; } } 哈希+队列 用哈希来存取值,以对首是最久未使用的,对尾是最近使用来实现 class LRUCache { Map<Integer,Integer> map; //队列,对首是越久未使用的,对尾是最近使用的 Queue<Integer> queue; int cap; public LRUCache(int capacity) { this.map = new HashMap<>(); this.queue = new LinkedList<>(); this.cap = capacity; } public int get(int key) { //如果key存在,移除重新添加就能靠近队尾 if(queue.contains(key)){ queue.remove(key); queue.add(key); return map.get(key); }else{ return -1; } } public void

联系我们

联系电话

4000-640-466

联系邮箱

service@f-li.cn

办公地址

上海黄浦区外滩源1号

谢谢,您的信息已成功发送。
请填写信息。