JVM基础
欢迎参观我的博客,一个Vue 与 SpringBoot结合的产物:https://poetize.cn
- 博客:https://gitee.com/littledokey/poetize-vue2.git
- 聊天室:https://gitee.com/littledokey/poetize-im-vue3.git
- 后端:https://gitee.com/littledokey/poetize.git
- 七牛云登录/注册地址(文件服务器,CDN):https://s.qiniu.com/Mz6Z32
原文链接:https://poetize.cn/article?id=7
Java堆
从回收内存的角度看
- Java 堆,由年轻代和年老代组成,分别占据 1/3 和 2/3。
- 而年轻代又分为三部分,Eden、From Survivor、To Survivor,占据比例为 8:1:1,可调。
对象的创建
- 类加载
- 内存分配:1,指针碰撞(复制算法/标记-整理算法);2,空闲列表(标记-清除算法)
- 初始化零值
- 设置对象头
- init方法
类加载过程
- 加载:通过全限定类名加载类型数据到元空间,并在堆中生成一个代表这个类的Class对象,作为访问类型数据的外部接口。
- 验证
- 准备:给静态变量分配内存并设置类变量初始值的阶段。
- 解析符号引用转为直接引用
- 初始化:为静态变量赋代码编写的值
对象的回收
可达性算法
- 静态变量,常量
- 执行上下文(方法栈帧中局部对象引用)
垃圾收集器
新生代
- Serial:复制,单线程,STW
- ParNew:复制:多线程,STW
- Parallel Scavenge:复制,多线程,STW,
吞吐量优先收集器
- G1
老年代
- Serial Old:标记整理,单线程,STW
- Parallel Old:标记整理,多线程,STW,吞吐量优先收集器
- CMS:标记清除,
并发
,低停顿
,内存碎片较多
,依赖CPU资源
,浮动垃圾
- 初始标记:单线程,STW
- 并发标记
- 重新标记:多线程,STW
- 并发清除
- G1
CMS与G1
CMS会导致碎片化,大对象会频繁触发FullGC,FullGC意味着完全的STW,应用线程彻底停止,直到完成对堆的清理。堆越大FullGC停顿的时间就越长。
CMS垃圾回收器不能满足性能需求,而G1相比CMS有更清晰的优势:
-
CMS没有采用复制算法,所以它不能压缩,最终导致内存碎片化问题。而G1采用了复制算法,它通过把对象从若干个Region拷贝到新的Region过程中,执行了压缩处理。
-
CMS必须扫描整个堆来确认存活对象,所以,长时间停顿是非常常见的。而G1的停顿时间取决于收集的Region集合数量,而不是整个堆的大小,所以相比起CMS,长时间停顿要少很多,可控很多。
选择合适的垃圾回收器
- CPU单核,那么毫无疑问Serial 垃圾收集器是你唯一的选择。
- CPU多核,关注吞吐量 ,那么选择PS+PO组合。
- CPU多核,关注用户停顿时间,JDK版本1.6或者1.7,那么选择CMS。
- CPU多核,关注用户停顿时间,JDK1.8及以上,JVM可用内存6G以上,那么选择G1。
JVM 调优
1. 网站流量暴增后反应很慢
- 推测:垃圾收集导致业务线程停顿
- 定位:
jstat -gc
发现GC频率过高 - 解决:加大内存
- 第二个问题:加大内存后单次卡顿变长
- 解决:JDK1.8默认垃圾收集器
Parallel Scavenge + Parallel Old
,增大内存后停顿时间就变长了。可以使用CMS,并设置了一个预期的停顿时间。
2. OOM
-XX:+HeapDumpOnOutOfMemoryError参数获得堆内存的dump文件。
3. 内存飙高
1、先观察垃圾回收的情况
jstat -gc PID 1000
查看GC次数,时间等信息,每隔一秒打印一次。jmap -histo PID | head -20
查看堆内存占用空间最大的前20个对象类型,可初步查看是哪个对象占用了内存。
如果每次GC次数频繁,而且每次回收的内存空间也正常,那说明是因为对象创建速度快导致内存一直占用很高;
如果每次回收的内存非常少,那么很可能是因为内存泄露导致内存一直无法被回收。
2、导出堆内存文件快照
jmap -dump:live,format=b,file=/home/myheapdump.hprof PID
dump堆内存信息到文件。
4. 频繁Full GC
通过jstat
命令发现系统每次Young GC
后大约有10%的存活对象进入老年代。
原来是因为Survivor区空间设置过小,每次Young GC后存活对象在Survivor区域放不下,提前进入老年代。
通过调大Survivor区,使得Survivor区可以容纳Young GC后存活对象,对象在Survivor区经历多次Young GC达到年龄阈值才进入老年代。