java性能优化几点总结

361 阅读2分钟

1.性能调优的标准

响应时间,吞吐量,cpu/内存/io使用率
tps,qps

2.制定优化策略

性能测试,压力测试,接口测试,环境测试
优化代码,优化设计,优化算法,时间换空间,空间换时间,优化参数等
兜底策略:限流熔断,扩容

3.io优化

传统io->nio:nio使用buffer
用户-系统内核-硬件,需要经过两次数据copy。
使用derectbuffer优化用户数据copy,生成一个jvm堆外的物理内存。
多路复用避免阻塞,减少上下文切换,selector(epoll),channel
mapperedbyteBuffer直接将硬件数据copy到用户

4.锁优化

4.1 synchronized

synchronized是基本底层操作系统的metux lock实现的,metuxenter,metuxexit。每次获取锁和释放锁都会导致用户态和内核态的切换,上下文切换。
竞争锁失败的线程会放入contentionlist里,monitor对象管控jvm的同步,每个对象都有一个monitor对象。
无锁(判断markword)->偏向锁->偏向锁的撤销(需要保存安全点,没有执行才能撤销,否则升级锁)->轻量级锁-》自旋锁->重量级锁(通过对象头的标记来判断)
偏向锁:同一个线程再次申请锁时候优先使用,锁竞争少的时候使用。
轻量级锁:cas,适用于线程交替执行的场景
自旋锁:自旋锁获取锁
重量级锁:完全独占,所有都同步等待
锁的优化:jit编译的时候对锁消除(通过逃逸分析,发现这个资源只会一个线程使用,就去掉synchronized)
和锁粗化(几段代码使用的是同一个锁,就将这几段代码合并,使用一个锁,减少了锁的申请和释放)。
减少锁的力度,锁住必须锁住的地方,建大对象拆成多个小的,分开获取锁,如老版concurrenthashmap的segment

4.2 lock

lock是显示的获取和释放锁,基于java实现的。有ReentrantLock, ReentrantReadWriteLock(读写分离的锁,读很多写很少会写线程饥饿),基于AQS,
使用state标记是否获取锁,state非0就要去竞争锁,读写分离是基于state的高低位实现的,低16位控制写。获取锁基于cas
还有StampedLock(更优良的读写分离锁,不会一直cas,读很多写很少也不会写线程饥饿);

4.3 乐观锁

实现基于cas,需要更新的值V,预期的值E,更新的值N,当E=V是才更新为N。乐观锁减少了锁竞争,减少了上下文切换。
atomic类都是基于cas实现的,unsafe来实现原子操作。
cas的弊端就是失败会自旋,很耗cpu。LongAddr分散压力,性能更高。
在普通的场景下,读写分离的锁表现的性能最好,synchronized性能比较差。