3.1 JVM 基础 3.2 JVM 内存布局及三色标记算法 3.3 JVM 调优
3.1 JVM 基础
基础主要简单过一下,不了解的百度搜很多
Java理解
平台无关性-- javac编译 javap反汇编,一次编译到处运行
GC垃圾回收
语言特性--泛型,反射等
面向对象--封装,继承,多态
类库--Java本身集合,IO等
异常处理
异常处理
Throwable -> error 和 exception
exception -> runtimeException 和 非runtimeException
error 是JVM负责
runtimeException 是程序负责
checkedException 也就是非runtime是 Java编译器负责
类加载过程
作者:柯西王子
链接:https://www.nowcoder.com/discuss/401895?type=2
来源:牛客网
类加载器的本质其实就是通过文件操作扫描到应用classpath下的Jar包,然后读取
Jar包里的class文件,经过解析,校验等等一堆乱七八糟的操作之后,再将文件里
的字节码内容加载到内存里,供后面类实例化为对象使用,实例化对象的时候会根
据已加载的类信息去做分配内存,初始化成员变量等等一些工作
JVM架构(稍微看看即可)
ClassLoader 根据特定格式,加载class文件到内存
Runtime Data Area 是JVM内存空间模型
Execution Engine 解析命令到操作系统
Native Interface 不同语言的原生库为Java使用
反射
简单略
双亲委派机制
BootStrapClassLoader C++编写加载核心库
ExtClassLoader: Java编写,加载扩展库
AppClassLoader:Java编写,加载程序所在目录
自定义ClassLoader:Java编写,定制化加载
定义流程--略
Why use--避免重复加载
How 自定义? extend ClassLoader
-> 重写findClass 不打破双亲
-> 重写loadClass 打破双亲
Java内存模型
线程私有的--程序计数器,虚拟机栈,本地方法栈
线程共有的--MetaSpace,堆(常量池和堆)
各自定义,元空间方法区,Intern JDK6改动等等太基础略
GC
引用计数法
可作为GC root对象:虚拟机栈,方法区,活跃线程,本地方法栈中各种对象等
标记清除算法
标记整理算法
复制算法
年轻代
老年代
触发full GC条件
常用垃圾收集器
stop-the-world
safepoint
强引用,软引用,弱引用,虚引用,引用队列
CMS 初始标记,并发标记,重新标记,并发清除及原理(性能不咋地,一直被嫌弃)
G1 堆内存划分,维护优先级,会清除垃圾最多的
太基础略
JMM内存模型
happen-before原则等等,蛮重要的略
3.2 JVM 内存布局及三色标记算法
对象在内存中布局
普通对象
对象头(8字节)
类型指针(4字节)
实例数据(根据具体数据判断,一个int4字节,一个long8字节推类)
对齐 (当整个字节不能被8整除时,则填冲到被8整除)
数组对象
对象头(8字节)
类型指针(4字节)
数组长度
对齐 (当整个字节不能被8整除时,则填冲到被8整除)
一般Java 类型指针64位,但是JVM默认开启compressedPointer会压缩到4字节。compressedOops也会压缩普通对象字节,如String默认是8字节,但是String会被压缩到4字节
例题
User{
int id;
String name;
}
整个类占多少字节?
对象头8字节,类型指针4字节,int4字节,string4字节,对齐4字节(因为前面只有20字节不能被8整除,自动填充4字节)。总共24字节
垃圾回收算法
CMS 三色标记算法-标错+写屏障
G1 三色标记算-STAB+写屏障
ZGC 颜色指针,着色指针+读屏障
三色标记算法
对象分三种类型:黑色,灰色,白色
黑色指确认不是垃圾并且成员变量已找完
灰色指确认不是垃圾,但是还没有识别成员变量
白色指还没有确认自己是不是垃圾,会被当成垃圾回收
遍历所有灰色对象,把灰色对象下白色成员变量都变成灰色后自己变成黑色。
依次反复知道遍历完整个灰色对象。然后垃圾回收所有白色对象,因为这写白色
对象不可达
情况分析
黑色对象A—(引用)—> 灰色对象B --(引用)—> 白色对象C
场景1
灰色对象和白色对象中间引用消失
这种情况不会产生问题,白色对象本身就是浮动垃圾,
不可达,就应该被清除
场景2
灰色对象和白色对象中间引用消失,但是黑色对象和白色对象中间增加引用
这种情况会产生问题,黑色对象因为已经标记查完成员变量,所以不会再查
这个白色对象,白色对象将会被当成垃圾回收。但是白色对象C现在已经和
黑色对象A产生引用,可达了,不应该是垃圾,产生了矛盾
对于场景2的问题
CMS解决方案:写入屏障方法,A,C新增引用时候,通过写屏障算法把C白色对象
强行变成灰色,这样下一次就可以继续遍历灰色对象C了,C不会被垃圾回收
那么CMS为什么需要重新标记呢?
并发标记阶段加入的新白色对象不能直接GC回收,所以需要重新标记上述步骤
最后就是并发清除—标记清除算法,会产生严重碎片化
CMS 垃圾回收器可以和用户线程一起并发执行,如果这时候用户线程内存空间不够怎么办?
这个时候会报错,可以用Serial Old 垃圾回收器
替代 CMS 进行 stop the world 垃圾回收
3.3 JVM 调优
调优步骤
1 熟悉业务场景:启动慢,响应慢,吞吐量低等等
2 目标:内存占用,低延迟,吞吐量大
3 收集日志:通过参数收据GC日志,通过JDK工具实时查看GC状态
4 分析日志:可以通过Arthas辅助分析日志
5 调整参数:切换垃圾收集器,调参数等等
场景1 (定位处理OOM)
1 jps 查找进程id号(如id号1011)
2 jmap -histo 1011 | head 20 查看哪些类产生多少个对象
(少用jmap,因为jmap时候会影响线上性能)
3 发现线程池实例不停增加,问题找到
4 可以用redefine线上内存中直接修改代码,不停服务器,问题解决
场景2(性能调优)
1 java -Xmax 1024 m -X loggc: xxxx -jar xxxxx
比如测试环境下,配置参数启动项目并打印日志
2 jstat -gc id xxxx
查看gc次数和gc时间(PerallelGCThreads默认2个线程)
3 配置PerallelGCThreads4个线程重新运行,再jstat比较发现gc时间变大了,
这方法不行
4 改CMS再重新运行,再jstat比较发现gc时间依然变大不可取
5 改G1,发现仍然不咋地
6 。。。。。不停改看哪个性能好用哪个