0%

基于进程

1
2
Fork()
Signal(SIGCHLD, sigchld_handler)

共享信息比thread少,浪费资源

基于IO Multiplexing

就是使用select操作fdset

1
int select(int n, fd_set *fdset, NULL,NULL,NULL);

fdset就是fd列表的bitmap,n是大小,有这些宏操作指令

1
2
3
4
FD_ZERO(fd_set *fdset); //统统清空
FD_CLR(int fd,fd_set *fdset); //清掉一个fd
FD_SET(int fd,fd_set *fdset); //开一个fd
FD_ISSET(int fd,fd_set *fdset);//返回某个fd是开着的吗

select.c

基于线程

API

1
2
3
4
5
6
7
8
int pthread_create(pthread_t *tid, pthread_addr_t *addr, func *f, void *arg);
pthread_t pthread_self(void);
int pthread_exit(void *thread_return);
int pthread_cancel(pthread_t tid);
int pthread_join(pthread_t tid, void **thread_return);//阻塞,等tid结束,然后release它的资源,成功的话返回0
int pthread_detach(pthread_t tid);//分离tid,我不管tid了,它结束了就自己回收资源吧
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));

线程同步

共享变量count问题

1
2
3
4
5
其他指令H
movl count, %eax
leal l(%eax), %edx //count++
movl %edx, count
其他指令T

进度图

progress-graph

Semaphore

然后就要锁定不安全区
semaphore-op
semaphore
就是看每个点只能往上和往右走,但是P操作可能会被block,V操作是自由的
红线就是被block的P操作。保护出来走不到的点就是禁止区。

线程不安全的情况

  1. 共享变量
  2. 调用线程不安全的函数

死锁

deadlock
书里说的重叠就是死锁,不是说禁止区重叠,而是block的红线相交了,交点左下角那个地方又不能往上,又不能往右,死锁。
感觉这个进度图不太实用,一般判断死锁也不会去画这个。。。

攻略向

签证

  • 前提:住在上海,夫妻俩自由行,不参团,第一次申根签证
  • 选择了法签,因为朋友说法签比较慷慨,以后也好升级
    • 官方说法是要看住的酒店天数,哪国最多就哪国,如果一样多就要看先入境的,为了法签特意把意大利和法国弄成各住7晚,因为巴黎进,罗马出,所以可以法签。
  • 法签官网
    • 要注册,填报信息,准备材料,预约时间到签证中心(法国大使馆授权给签证中心了)提交材料。签证中心官方步骤
  • 费用:如果你可以工作日请假,可以省去220元人民币/人的贵宾服务费,可惜我俩不行,因为两个人必须同时请假,一起递交材料。签证费650+服务费+贵宾费大概900多一个人,签证材料还要加上保险,200多一个人。后来上网看了,感觉淘宝代办省不了多少事,代办费多出300多,自己能搞定的
  • 最麻烦的材料是在职证明,上面要求的项目很多,建议上网搜携程的模板,照着抄,否则容易漏点。
  • 照片要求根本没有那么严,正常照片都能过,当时真是多虑了。
  • 银行流水一定要柜台上拉,当时弄了个电子版,打电话确认说还是要柜台。。。
  • 行程单用了穷游行程助手直接一键导出,很好用
    • 递交材料的时候被要求跨境的交通要有订单,当场买了法国到意大利的机票。要早做准备啊。
    • 周末贵宾那个厅是有打印机和电脑的,普通的没有。

电话卡和上网

  • 一定要提前某宝订好电话卡!
  • 我们的选择是到了当地买小米的全球上网服务,极其不稳定,GPS走着走着就不动了,而且经常瞬间移动,价格也不便宜。莫不是走的欧洲版铁通网络。。。

换欧元

  • 一定要国内换好,招商银行换的,7.67的汇率换了1200欧元,2000欧元以下不用预约。
  • 法国exchange小店里骇人听闻,买入卖出价格8.9和6.7,甚至6.3,简直不能再坑。
  • 信用卡要准备好,我的是万事达单标的(没有银联标志,国内刷不了的)一般汇率7.80-7.99左右。据说多个银联标志会更贵
  • 支付宝,微信基本还没普及,都是信用卡多(当时是2019年3月)

