killWork -- bug's party -- 180729

class 的热替换

  1. class 的热替换包含 tomcat
    http://www.importnew.com/22462.html
    jar 包的读取
    https://blog.csdn.net/hy_timer/article/details/76268759

    netty selector 原理

  2. NIO 中 selector 是通过 epoll 实现
    https://cloud.tencent.com/developer/article/1005481

[1]select、poll、epoll_wait 陷入内核,判断监控的socket是否有关心的事件发生了,如果没,则为当前 process 构建一个 wait_entry 节点,然后插入到监控 socket 的 sleep_list
[2]进入循环的schedule 直到关心的事件发生了
[3]关心的事件发生后,将当前 process 的 wait_entry 节点从 socket 的 sleep_list 中删除。

[1]socket 的事件发生了,然后 socket 顺序遍历其睡眠队列,依次调用每个 wait_entry 节点的 callback 函数
[2]直到完成队列的遍历或遇到某个 wait_entry 节点是排他的才停止。
[3]一般情况下callback 包含两个逻辑:
1.wait_entry自定义的私有逻辑;
2.唤醒的公共逻辑,主要用于将该 wait_entry 的 process 放入CPU的就绪队列,让CPU随后可以调度其执行。

select 逻辑

  1. 我们应该block在等待事件的发生上,这个事件简单点就是”关心的N个socket中一个或多个socket有数据可读了”,当block解除的时候,就意味着,我们一定可以找到一个或多个socket上有可读的数据。
  2. 另一方面,根据上面的socket wakeup callback机制,我们不知道什么时候,哪个socket会有读事件发生,于是,process需要同时插入到这N个socket的sleep_list上等待任意一个socket可读事件发生而被唤醒,当时process被唤醒的时候,其callback里面应该有个逻辑去检查具体那些socket可读了。

当用户process调用select的时候,
select会将需要监控的readfds集合拷贝到 内核空间(假设监控的仅仅是socket可读),
然后遍历自己监控的socket_sk,挨个调用sk的poll逻辑以便检查该sk是否有可读事件,
遍历完所有的sk后, 如果没有任何一个sk可读,那么select会调用schedule_timeout进入schedule循环,使得process进入睡眠。
如果在timeout时间内某个sk上有数据可读了,或者等待timeout了,则调用select的process会被唤醒,接下来select就是遍历监控的sk集合,挨个收集可读事件并返回给用户了

epoll 逻辑

fds集合拷贝问题的解决

epoll引入了epoll_ctl系统调用,将高频调用的epoll_wait和低频的epoll_ctl隔离开。
epoll_ctl通过(EPOLL_CTL_ADD、EPOLL_CTL_MOD、EPOLL_CTL_DEL)三个操作来分散对需要监控的fds集合的修改,做到了有变化才变更,
将select或poll高频、大块内存拷贝(集中处理)变成epoll_ctl的低频、小块内存的拷贝(分散处理),避免了大量的内存拷贝。

同时,对于高频epoll_wait的可读就绪的fd集合返回的拷贝问题,epoll通过内核与用户空间mmap(内存映射)同一块内存来解决。mmap将用户空间的一块地址和内核空间的一块地址同时映射到相同的一块物理内存地址(不管是用户空间还是内核空间都是虚拟地址,最终要通过地址映射映射到物理地址),使得这块物理内存对内核和对用户均可见,减少用户态和内核态之间的数据交换。

epoll通过epoll_ctl来对监控的fds集合来进行增、删、改,那么必须涉及到fd的快速查找问题,于是,一个低时间复杂度的增、删、改、查的数据结构来组织被监控的fds集合是必不可少的了。
fds 集合为红黑树。

按需遍历就绪的fds集合

通过上面的socket的睡眠队列唤醒逻辑我们知道,socket唤醒睡眠在其睡眠队列的 wait_entry(process )的时候会调用wait_entry的回调函数callback,
并且,我们可以在callback中做任何事情。为了做到只遍历就绪的fd,我们需要有个地方来组织那些已经就绪的fd。
为此,epoll引入了一个中间层,一个双向链表(ready_list)作为一个单独的睡眠队列(single_epoll_wait_list)
并且,与select或poll不同的是,epoll的process不需要同时插入到多路复用的socket集合的所有睡眠队列中,
相反process只是插入到中间层的epoll的单独睡眠队列中,process睡眠在epoll的单独队列上,等待事件的发生。

同时,引入一个中间的wait_entry_sk,它与某个socket_sk密切相关,wait_entry_sk睡眠在sk的睡眠队列上,
其callback函数逻辑是将当前sk排入到epoll的ready_list中,并唤醒epoll的single_epoll_wait_list。
而single_epoll_wait_list上睡眠的process的回调函数就明朗了:遍历 ready_list 上的所有sk,挨个调用sk的poll函数收集事件,然后唤醒 process 从 epoll_wait 返回。

