1. 内存溢出
内存溢出的方式:
- 栈溢出
- 堆溢出
- 方法区溢出
- 本级内存直接溢出
1.1 栈溢出
StackOverflowError:
方法自己调用自己,死递归导致的溢出。
OutOfMemoryError:
栈内存分配有限,每个线程占据一个内存空间,比如 1M,如果线程多了,会导致内存溢出。
1.2 堆溢出
1.3 方法区溢出
方法区垃圾回收效率很低,因此在加载动态语言、CGLIB、JSP、OSGI 时可能会内存溢出。
1.4 本地直接内存溢出
限制了本地直接内存的大小,分配时超过了其大小。
2. 内存泄露
内存溢出是程序申请内存时,没有足够的内存空间。
内存泄露是程序在申请内存后,无法释放已申请的内存空间。就是本意是无用的对象,但是没有释放,垃圾回收器无法回收内存空间。
内存泄漏的几种原因:
- 长生命周期的对象持有短生命周期对象的引用
- 连接未关闭
- 变量作用域不合理
- 内部类持有外部类
- Hash值改变
3. MAT
-XX:+HeapDumpOnOutOfMemoryError
深堆(Shallow Heap):分配一个对象所消耗的内存。
浅堆(Retained Heap):分配一个对象,在垃圾回收时真实可回收的内存大小。
Incoming:这个对象引用了谁。
Outgoing:谁引用了这个对象。
4. JDK 为我们提供的工具
jps -mlv
jstat -gc <pid>
jinfo -flags <pid>、jinfo -flag +PrintGCDetails 操作参数
可视化工具在使用时需要在远端程序中添加:
-Djava.rmi.server.hostname=…… -Dcom.sun.management.jmxremote
-Dcom.sun.management.jmxremote.port=8888
-Dcom.sun.management.jmxremote.authenticate=false
-Dcom.sun.management.jmxremote.ssl=false
5. JVM 调优
JVM 调优的本质是调整垃圾回收的频次,提升系统的稳定性,而不是提升性能。
影响一个系统性能的方方面面:
5.1 调优原则
- 大多数的 java 应用不需要 GC 调优。
- 大部分需要 GC 调优的,不是参数问题,而是代码问题。
- 在实际项目中,分析 GC 情况优化代码比调整 GC 参数要重要。
- GC 调优是最后的手段。
5.2 GC 调优目的
- GC 的时间足够小
参考性指标:Minor GC 执行时间不到 50ms,Full GC 执行时间不到 1s。 - GC 的次数足够小
参考性指标:Minor GC 执行不频繁,大于 10s 一次,Full GC 执行频率不算频繁,大于10min 1次。
5.3 调优步骤
- 监视 GC 状况
- 分析结果,判断是否需要优化
- 调整垃圾回收器类型、内存分配参数
- 不断分析、调整
- 全面应用参数
阅读 GC 日志:主要关注 Minor GC、Full GC 的回收效率(回收前大小、回收比较)、回收时间。
5.4 调优实战
- 项目启动 GC 优化
启动后观察 Minor GC、Full GC 次数,优化参数达到调优目的。 - 项目运行 GC 优化
切换不同垃圾回收器,JMeter 压测查看结果。
5.5 推荐策略
- 新生代大小选择
响应时间优先的应用:尽可能设大,直到接近系统的最低响应时间限制,在此种情况下,新生代收集发生的频率也是最小
的,同时减少到达老年代的对象。
吞吐量优先的应用:尽可能设大,可能到达 Gbit 的程度,因为对响应时间没有要求,垃圾收集可以并行进行,一般适合 8CPU 以上的应用。
避免设置过小,当新生代设置过小时会导致:1.MinorGC 次数更加频繁;2.可能导致 MinorGC 对象直接进入老年代,如果此时老年代满了,会触发 FullGC。 - 老年代大小选择
响应时间优先的应用:老年代使用并发收集器,所以其大小需要小心设置,一般要考虑并发会话率和会话持续时间等一些参数:如果堆设置小了,可以会造成内存碎片,高回收频率以及应用暂停而使用传统的标记清除方式;如果堆大了,则需要较长的收集时间。最优化的方案,一般需要参考以下数据获得:并发垃圾收集信息、持久代并发收集次数、传统 GC 信息、花在新生代和老年代回收上的时间比例。
6. 常用的性能评价指标
- 响应时间:一般关注平均响应时间
- 并发数
同一时刻,对服务器有实际交互的请求数。 - 吞吐量
对单位时间内完成的工作量(请求)的量度。如:每分钟的数据库事务,每秒传送的文件千字节数,每分钟的 Web 服务器命中数。
7. 常用的性能优化手段
总原则:
- 避免过早优化
- 进行系统性能测试
- 寻找系统瓶颈,分而治之,逐步优化
优化分为:
- 前端优化
- 应用服务性能优化
- 存储性能优化
7.1 前端优化常用手段
- 浏览器/App:减少请求数;使用客户端缓冲;启用压缩;资源文件加载顺序;减少Cookie传输
- CDN加速
- 反向代理缓存
- WEB组件分离
7.2 应用服务性能优化手段
-
缓存
网站性能优化第一定律:优先考虑使用缓存优化性能。
缓存优化原则:缓存离用户越近越好。
缓存的本质:数据读多改少的情况,将数据访问放到高效介质中。 -
集群
-
异步
常见异步手段:Servlet 异步、多线程、消息队列。 -
程序
代码级别、并发编程、算法、资源的复用(减少开销很大的系统资源的创建和销毁:单例模式、池化技术)、JVM。
7.3 存储性能优化手段
- 尽量使用SSD
- 定时清理数据或者按数据的性质分开存放
- 结果集处理
8. 虚拟机优化技术
栈上分配:对逃逸分析出来的对象有可能进行栈上分配。
牵涉到的 JVM 参数:
- -XX:+DoEscapeAnalysis:启用逃逸分析(默认打开)
- -XX:+EliminateAllocations:标量替换(默认打开)
- -XX:+UseTLAB:本地线程分配缓冲(默认打开)