语言

  • 英语基本可以搞定
  • 法国:出发前学了一丢丢,暴力替换一些单词可以看懂一些,不一定准确,但是实用
    • de du = of d’XXX是de XXX的缩写
    • le, la = the,一个是阴the,一个是阳the,l’XXX是le XXX的缩写
    • 发音:Paris读巴黎就已经记住了1345
      1. r变h(其实是小舌音),
      2. h不发音,
      3. 所有a按照中国拼音a发音
      4. 后面元音的话p->b t->d之类的
      5. 词尾的辅音字母不发音
    • 例子:
      • Musée du Louvre=museum of 卢浮
      • la Tour Eiffel=the tower 埃菲尔
      • Le Jardin de Luxembourg=the garden of 卢森堡
      • Musee de l’Orangerie=museum of the 橘园
      • Chateau de Versailles=castle of 凡尔塞
      • Notre Dame du Paris=我们的Madam(夫人)?of 巴黎(巴黎圣母院)
      • Gare du Nord=火车站 of North
  • 意大利
    • 属于拉丁语,也能找到好多英语的影子
    • h不发音here=ear
    • g在词尾都发音morning=墨宁哥
    • j读成y,其实怀疑应该是measure的s的那种发音,jason=伊阿宋

景点

  • 每个月第一个周日免费参观博物馆,一定要选又贵又快的博物馆
  • 关注开门关门时间,周一,周二开不开等信息
  • 巴黎博物馆通票
  • 买罗马通票一定要注意你有没有足够的时间参观罗马,如果光是交通费和斗兽场是不足以覆盖48小时票价的,应该还有凭票打折的功能
  • 威尼斯通票,29岁以下29欧,以上39欧
  • 好多学生票和青年票打折
  • 需要提前补习人家的朝代,历代君主(参观城堡,军事博物馆等用),希腊罗马神话(雕塑和油画会用到),圣经故事(油画和教堂)
  • 讲解器一般要5-7欧,死贵,但是还是需要的。

机票

  • 感觉天巡网什么的都不给力,还是国内同程,携程,去哪什么的比较便宜,买的俄罗斯航空,如果联航机票(就是一次订单买的)就不需要过境签
  • 回程买了卡塔尔航空,卡塔尔是免签国

酒店

  • 都是booking上订的,以为是hotel,其实是民宿,还要提前告知时间,否则就可能撞门上。。。
  • 会额外收取城市税,按照星级,越高级的越贵。但是感觉明明是民宿啊,强行三星级,醉了。还有一些莫名其妙的费用,比如清洁费(你不清洁开啥旅馆呢?),空调费(我都没用空调强行收空调费,我申诉了,回我说这个就算是水电费!!!哪有旅馆额外收水电费的?)反正感觉就是各种乱,还不如直接airbnb全民宿呢。

租车

  • 被坑了400把保险起赔从15000人民币降成0,其实租租车上已经买了国内的保险了,自己没仔细看条款,哎。。。
  • 导航最好google baidu一起开,否则总有一家要坑你
  • 停车好多都是路边自主缴费,用信用卡,白线可能免费,蓝线一定收费
  • 油费比国内的贵,而且巴黎市内比较堵,找加油站比较难,关门也比较早(8点),巴黎还车的话最好早点加满油。

总结

后来去网上看了跟团游的费用,好多反映在

  1. 车上的时间要一半以上,
  2. 吃的都是中餐,
  3. 好多著名景点的时间给的太少,比如卢浮宫,我们用了一天,而跟团只给2小时,罗马斗兽场才半小时
  4. 非著名景点有的不安排,比如威尼斯总督府
  5. 有很多景点得自费,而且比官方票价贵
  6. 大把时间用在购物上
    总体费用比较:如果做好足够的攻略,应该是自由行并不会贵太多,但是体验是质的飞越。

net

OSI七层模型

layers
数据:
data

internet connection:

  1. 点对点
  2. 全双工Full-duplex:双向同时可以传输信息
  3. 可靠:最终会按照顺序收到所有包

