目录
基准测试和JIT
GC相关
堆内存
原生内存
线程
JavaEE相关
Java SE 相关
即便是单线程的基准测试用例,变量也要申明成 volatile类型的,这样可以防止编译器做优化
要加入一定的热身期
监控命令
jcmd 打印java进程所涉及的基本类,线程和VM信息
jconsole
jhat,读取内存堆转储,并有助于分析
jmap,提供堆转储和其他JVM内存使用的信息
jinfo,查看JVM的系统属性,可以动态设置一些系统属性
jstack
jstat
jvisualvm
比如
jcmd [process_id] VM.system_properties
或者 jinfo -sysprops [process_id]
jcmd [process_id] VM.version
jcmd [process_id] VM.command_line
jcmd [process_id] VM.flags [-all]
打印所有的调优标志
java other_options -XX:+PrintFlagsFinal -version
jstack [process_id]
或者 jcmd [process_id] Thread.print
关闭某个属性
jinfo -flag -PrintGCDetails [process_id]
可以在运行期间设置属性开启/关闭,但不是所有属性都支持热关闭/开启的
Java Mission Control 商业监控工具,其关键特性是java飞行记录器(Java Flight Recorder)
GC输出
-verbose:gc
-XX:+PrintGC
-XX:+PrintGCDetails
-XX:+PrintGCTimestamps
-XX:+PrintGCDateStamps
-Xloggc:filename
-XX:+UseGCLogfileRotation
-XX:+NumberOfGCLogfiles
Througput GC算法是一种空间和时间的权衡
开始时可以用默认的自适应方式
如果把暂停时间设置的太短,就会触发更频繁的GC,会导致吞吐量下降
如果把GC比率设置太低,也会导致堆空间变小,更可能会频繁触发GC
有两个相关的参数
-XX:MaxGCPauseMillis=N
-XX:GCTimeRatio=N
Through收集器
分为Minor GC,Full GC
暂停时间越短GC就会越多,GC次数减少意味着时间增大堆空间增加
对through调优是一种取舍
CM收集器
初始标记,并发标记,预清理,重新标记,并发清理,(清理失败)
增量式CMS垃圾收集器,在单CPU上会增加CPU负担,增量收集采用间歇性的方式收集,1.8之后被废弃
G1收集器
初始标记,并发标记,预清理,重新标记,并发标记
分成2048个region
新生代+老年代 混合收集模式
并发模式失败(类似CMS),疏散失败(Srvivor和老年代没有空间容纳幸存对象)
调整后台收集线程数
调整垃圾收集器运行频率(默认占到45%时回收)-XX:InitiatingHeapOccupancyPercent=N
调整混合垃圾收集周期
分区默认为35%时启动收集 -XX:G1MixedGCLiveThresholdPercent
最大混合式GC周期数,可解决晋升失败问题,但会增加GC周期停顿时间,-XX:G1MixedGCCountTarget=N
最大停顿时间 -XX:MaxGCPauseMillis
晋升调整
-XX:MinSurvivorRatio=N
-XX:InitialTenuringThreshold=N
-XX:TargetSurvivorRatio
-XX:+AlwaysTenure
-XX:+NeverTenure
分配大页
-XX:+UseTLAB
-XX:TLABWasteTargetPercent=N
-XX:TLABWasteIncrement=N
-XX:G1HeapRegionSize=N
内存溢出错误
1.原生内存不足
Exception in thread "main" java.lang.OutOfMemoryError
unable to create new native thread
2.永久代或元空间内存不足
Exception in thread "main" java.lang.OutOfMemroyError: PermGen space
3.堆内存不足
Exception in thread "main" java.lang.OutOfMemoryError: java heap space
4.达到GC的开销限制
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
-XX:+UseGCOverheadLimit 如果超过了这个设定值,比如默认98%的时间用在GC上
-XX:+GCHeapFreeLimit 如果释放的内存少于这个指标,默认是2%,释放的内存不足堆的2%
减少内存使用
减少对象大小
延迟对象初始化
不可变对象
字符串保留 intern()
对象重用
1.线程池
2.JDBC池
3.EJB池
4.大数组
5.原生NIO缓冲区
6.安全相关类
7.字符串编解码器对象
8.StringBuilder协助者
9.从NDS查询到的名字
10.ZIP编解码器
弱引用,软引用,幻引用
Finalizer
内存占用最小化
1.堆
2.线程栈
3.代码缓存
4.直接字节缓冲区
原生内存跟踪
jcmd [process_id] VM.native_memory summary
-XX:NativeMemoryTracking=off|summary|detail
原生NIO缓存
-XX:MaxDirectMemorySize
大页
+XX:+UseLargePages
+XX:LargePageSizeInBytes
指针压缩
+XX:+UseCompressedOops
jcmd 11674 VM.native_memory summary
11674:
Native Memory Tracking:
Total: reserved=1573497KB, committed=36193KB
- Java Heap (reserved=253952KB, committed=16384KB)
(mmap: reserved=253952KB, committed=16384KB)
- Class (reserved=1056875KB, committed=4971KB)
(classes #389)
(malloc=107KB #122)
(mmap: reserved=1056768KB, committed=4864KB)
- Thread (reserved=10304KB, committed=10304KB)
(thread #11)
(stack: reserved=10260KB, committed=10260KB)
(malloc=33KB #52)
(arena=12KB #20)
- Code (reserved=249631KB, committed=2567KB)
(malloc=31KB #295)
(mmap: reserved=249600KB, committed=2536KB)
- GC (reserved=839KB, committed=71KB)
(malloc=7KB #79)
(mmap: reserved=832KB, committed=64KB)
- Compiler (reserved=132KB, committed=132KB)
(malloc=1KB #22)
(arena=131KB #3)
- Internal (reserved=203KB, committed=203KB)
(malloc=171KB #1206)
(mmap: reserved=32KB, committed=32KB)
- Symbol (reserved=1356KB, committed=1356KB)
(malloc=900KB #64)
(arena=456KB #1)
- Native Memory Tracking (reserved=32KB, committed=32KB)
(malloc=2KB #29)
(tracking overhead=30KB)
- Arena Chunk (reserved=171KB, committed=171KB)
(malloc=171KB)
线程池ThreadPoolExecutor设置
1.最小任务数
2.最大任务数
3.任务队列,有界队列,无界队列
ForkJoinPool
使用map-reduce原理
如果整个待执行的任务是均衡的用ThreadPoolExecutor更好
否则用ForkJoinPool更好
窃取双端队列
同步的方式
volatile
CAS
原生同步
竞争不大用CAS,竞争比较多用原生同步
Amdahl定律
volatile导致的伪共享会让性能急剧下降
偏向所,优先处理访问频繁的线程
自旋锁,JDK8废弃
减少输出,包括减少空格
合并CSS和JavaScript
压缩输出
不要使用JSP动态编译
减少会话中保留的数据,减少会话时间
servlet可能有专门的线程池,EJB也有专门的线程池,同样JMS也是
EJB bean的执行过程
1.创建EJB
2.处理注解并且将依赖的资源注入这个新的EJB对象
3.调用注解为@PostConstruct的函数
4.如果是有状态bean,调用注解为@Init的函数或者ejbCreate()函数
5.执行业务方法
6.调用任何注解为@PreRemove的函数
7.如果是有状态bean,则调用remove()函数
EJB池化后,只需要执行业务方法,其他初始化销毁的都可以不做
有状态bean的钝化(Passivation),尽量避免这么做
及时在一个服务器中,本地接口也比远程接口快,因为少了序列化/反序列化步骤
所有远程EJB都必须支持IIOP(CORBA)协议
Web Service使用JAX-WS
RESTful使用JAX-RS
XML和Json的解析方式,XML的验证会影响很大性能
创建JAXB对象比创建DOM对象昂贵,但JAXB写XML速度要快于DOM对象
Serializable标记接口
Externalizable自定义序列化接口(包含readExternal和writeExternal)
序 号 |
区 别 |
Serializable |
Externalizable |
1 |
实现复杂度 |
实现简单,Java对其 有内建支持 |
实现复杂, 由开发人员自己完成 |
2 |
执行效率 |
所有对象由Java统一保存, 性能较低 |
开发人员决定哪个对象保存, 可能造成速度提升 |
3 |
保存信息 |
保存时占用空间大 |
部分存储, 可能造成空间减少 |
在一般的开发中,因为Serializable接口的使用较为方便,所以出现较多
JDBC驱动程序类型1-4,四种
PrepareStatment可以重用sql语句,比Statement性能更好
预处理语句必须依靠单个连接进行池化,而预处理语句会消耗大量堆空间
数据库查询返回的取舍,频繁的调用数据库每次取一小部分,或者一次取很多但会造成GC
Container Mnaged Transactions CMT 使用容器管理事务
User Managed Transaction UMT 使用用户管理事务
XA事务
减少写入的字段,不必要跟新的不写
延迟载入大型字段
不太容易冲突的使用乐观锁,经常发生冲突的使用悲观锁
加入JPA缓存
I/O缓冲区,socket I/O也需要缓冲
ClassLoader的双亲委派模型,多线程创建类时候会有影响性能,1.7之后改为并行模式性能提升
ClassLoader使用loadClass()寻找类,如果找不到调用findClass(),我们自己的类加载只需实现findClass即可
-XX:+AlwaysLockClassLoader 还原为JDK6的持有锁的类加载模式
Random 线程安全的
ThreadLocalRandom 实现方式类似Random,一种伪随机数
SecureRandom,获得了更多的随机数实现确保更安全,如果无法获取更多随机数可能会等待好几秒
现在的JNI未必比Java代码更快了,若一定要使用应限制Java到C的调用次数,这会影响性能
现在的JVM处理异常速度也很快了,但获取异常栈信息(尤其栈很深)会损耗性能
防御性代码比抛出异常更好
可以用 -XX:-StrackTraceInThrowable 禁止生成异常栈信息(不推荐)
String str = "aa"+"bb"+"cc" 编译器会做优化
str += "aa";
str += "bb";
会变成
str = new StringBuilder(str).append("aa");
str = new StringBuilder(str).append("bb");
Java集合类
线程安全的集合
设定集合的大小,IO类中的ByteArrayOutuptStream也是类似的
ConcurrentHashMap
AggressiveIpts 会在啊一些基本的类中开启某些优化,导致一些副作用
AutoBoxCacheMax 自己装箱cache
lambda性能并不差,可能比匿名类性能更好
应该从方便编程的角度出发,因为性能上没什么差别
list.stream().filter(s->s.charAt(0)!='A').(s->s.charAt(1)!='A').(s->s.charAt(2)!='A').(s->s.charAt(3)!='A')
过滤器会先判断最后一个
(s->s.charAt(3)!='A')
拿到数据之后交给倒数第二个,然后倒数第三个。。。
过滤器的性能比迭代器好,因为能少处理很多数据
但多个过滤器会有开销,实现时需要注意
G1垃圾收集器入门-原创译文
详解 JVM Garbage First(G1) 垃圾收集器