这里写目录标题
1. GarbageCollect(垃圾回收)
内存泄漏:不能被回收的无用对象,eg,存在循环引用的对象。
内存泄漏的堆积会产生内存溢出问题。
引用计数法:
可达性分析算法/根搜索算法:
可达性:
回收哪:堆
判定我回收的数据对象的回收
java中对象引用—对象
对象身上是否有引用引用计数为0的情况下
引用计数法执行的效率非常高难以解决对象的循环引用问题
内存泄漏
的堆积会产生内存溢出问题
算法
可达性分析算法/根搜索算法
可达性 GC root 引出一条引用链 引用链上的对象便是存活对象
JNI
局部变量表中的元素
静态变量
常量
JNI
相当于一组活跃的引用
什么时候进行回收
1.Eden满了之后 S区满了之后 young GC
2.老年代空间不够了OldGC
3.方法区空间不够 Meta Space GC
4.System.gc() I //通知进行一次垃圾回收 full GC 消费非常的大
2.垃圾收集算法
标记清除算法
只有两个过程
- 标记:通过GC引用链遍历,进行标记
- 清除: 把剩下的未标记的进行清除
缺点:垃圾回收线程和业务线程并行会导致 不该回收的回收掉了,该回收的没有回收掉。
1.会导致STW
2.会导致内存碎片
3.效率慢
4.jvm还需要维护一个空闲的内存列表
STW(STOP THE WORLD):进行垃圾回收时,停止业务线程的所有事件
标记复制算法
将内存划分为两块相等的区域,每次只使用其中一块。
每次只对半个内存进行垃圾回收。
以空间去换时间。
肯定不能用在整个内存空间,
标记整理算法
标记–清除–整理
效率小于或者等于标记清楚算法的
现代的整理算法: 随机整理,线性整理,滑动整理。
滑动整理算法
红色:存活对象
灰色:垃圾对象
派出两个指针, 一个找到非存活 的区域,另一个找到存活区域。
一直滑动判断(满足条件停下,不满足条件继续走,直到都满足条件),若都满足条件,则进行交换。
两个指针指向相同区域,清除垃圾区域,
分代收集算法
新生代: 复制算法(对象在被分配之后,可能生命周期比较短,Young区复制效率比较高)
老年代: 标记清除或标记整理(Old区对象存活时间比较成,复制来复制去没必要,不如做个标记再清理)
3.垃圾收集器
1. Serial(新生代)
jdk1.3.1之前
单线程,复制算法,(不会有STW)
很高的单线程收集效率
1. Serial Old(老年代)
单线程,标记整理算法
完善 STW时间长 单线程–多线程
2. parNew
多个垃圾收集线程
STW 多线程缩短停顿时间的情况下 完全的去掉停顿时间 代价非常大 用户没有感知
关注 吞吐量 系统一天能够去干的事情 能不能控制
吞吐量=业务代码时间/(业务代码时间+垃圾收集时间)99/100 = 99%
3. Parallel Scavenge
Parallel Scavenge 多线程
4. Parallel Old
Parallel Old 老年代 标记整理算法
两个概念1. 吞吐量和 2. 停顿时间
调优策略
交互式应用
保证停顿时间300ms情况下尽可能 增加吞吐
如果是常规应用需要在保证最大吞吐量的情况下尽可能减小停顿时间
一般情况下 项目吞吐量要在 95%以上
GCview能看到吞吐量。
5. CMS标记清除算法
垃圾收集线程+业务线程 我应该保证不出现(不该回收的回收了,该回收的没回收)问题 尽可能的减少耗时操作
将耗时操作去进行 垃圾收集线程+业务线程 同时跑
并发类垃圾收集器
CMS标记清除算法:目标是缩短停顿时间。不关注内存碎片,所以使用标记清除算法。
标记清除算法 分治算法
标记
初始标记1.找到所有的GCroot以及与之直接相关联的对象 不耗时 STW
并发标记2.找到引用链上的所有对象并且进行标记 耗时 并发垃圾收集线程+业务线程
重新标记3.增量的标记那些依赖关系发生了变化的对象 不耗时 STW
4.并发清除 耗时 并发垃圾收集线程+业务线程
6. G1(garbage first)
和CMS一样都是并发类垃圾收集器
比CMS好的地方:
- 我能有更短的垃圾收集时间,想多短就多短,可以自己设置(太小会导致并发失败, 会进入负GC)
- 我在某种程度上可以解决空间碎片(依赖于内存空间的重定义)2048个region的区域。 每个区域1M-32M。跑起来最少2个G
会选价值最高的垃圾进行回收
如何进行选取?
串行收集器->Serial和Serial Old
只能有一个垃圾回收线程执行,用户线程暂停。
适用于内存比较小的嵌入式设备 。
并行收集器[吞吐量优先]->Parallel Scanvenge、Parallel Old
多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。
适用于科学计算、后台处理等若交互场景 。
并发收集器[停顿时间优先]->CMS、G1
用户线程和垃圾收集线程同时执行(但并不一定是并行的,可能是交替执行的),垃圾收集线程在执行的时候不会停顿用户线程的运行。
适用于相对时间有要求的场景,比如Web 。
4.性能优化
传统项目不需要进行JVM优化。
分布式微服务 秒杀促销 需要JVM优化
有并发才会进行优化
手段: 限流,动静分离,线程池,负载均衡,mysql性能优化,缓存,队列。
1. 代码层面
封装,继承,多态。
2. 非代码层面
内存泄漏
OOM :内存不够用了
GC频繁:
CPU飙升
业务场景
要考虑峰值,
4核8G 堆内存分几个G? 4~6个G(不仅仅有项目,操作系统几百兆,元空间几百兆,栈帧指标数据localStart也会占空间,6G是极限值)
就分4个G
新生代:1333M
老年代:2667M
下订单会产生对象,Order对象,一个对象 300字节~1K 之间
Order对象 1K*20 = 20k (Order对象 根据业务跑下来可能会产生20个对象, 订单-》商品-》明细 -》下单)
一台服务器处理1200笔订单,(多200笔多一点容错)
1200 * 20k = 24M
这些对象会产生在 堆内存中的新生代区域中(一般情况下,新创建的对象都会被分配到Eden区,一些特殊的大的对象会直接分配到Old区) ,可用空间为1333*0.8=1066.4
1066.4 / 24 = 44.44S
会抗住44s,过后会进行youngGC,然后STW,但是我们会引起对象的晋升,有可能会引起full GC
优化方案
- 调整堆内存大小, 4G–6G
- 更换新老年代比例 3000-3000 2400/24=100S
G1不适用于这个案例的,因为
出现oom优先要排查
cpu飙升,没有报oom,优先考虑死锁,
其次内存泄漏
老年代 86%