Network

SAN(System Area Network):cluster级别
LAN(Local …):building, campus
WAN(Wide …):world
internet:一堆network以共享的router互联, Internet: internet的最著名实现

Socket API

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
int getaddrinfo(const char *host,              
const char *service /* port or service name */
const struct addrinfo *hints, /* input parameters */
struct addrinfo **result) /* 这个其实是返回值linked list<addrinfo> */
//然后server端就会轮询这个linked list,尝试连接socket,直到bind成功
//client端轮询这个linked list,尝试连接socket,直到connect成功
void freeaddrinfo(struct addrinfo *result)
//free linked list
const char *gai_strerror(int errcode)
//return error message
int socket(int domain, int type, int protocol)
//3个参数总是AF_INET SOCK_STREAM 0,就是创建socket
int bind(int sockfd, SA *addr, socklen_t addrlen)
//告诉kernal把addr和sockfd bind起来
int listen(int sockfd, int backlog)
//kernal 默认socket函数的fd都是active socket,这个函数是告诉kernal把这个sockfd变成server端的listening socket,阻塞的
int accept(intlistenfd, SA *addr, int *addrlen)
//server 等待connect连上来,后两个参数其实是返回值,客户端的socket地址和地址长度,函数本身的返回值是connected fd
int connect(int clientfd, SA *addr, socklen_t addrlen)
//也是阻塞的
int open_listenfd(int port)=socket+bind+listen
int open_clientfd()=socket+connect

socket
socket address= IP地址:port
![socket-api](https://fanjingdanhttps://fanjingdan012.github.io/2019/05/09/Net/

Example Code

一个小型server,main方法进来,无限循环accept(),然后doit()做一些读写和处理
tiny.c

DNS

1
2
$> nslookup localhost
127.0.0.1

标准输入STDIN_FILENO=0 标准输出STDOUT_FILENO=1 标准错误 STDERR_FILENO=2
文件位置k=0

打开关闭

1
2
int open(char *filename, int flags, mode_t mode)
int close(int fd)

读写

1
2
ssize_t read (int fd, void *buf, size_t n) //从fd读取n个byte到buf里
ssize_t write(int fd, const void *buf, size_t n)

ssize_t是int而size_t是unsigned int

Rio(Robust io)

1
2
ssize_t rio_readn (int fd, void *usrbuf, size_t n)
ssize_t rio_writen (int fd, void *usrbuf, size_t n)

读取文件元数据

1
2
int stat(const char *filename , struct stat *buf)
int fstat(int fd, struct stat *buf)

内核处理

io
io-share
io-subthread

重定向

1
int dup2(int oldfd, int new fd)

io-dup2

所有函数

io-apis

Propagation

Propagation

源代码

其实通过Interceptor加的。
TransactionAspectSupport.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
/**
* General delegate for around-advice-based subclasses, delegating to several other template
* methods on this class. Able to handle {@link CallbackPreferringPlatformTransactionManager}
* as well as regular {@link PlatformTransactionManager} implementations.
* @param method the Method being invoked
* @param targetClass the target class that we're invoking the method on
* @param invocation the callback to use for proceeding with the target invocation
*/
@Nullable
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {

// If the transaction attribute is null, the method is non-transactional.
TransactionAttributeSource tas = getTransactionAttributeSource();
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);

if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
} else{...CallbackPreferringPlatformTransactionManager处理:可以定义transaction callback的}

}

transactionInfoHolder 其实是个ThreadLocal bindToThread

提交点

java1.4
savepoint
a1
insert1
a2
insert2
a3
insert3

savepoint是在数据库事务处理中实现“子事务”(subtransaction),也称为嵌套事务的方法。事务可以回滚到savepoint而不影响savepoint创建前的变化。不需要放弃整个事务。

SQL语言国际标准中,SAVEPOINT name语句声明一个savepoint。ROLLBACK TO SAVEPOINT name语句回滚到savepoint。RELEASE SAVEPOINT name将使得命名的savepoint被放弃,但不影响其他savepoint。ROLLBACK或COMMIT导致所有savepoint被放弃。

