int epoll_create(int size);//创建一个epoll的句柄,size用来告诉内核这个监听的数目一共有多大 // 只是一个初始大小 int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); intepoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
B 树的检索的过程相当于对范围内的每个节点的关键字做二分查找,可能还没有到达叶子节点,检索就结束了。而 B+树的检索效率就很稳定了,任何查找都是从根节点到叶子节点的过程。
MyISAM实现
非聚簇索引,在索引数据结构(叶子节点的data域)上存放的是数据记录的地址。在索引检索的时候,首先按照B+Tree搜索算法搜索索引,如果指定的Key存在,则取出其 data 域的值,然后以 data 域的值为地址读取相应的数据记录。非聚集索引的叶子节点并不一定存放数据的指针, 因为二级索引的叶子节点就存放的是主键,根据主键再回表查数据。
间隙锁是施加在索引记录之间间隙、在第一个索引记录之前和最后一个索引记录之后的锁。例如查询 SELECT c1 FROM t WHERE c1 BETWEEN 10 and 20 FOR UPDATE会阻止其他事务插入一个t.c1 = 15的行,因为在这个范围内的间隙都已被加锁。(一个间隙指的是一个区间)
127.0.0.1:6379> multi OK 127.0.0.1:6379> set test1 value1-3 QUEUED 127.0.0.1:6379> lpush test2 value2-3 QUEUED 127.0.0.1:6379> set test3 value3-3 QUEUED 127.0.0.1:6379> exec 1) OK 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 3) OK 127.0.0.1:6379> get test1 "value1-3" 127.0.0.1:6379> get test3 "value3-3"
大致的做法:使用k个hash函数,对于每个要加入的元素,计算出k个hash值。准备一个长度为m的bitmap,然后令这k个hash值对m取模,得到k个在[0, m - 1]范围内的下标,将bitmap中这k个下标对应位置的bit设置为1。对于一个待查找的元素,用同样的k个hash函数计算出k个hash值并对m取模得到k个下标,如果这k个下标对应的位都是1,则说明这个元素可能在集合中,否则,说明这个元素一定不在集合中。布隆过滤器有将不存在的元素判断为在集合中的可能,但是一定不会将存在于集合中的元素判断为不在集合中。
网络接口层(数据链路层/物理层):数据链路层:两台主机之间的数据传输,总是在一段一段的链路上传送的,这就需要使用专门的链路层的协议。 在两个相邻节点之间传送数据时,数据链路层将网络层交下来的 IP 数据报组装成帧,在两个相邻节点间的链路上传送帧。每一帧包括数据和必要的控制信息(如同步信息,地址信息,差错控制等)。物理层:实现相邻计算机节点之间比特流的透明传送,尽可能屏蔽掉具体传输介质和物理设备的差异。
快重传与快恢复(Fast Retransmit and Recovery,FRR):如果不使用快重传,数据包丢失时TCP将会使用定时器来要求传输暂停(等待ACK到来或超时重传),这段时间内没有新的或复制的数据包被发送。如果使用FRR,接收机接收到一个不按顺序的数据段,它会立即给发送机发送一个重复确认。如果发送机接收到三个重复确认,它会假定确认件指出的数据段丢失了,并立即重传这些丢失的数据段。有了 FRR,就不会因为重传时要求的暂停被耽误。
tcp_max_syn_backlog - INTEGER Maximal number of remembered connection requests, which have not received an acknowledgment from connecting client. The minimal value is 128 for low memory machines, and it will increase in proportion to the memory of machine. If server suffers from overload, try increasing this number. 根据大意是指半连接队列的最大长度。
somaxconn - INTEGER Limit of socket listen() backlog, known in userspace as SOMAXCONN. Defaults to 128. See also tcp_max_syn_backlog for additional tuning for TCP sockets.
Serial(串行)收集器是最基本、历史最悠久的垃圾收集器了。大家看名字就知道这个收集器是一个单线程收集器了。它的 “单线程” 的意义不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( “Stop The World” ),直到它收集结束。
新生代采用标记-复制算法,老年代采用标记-整理算法。
ParNew
ParNew 收集器其实就是 Serial 收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和 Serial 收集器完全一样。它是许多运行在 Server 模式下的虚拟机的首要选择,除了 Serial 收集器外,只有它能与 CMS 收集器(真正意义上的并发收集器,后面会介绍到)配合工作。
会暂停用户线程(Stop The World),适用与科学计算、大数据处理等弱交互场景。
新生代采用标记-复制算法,老年代采用标记-整理算法。
并行(Parallel) :指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行,可能会交替执行),用户程序在继续运行,而垃圾收集器运行在另一个 CPU 上。
Parallel Scavenge
Parallel Scavenge 收集器也是使用标记-复制算法的多线程收集器,它看上去几乎和 ParNew 都一样。 那么它有什么特别之处呢?(Parallel Scavenge无法与CMS配合工作) Parallel Scavenge 收集器关注点是吞吐量(高效率的利用 CPU)。CMS 等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。(降低总的停顿时间 vs 降低单次停顿需要的时间)所谓吞吐量就是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值。 Parallel Scavenge 收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解,手工优化存在困难的时候,使用 Parallel Scavenge 收集器配合自适应调节策略,把内存管理优化交给虚拟机去完成也是一个不错的选择。 此为JDK1.8的默认收集器。
新生代采用标记-复制算法,老年代采用标记-整理算法。
Serial Old
Serial 收集器的老年代版本,它同样是一个单线程收集器。它主要有两大用途:一种用途是在 JDK1.5 以及以前的版本中与 Parallel Scavenge 收集器搭配使用,另一种用途是作为 CMS 收集器的后备方案。
Parallel Old
Parallel Scavenge 收集器的老年代版本。使用多线程和“标记-整理”算法。在注重吞吐量以及 CPU 资源的场合,都可以优先考虑 Parallel Scavenge 收集器和 Parallel Old 收集器。
CMS(Concurrent Mark Sweep,也是用于老年代的垃圾收集器)
CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。它非常符合在注重用户体验的应用上使用。是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。是一种标记-清除算法的实现。
在发生 Minor GC 之前,虚拟机先检查老年代最大可用的连续空间是否大于新生代所有对象总空间,如果条件成立的话,那么 Minor GC 可以确认是安全的。 如果不成立的话虚拟机会查看 HandlePromotionFailure 的值是否允许担保失败,如果允许那么就会继续检查老年代最大可用的连续空间是否大于历次晋升到老年代对象的平均大小,如果大于,将尝试着进行一次 Minor GC;如果小于,或者 HandlePromotionFailure 的值不允许冒险,那么就要进行一次 Full GC。
这里所设置的初始值”通常情况”下是数据类型默认的零值(如0、0L、null、false等),比如我们定义了public static int value=111 ,那么 value 变量在准备阶段的初始值就是 0 而不是111(初始化阶段才会赋值)。特殊情况:比如给 value 变量加上了 fianl 关键字public static final int value=111 ,那么准备阶段 value 的值就被赋值为 111。
JVM运行时并不会一次性加载所需要的全部类,它是按需加载(懒加载、延迟加载)。比如你在调用某个类的静态方法时,首先这个类肯定是需要被加载的,但是并不会触及这个类的实例字段,那么实例字段的类别 Class 就可以暂时不必去加载,但是它可能会加载静态字段相关的类别,因为静态方法会访问静态字段。而实例字段的类别需要等到你实例化对象的时候才可能会加载。
程序在运行过程中,遇到了一个未知的类,它会选择哪个 ClassLoader 来加载它呢?虚拟机的策略是使用调用者 Class 对象的 ClassLoader 来加载当前未知的类。何为调用者 Class 对象?就是在遇到这个未知的类时,虚拟机肯定正在运行一个方法调用(静态方法或者实例方法),这个方法挂在哪个类上面,那这个类就是调用者 Class 对象。前面我们提到每个 Class 对象里面都有一个 classLoader 属性记录了当前的类是由谁来加载的。
privatefinal ClassLoader parent; protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // 首先,检查请求的类是否已经被加载过 Class<?> c = findLoadedClass(name); if (c == null) { long t0 = System.nanoTime(); try { if (parent != null) {//父加载器不为空,调用父加载器loadClass()方法处理 c = parent.loadClass(name, false); } else {//父加载器为空,使用启动类加载器 BootstrapClassLoader 加载 c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { //抛出异常说明父类加载器无法完成加载请求 } if (c == null) { long t1 = System.nanoTime(); //自己尝试加载 c = findClass(name);
// this is the defining class loader; record the stats sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } } if (resolve) { resolveClass(c); } return c; } }
// Entry的定义 /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ // 通过继承WeakRefernce<ThreadLocal>的方式实现"以ThreadLocal对象的弱引用作为Key" staticclassEntryextendsWeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { // 此处完成WeakReference<ThreadLocal<?>>的初始化 super(k); value = v; } }
// ThreadLocalMap内部使用Entry数组进行存储 //ThreadLocalMap构造方法 ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) { //内部成员数组,INITIAL_CAPACITY值为16的常量 table = new Entry[INITIAL_CAPACITY]; //位运算,结果与取模相同,计算出需要存放的位置 //threadLocalHashCode比较有趣 int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1); table[i] = new Entry(firstKey, firstValue); size = 1; setThreshold(INITIAL_CAPACITY); }