ET(Edge Triggered 边沿触发) vs LT(Level Triggered 水平触发)

Edge Triggered (ET) 边沿触发

  1. socket的接收缓冲区状态变化时触发读事件,即空的接收缓冲区刚接收到数据时触发读事件
  2. socket的发送缓冲区状态变化时触发写事件,即满的缓冲区刚空出空间时触发读事件
    仅在缓冲区状态变化时触发事件,比如数据缓冲去从无到有的时候(不可读-可读)

Level Triggered (LT) 水平触发

  1. socket接收缓冲区不为空,有数据可读,则读事件一直触发
  2. socket发送缓冲区不满可以继续写入数据,则写事件一直触发

ifcfg-eth33 prefix 参数

为了自己的虚拟机里的 Linux 可以连接上网,不知道在哪拷贝了一段代码,
加入到了自己的文件里。
但是还是连接不上,经过排错后,发现是 因为 prefix 参数的问题。
如果 prefix 参数设置不对,那么 ip 地址和 网关 不管怎么修改,都不对。
最简单的方法,删除 prefix 参数。

isAssignableFrom() 与 instanceof() 区别

  • isAssignableFrom()方法是从类继承的角度去判断,instanceof()方法是从实例继承的角度去判断。
  • isAssignableFrom()方法是判断是否为某个类的父类,instanceof()方法是判断是否某个类的子类。

JDK 自带的监控器模式

在Java中通过Observable类和Observer接口实现了观察者模式。Observer对象是观察者,Observable对象是被观察者。
实现观察者模式非常简单,
[1]创建被观察者类,它继承自java.util.Observable类;
[2]创建观察者类,它实现java.util.Observer接口;
[3]对于被观察者类,
添加它的观察者:
void addObserver(Observer o)

addObserver()方法把观察者对象添加到观察者对象列表中。

当被观察事件发生时,执行:

1
2
setChanged();
notifyObservers();

setChange()方法用来设置一个内部标志位注明数据发生了变化;notifyObservers()方法会去调用观察者对象列表中所有的Observer的update()方法,通知它们数据发生了变化。
只有在setChange()被调用后,notifyObservers()才会去调用update()。

[4]对于观察者类,实现Observer接口的唯一方法update

1
void update(Observable o, Object arg)

形参Object arg,对应一个由notifyObservers(Object arg);传递来的参数,当执行的是notifyObservers();时,arg为null。

获取 Annotation 相关的注解

http://liuxi.name/blog/20161227/java-annotations-api.html

1
2
3
4
5
6
7
8
getAnnotation
getAnnotations
getAnnotationByType
// 1. 忽略继承
// 2. JDK 1.8 以下没用
getDeclaredAnnotation
getDeclaredAnnotations
getDeclaredAnnotationByType

getResourceAsStream – getSystemResourceAsStream

ClassLoader().getSystemResource()
只能获取 jvm 的 ClassLoader, 获取不到 tomcat 的 ClassLoader

this.getClass().getClassLoader().getResource()
获取当前类的 ClassLoader – 随环境改变

this.getClass().getResource()
相对路径,会判断文件路径 “/” 还是 “./” 必须加入 /

Thread.currentThread().getContextClasLoader().getResource()
加载当前线程的 ClassLoader

1
2
3
4
5
6
7
8
9
10
11
System.out.println(ClassLoader.getSystemResource("").getPath());
// 当前类目录
System.out.println(getClass().getResource("").getPath());
// 根目录
System.out.println(getClass().getResource("/").getPath());
// 相对目录
System.out.println(getClass().getResource("./").getPath());
System.out.println(getClass().getResource("../").getPath());

System.out.println(getClass().getClassLoader().getResource("").getPath());
System.out.println(getClass().getClassLoader().getResource("").getPath());

输出

1
2
3
4
5
6
7
/D:/Programme/Tomcat-8/webapps/FR/WEB-INF/classes/
/D:/Programme/Tomcat-8/webapps/FR/WEB-INF/classes/com/fr/env/util/
/D:/Programme/Tomcat-8/webapps/FR/WEB-INF/classes/
/D:/Programme/Tomcat-8/webapps/FR/WEB-INF/classes/com/fr/env/util/
/D:/Programme/Tomcat-8/webapps/FR/WEB-INF/classes/com/fr/env/
/D:/Programme/Tomcat-8/webapps/FR/WEB-INF/classes/
/D:/Programme/Tomcat-8/webapps/FR/WEB-INF/classes/

双重校验锁的 volatie 变量

申请内存空间,
初始化默认值(区别于构造器方法的初始化),
执行构造器方法
连接引用和实例。

这4个步骤后两个有可能会重排序,1234 1243都有可能,造成未初始化完全的对象发布。
volatile可以禁止指令重排序,从而避免这个问题。

File

new File(String Path)
path 不能为这种形式。
Tomcat 8.

0%