支持savepoint的数据库有:PostgreSQL, Oracle数据库, Microsoft SQL Server, MySQL, DB2, SQLite (从3.6.8), Firebird, H2数据库, Informix (从11.50xC3).

  • 米尔顿.弗里德曼 1912-
    • 自由经济
  • 科斯
  • 张五常
    • 市场经济
  • 阿尔钦
    • 产权与竞争

分代

分代和JMM
GC就发生在Heap和Metaspace

  • XX:SurvivorRatio = Eden/S0(=S1) = 8(default)
  • XX:NewRatio = Old/Young = 2(default)

术语

  • 并行(Parallel):指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
  • 并发(Concurrent):指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行),用户程序在继续运行,而垃圾收集程序运行于另一个CPU上。
  • 吞吐量:就是CPU用于运行用户代码的时间与CPU总消耗时间的比值,即吞吐量 = 运行用户代码时间 /(运行用户代码时间 + 垃圾收集时间)。虚拟机总共运行了100分钟,其中垃圾收集花掉1分钟,那吞吐量就是99%。停顿时间越短就越适合需要与用户交互的程序,良好的响应速度能提升用户体验,而高吞吐量则可以高效率地利用CPU时间,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的任务。
  • STW stop-the-world

java对象存活分析

引用计数法

  • 在堆中存储对象时,在对象头处维护一个counter计数器,如果一个对象增加了一个引用与之相连,则将counter++。如果一个引用关系失效则counter–。如果一个对象的counter变为0,则说明该对象已经被废弃,不处于存活状态。
  • 缺点
    • jdk从1.2开始增加了多种引用方式:软引用、弱引用、虚引用,且在不同引用情况下程序应进行不同的操作。如果我们只采用一个引用计数法来计数无法准确的区分这么多种引用的情况。
    • 循环引用导致永远无法回收

可达性分析

  • 通过一系列名为GC Roots的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的
  • GC Roots
    • 虚拟机栈(栈桢中的本地变量表)中的引用的对象
    • 方法区中的类静态属性引用的对象
    • 方法区中的常量引用的对象
    • 本地方法栈中JNI(Native方法)的引用的对象
  • HotSpot实现:
    • 使用OopMap记录并枚举根节点
      方法区太大了(几百兆),所以需要OopMap,在类加载完成时,HotSpot就把对象内什么偏移量上是什么类型的数据计算出来,在JIT编译过程中,也会在特定的位置记录下栈和寄存器中哪些位置是引用。
    • Safepoint
      “安全点”简单点说就是程序执行停顿下的这一个点。程序在执行时,并非在所有的地方都能停下来开始GC,只有到达这个“安全点“时才能停顿下来。安全点的选区既不能太少以至于让GC等待时间过长,也不能过于频繁以至于过分增大运行时的负荷。所以,”安全点“的选择基本上是以程序”是否具有让程序长时间执行的特征“为标准来选定的。因为每条执行指令执行的时间都非常地短暂,程序不太可能因为指令流长度太长这个原因而过长时间运行,而程序”长时间的运行“实际上就是指令序列的一个复用。例如方法调用、循环跳转、异常跳转等,所以具有这些功能的指令才会产生”安全点“。而当我们选取好”安全点“之后,我们又是怎样使所有执行线程跑到”安全点“时停顿下来呢?
      • 中断方式
        • 抢先式中断:在GC发生时,首次会把所有的线程全部中断,如果发现有些线程中断点不是安全点,就恢复该线程直到安全点上停止。
        • 主动式中断:在GC发生时,不直接操作线程中断,而是简单地设置一个标志,让各个线程执行时主动轮询这个标志,发现中断标志为真时就自己中断挂起。
      • Safe Region
        • 安全区域是指在一段代码片中,引用关系不会发生改变,在这个区域内的任意地方开始 GC 都是安全的(比如Thread.sleep()的时候)。当线程执行到安全区域时,首先标识自己已进入安全区域,那样,当在这段时间里JVM要发起GC时,就不用管标识自己为“安全区域”状态的线程了,等到被唤醒时准备离开 Safe Region 时,先检查能否离开,如果 GC 完成了,那么线程可以离开,否则它必须等待直到收到安全离开的信号为止。

算法

标记-清除算法(mark-sweep)

  • 标记->在清除阶段,垃圾收集器会从Java堆中从头到尾进行遍历,如果有对象没有被打上标记,那么这个对象就会被清除。
  • 遍历过程效率低。
  • 会产生很多不连续的空间碎片,所以可能会导致程序运行过程中需要分配较大的对象的时候,多触发几次垃圾回收。

复制算法(copying)

  • 复制算法是为了解决标记-清除算法的效率问题的。基本新生代垃圾收集器全部是复制算法。
  • 优点:快,没有碎片。
  • 缺点:浪费内存。
  • 现在的商业虚拟机都采用这种收集算法来回收新生代,新生代中的对象98%是“朝生夕死”的,所以并不需要按照1∶1的比例来划分内存空间,而是将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor。当回收时,将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上,最后清理掉Eden和刚才用过的Survivor空间。HotSpot虚拟机默认Eden和Survivor的大小比例是8∶1,也就是每次新生代中可用内存空间为整个新生代容量的90%,只有10%的内存会被“浪费”。当然,90%的对象可回收只是一般场景下的数据,我们没有办法保证每次回收都只有不多于10%的对象存活,当Survivor空间不够用时(例如,存活的对象需要的空间大于剩余一块Survivor的空间),需要依赖其他内存(这里指老年代)进行分配担保(Handle Promotion)。
    Copying

标记-整理算法(mark-compact)

  • 复制收集算法在对象存活率较高时就要进行较多的复制操作,效率将会变低。所以在老年代不用复制算法,而用标记-整理算法。
  • 标记-让所有的存活对象都向一端移动,然后清理掉边界外的内存。

垃圾收集器

Serial
ParNew
ParOld

| 收集器 | 串并 | 新老 |算法|目标|适用场景|优缺点|JVM参数|推出时间|
| ————- | ——- | ——-|———–|————|————-|————-|
| Serial | 串行 |新生代 |复制算法|响应速度优先|单CPU环境下的Client模式默认收集器Java3前唯一选择|简单高效,单CPU首选|-XX:+UseSerialGC||
| Serial Old | 串行 |老年代 |标记-整理| 响应速度优先| 单CPU环境下的Client模式、CMS的后备预案||默认||
| ParNew | 并行 |新生代 |复制算法 |响应速度优先| 多CPU环境时在Server模式下与CMS配合||-XX:+UseParNewGC||
|Parallel Scavenge| 并行 | 新生代|复制算法 |吞吐量优先 |多CPU,大Heap(>10GB)在后台运算而不需要太多交互的任务|GC自适应的调节策略:Parallel Scavenge收集器有一个参数-XX:+UseAdaptiveSizePolicy。当这个参数打开之后,就不需要手工指定新生代的大小、Eden与Survivor区的比例、晋升老年代对象年龄等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大的吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomics)是和ParNew的最大区别,配合的老年代收集器只有Serial和Parallel Old|-XX:+UseParallelGC XX:MaxGCPauseMills XX:GCTimeRatio(0-100)这俩参数是矛盾的|JDK 1.4.0|
|CMS | 并发 | 老年代|标记-清除| 缩短GC时用户线程的停顿时间| 集中在互联网站或B/S系统服务端上的Java应用|配合的新生代只有ParNew和Serial|-XX:+UseConcMarkSweepGC|JDK1.5|
|Parallel Old | 并行 | 老年代|标记-整理| 吞吐量优先| 在后台运算而不需要太多交互的任务以及CPU资源敏感的场合||-XX:+UseParallelOldGC|JDK 1.6|
|G1 |并发 | both|标记-整理+复制算法 |响应速度优先| 面向服务端应用,将来替换CMS||-XX:+UseG1GC|JDK1.8|

收集器配合

CMS

CMS

  • 基于“标记—清除”算法,分4步:

    1. 初始标记(CMS initial mark):仅仅只是标记一下GC Roots能直接关联到的对象,速度很快,要STW。
    2. 并发标记(CMS concurrent mark):进行GC Roots Tracing的过程。
    3. 重新标记(CMS remark):为了修正并发标记期间因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长一些,但远比并发标记的时间短,要STW。
    4. 并发清除(CMS concurrent sweep)
  • 缺点:

    • CPU个数太少(<4个)的话,CMS收集器占用CPU导致应用程序变慢,总吞吐量会降低
      CMS默认启动的回收线程数是(CPU数量+3)/ 4,也就是当CPU在4个以上时,并发回收时垃圾收集线程不少于25%的CPU资源,并且随着CPU数量的增加而下降。但是当CPU不足4个(譬如2个)时,CMS对用户程序的影响就可能变得很大。
    • CMS收集器无法处理浮动垃圾
      可能出现“Concurrent Mode Failure”失败而导致另一次Full GC的产生。由于CMS并发清理阶段用户线程还在运行着,伴随程序运行自然就还会有新的垃圾不断产生,这一部分垃圾出现在标记过程之后,CMS无法在当次收集中处理掉它们,只好留待下一次GC时再清理掉。这一部分垃圾就称为“浮动垃圾”。也是由于在垃圾收集阶段用户线程还需要运行,那也就还需要预留有足够的内存空间给用户线程使用,因此CMS收集器不能像其他收集器那样等到老年代几乎完全被填满了再进行收集,需要预留一部分空间提供并发收集时的程序运作使用。要是CMS运行期间预留的内存无法满足程序需要,就会出现一次“Concurrent Mode Failure”失败,这时虚拟机将启动后备预案:临时启用Serial Old收集器来重新进行老年代的垃圾收集,这样停顿时间就很长了。
    • 会产生大量空间碎片
  • GC 参数

    • XX:+UseCMSCompactAtFullCollection 是非每次CMS GC以后整理内存
    • XX:CMSFullGCsBeforeCompaction 几次CMS GC以后整理内存
    • XX:+CMSClassUnloadingEnabled 允许Class元数据回收
    • XX:UseCMSInitiatingPermOccupancyFraction 永久区占用率达到多少百分比启动CMS GC
    • XX:UseCMSInitiatingOccupancyOnly 只有达到阈值才进行CMS GC

G1

https://www.oracle.com/technetwork/articles/java/g1gc-1984535.html
G1
Young GC + mixed GC(新生代,再加上部分老生代)+ Full GC for G1 GC算法(应对G1 GC算法某些时候的不赶趟,开销很大);

  • 优点
    • 可预测的停顿
      这是G1相对于CMS的另一大优势。使用G1收集器时,Java堆的内存布局就与其他收集器有很大差别,它将整个Java堆划分为多个大小相等的独立区域(Region),虽然还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分Region(不需要连续)的集合。G1收集器之所以能建立可预测的停顿时间模型,是因为它可以有计划地避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region(这也就是Garbage-First名称的来由)。这种使用Region划分内存空间以及有优先级的区域回收方式,保证了G1收集器在有限的时间内可以获取尽可能高的收集效率。
    • 空间整合
      G1从整体来看是基于“标记—整理”算法实现的收集器,从局部(两个Region之间)上来看是基于“复制”算法实现的,收集后能提供规整的可用内存。
  • 执行过程:
    1. 初始标记(Initial Marking)
      初始标记阶段仅仅只是标记一下GC Roots能直接关联到的对象,并且修改TAMS(Next Top at Mark Start)的值,让下一阶段用户程序并发运行时,能在正确可用的Region中创建新对象,要STW。
    2. 并发标记(Concurrent Marking)
    3. 最终标记(Final Marking)
      最终标记阶段是为了修正在并发标记期间因用户程序继续运作而导致标记产生变动的那一部分标记记录,虚拟机将这段时间对象变化记录在线程Remembered Set Logs里面,最终标记阶段需要把Remembered Set Logs的数据合并到Remembered Set中,这阶段需要停顿线程,但是可并行执行。
    4. 筛选回收(Live Data Counting and Evacuation)
      筛选回收阶段首先对各个Region的回收价值和成本进行排序,根据用户所期望的GC停顿时间来制定回收计划,这个阶段其实也可以做到与用户程序一起并发执行,但是因为只回收一部分Region,时间是用户可控制的,而且停顿用户线程将大幅提高收集效率。

Epsilon:低开销垃圾回收器

  • 启动: -XX:+UseEpsilonGC

SSO server Callstack

  • 在Spring mvc里call Filter Chain, call到org.springframework.security.web.FilterChainProxy.doFilter (extends GenericFilterBean )
    • 这里加装了一个HttpFirewall, 提供firewalledRequest和firewalledResponse,去掉一些危险的请求
  • 然后Filter到熟悉的OncePerRequestFilter,有一个WebAsyncManagerIntegrationFilter继承自它
    • 就是添加了一个SecurityContextCallableProcessingInterceptor,在处理请求的前后包了一层securityContext,preProcess设置它,postProcess clear它
  • SecurityContextPersistenceFilter 强制的session管理
  • HeaderWriterFilter, 写了一些security相关的header
  • CsrfFilter
    • 从csrf token repository里load这个request的csrf token, 如果没有csrf token的话,那就generate一个,存csrf token repository。 跟request里带的csrf token比对,不对的话就拦截。
    • 当然,可以设置不需要csrf保护,RequestMatcher requireCsrfProtectionMatcher来决定要不要
    • example:
      1
      <input type='hidden' name='${_csrf.parameterName}' value='${_csrf.token}' />
  • LogoutFilter
    • Logout是专门用这个来做的
  • AbstractAuthenticationProcessingFilter
    • !核心部分,就是这里验证用户名、密码。
    • call UsernamePasswordAuthenticationFilter
    • call ProviderManager.authenticate
    • call AbstractUserDetailsAuthenticationProvider.authenticate
    • call DaoAuthenticationProvider.retrieveUser
    • call this.getUserDetailsService().loadUserByUsername(username)
      • 这里我们要自己写一个XXXUserDetailsService implements UserDetailsService,实现loadUserByUsername方法就可以。

oauth/confirm_access
前面是一样的,后面又call了:

  • AbstractAuthenticationProcessingFilter
    • 可以设置需不需要authentication保护
    • 用了strategy模式,自己实现sessionSrategy.onAuthentication, 一般来说会检查session,保证不被fix session攻击
  • DefaultLoginPageGeneratingFilter
    • 返回login成功以后的page的
  • RequestCacheAwareFilter
    • 加cache
  • SecurityContextHolderAwareRequestFilter
    • 把request替换掉了, 用来处理servlet2.5和servlet3不同的request
    • 可以设置以下
      • rolePrefix
      • AuthenticationEntryPoint
      • AuthenticationManager
      • LogoutHandler
      • updateFactory
      • TrustResolver
  • AnonymousAuthenticationFilter
    • 检查securityContext有没有authentication,没有就创建
  • SessionManagementFilter
    • 检查authentication,如果有且不是匿名,就触发sessionAuthenticationStrategy.onAuthentication(authentication,request, response)
  • ExceptionTranslationFilter
    • 翻译authentication的exception
  • FilterSecurityInterceptor
  • WsFilter
    • 可忽略,only needs to handle WebSocket upgrade requests
  • 然后就回到了正常spring web的FilterChain,经过doDispatch到Controller这里

通过 session/cookie 维护 authentication

请求 http://127.0.0.1:8060/client2/index.html 需要Cookie:

  • JSESSIONID=90AEBC55CFDCDFD9277A00E654C6A4B4;
  • PGADMIN_LANGUAGE=en;
  • PGADMIN_KEY=13d9a244-76c6-46aa-9c66-e32ab9c9e447;
  • pga4_session=”7e6549b3-743a-4992-9b04-d423caa34fcc!51GhrQLaPDsFNZMsyrY0nD2Lx/g=”

SSO Client

@EnableOAuth2Sso
marks your service as an OAuth 2.0 Client. This means that it will be responsible for redirecting Resource Owner (end user) to the Authorization Server where the user has to enter their credentials. After it’s done the user is redirected back to the Client with Authorization Code (don’t confuse with Access Code). Then the Client takes the Authorization Code and exchanges it for an Access Token by calling Authorization Server. Only after that, the Client can make a call to a Resource Server with Access Token.

Also, if you take a look into the source code of @EnableOAuth2Sso annotation you will see two interesting things:

@EnableOAuth2Client. This is where your service becomes OAuth 2.0 Client. It makes it possible to forward access token (after it has been exchanged for Authorization Code) to downstream services in case you are calling those services via OAuth2RestTemplate.

@EnableConfigurationProperties(OAuth2SsoProperties.class). OAuth2SsoProperties has only one property String loginPath which is /login by default. This will intercept browser requests to the /login by OAuth2ClientAuthenticationProcessingFilter and will redirect the user to the Authorization Server.

application.properties
security.oauth2.client.client-id=client2
security.oauth2.client.client-secret=client2
security.oauth2.client.user-authorization-uri=http://127.0.0.1:9999/server/oauth/authorize
security.oauth2.client.access-token-uri=http://127.0.0.1:9999/server/oauth/token
security.oauth2.resource.jwt.key-uri=http://127.0.0.1:9999/server/oauth/token_key

请求

  • client2/index.html
    • Cookie: JSESSIONID=F9D6E6701C3446256BBDD3536B4C37A2
    • response: 302
  • client2/login
    • Cookie: JSESSIONID=F9D6E6701C3446256BBDD3536B4C37A2
    • response: 302
  • server/oauth/authorize?client_id=client2&redirect_uri=http://127.0.0.1:8060/client2/login&response_type=code&state=FDAZ6f
    • Cookie: JSESSIONID=3E6CBEFE193913C53AC3AAA1C7687C89
    • response: 302
  • server/login
    • Cookie: JSESSIONID=3E6CBEFE193913C53AC3AAA1C7687C89
    • response: 200 + login 页面

@EnableResourceServer

principles/checklist for refactor

  • small
  • safe
  • improve
    • readable
      • meet with human god feelings
      • naming correctly
    • testable

good practice

  • don’t modify param
  • don’t mass up input and output
    • e.g.
      1
      2
      3
      4
      5
      6
      Param someMethod(Param p){
      if(p.getA()){
      p.setB("a")
      }
      return p;
      }
  • one responsibility for one method
  • pass only the parameters needed by method
  • keep smallest visibility
  • use design pattern correctly
  • UT
    • can run locally
    • fast
    • as doc to tell users how to use the method

case study

refactor long refreshUserGroup method

Problem

  • do update and conditional dispatch in one method
  • modify parameter
  • parse whole cmd but use part of the params
  • no UT, just using testNG(integration test ~20 cases)
    • need to run tomcat to run cases
    • long time to run cases(~7 minutes)
    • dependency between cases(must run in a certain order)
    • dependency on db initial data
  • no pipeline
  • code too large
    • out of memory when running locally
    • long time to compile(~7 minutes)
  • after dispached in one case, outside of that class, actually modify the param and call itself for update again
  • misuse of parameter, eg isrefreshAll actually int, set to _99 to mean another case
  • cmd execute is common way to implement a service, actually naming is bad, bacause not preventing any information…

Actions

  • separate update logic and dispatch logic
  • separate input with output
  • add UT
  • only pass params that used to methods

refactor 30000 line code UserDAO

Problem

  • too large
  • own by multiple teams
  • user specific logics and application specific logics together
  • referenced by other modules by jar dependency

Actions

  • remove unused methods
  • move some to new user specific logic UserDAO and some to application DAO
  • make original one a delegate to new ones
  • add UT
  • rely on interfaces between modules,interface be small
  • adapters in caller,学习型测试

UT for singlton RuleEngine

Problem

  • different tests for different scenarios
  • misuse of singleton
  • class name XXFactory, actually not, it is a singleton and not producing XX Bean

Actions

  • modify UT to add mock for different scenarios

UT for smart controller

Problem

  • too much logic done by smart widgets, not easy to test

Actions

  • verify if update methods called
  • some logic move to service level to reuse code
  • only test code written by you