多看BAT招新要求,跟着学
为什么要学?
- 线上系统突然卡死,OOM
- 线上JVM GC
- 项目上线,JVM参数设置
- 实际调优经验
- 技术选型
- 性能优化、重构,稳定性
- 分布式解决方案
- 系统瓶颈,性能调优,解决疑难杂症。
-Xms1024m -Xmx1500m
内存与垃圾回收
官方文档(英文):https://docs.oracle.com/javase/8/docs/
生成字节码文件,可以在不同平台运行。
JVM跨语言的平台,运行字节码文件,不止能执行JAVA程序,只要编译器生成的字节码文件遵循虚拟机规范
难题:CPU、操作系统、编译器
JVM与JAVA体系结构
虚拟机分为:程序虚拟机、系统虚拟机
JAVA虚拟机专门为执行当个计算机程序而设计,JAVA虚拟机中执行的指令为JAVA字节码指令,是二进制字节码的运行环境
特点:
- 一次编译,到处运行
- 自动内存管理
- 自动垃圾回收
JDK包括JRE,多了前端编译器,java转.class
hotspot vm,执行引擎翻译成机器能执行的语言
java程序—javac前端编译–》字节码文件—–》JVM
- 词法分析、语法分析、语义分析、字节码生成器
JVM
类加载器
字节码校验器
翻译字节码(逐行解析执行)、JIT编译器编译执行(编译成机器指令、运行时缓存热点代码,提高程序执行效率),一般都有,但是用其中一个就能执行
具体的内存结构,取决于不同厂商的实现
架构模型
指令流基本基于栈的指令集架构,另一种是寄存器的指令集架构
- 栈式架构
- 设计实现简单,适用于资源受限
- 避开寄存器分配难题,使用零地址指令,执行依赖于操作栈,指令集小,编译器容易实现
- 不需要硬件支持,可移植性好
- 寄存器架构
- 传统的PC,以及Android的Davlik虚拟机
- 指令集架构完全依赖硬件,可移植性差
- 性能优秀,执行高效
- 指令少
javap xx.class
,查看字节码
JVM生命周期
启动
通过引导类加载器创建初始类,这个类由虚拟机的具体实现指定
执行
程序开始时才运行,结束时就停止
执行java程序,实际是执行JVM的进程
jps
查看执行中的进程
退出
- 程序正常结束
- 异常终止
- 操作系统错误
- 线程主动结束,Runtime.exit或者System
- JNI(Java Native Interface)加载卸载JAVA虚拟机
发展历程
Sum Classic Vm
第一款商用Java虚拟机,1.4时完全淘汰
只提供了解释器,此时不能与外挂的JIT一起执行,如果只用JIT,程序启动,暂停的时间长
hotspot内置此虚拟机
Exact Vm
- 可以知道内存中某个位置的数据具体是什么类型
hotspot VM
- 热点代码的探测技术
- 通过计数器找到最具有编译价值的代码,触发即使编译到缓存区或栈上替换
- 编译器(执行性能)和解释器(响应时间)协同工作
BEA的Jrockit
- 专注服务器应用
- 不关注程序的启动速度,内部不包含解释器实现
- 行业数据显示,世界上最快的JVM
- 全面的Java运行时解决方案组合
IBM的J9
- 也号称是最快的,主要是自己的产品的应用
- 服务器、桌面、嵌入式等多用途Vm
- 2017年,开源OpenJ9
KVM和CDC/CLDC Hotspot
- java me的两款虚拟机,CDC/CLDC Hotspot
- KVM是CLD早期产品,简单、轻量、高度可移植,面向低端设备维持市场
- 智能控制器、传感器
- 老人手机
Azul Vm
- 与硬件平台绑定
liquid Vm
- 运用在自己系统上,能实现专用系统的必要功能,线程调度、文件系统、网络支持等
Apache Harmony
- IBM和Intel联合开发,没有大规模商用,但是Java类库代码吸纳进入Android SDK
taobaoJVM
- 深度定制开源的高性能服务器版JAVA虚拟机
- 多个虚拟机进程实现共享
- 严重依赖intel的cpu
dalvik Vm
应用于Android系统
基于寄存器架构
执行的是编译后的dex文件,执行效率较高
5.0支持提前编译,不通过字节码文件,直接编译成机器指令
graal Vm
- oracle研发,跨语言全栈虚拟机,作为任何语言的运行平台使用
- 支持不同语言混用对方接口和对象
- 将源代码转换为GraalVm能接受的标识,快速构建面向新语言的解释器,在运行时能够即使编译优化
类加载子系统
详细
类加载器子系统
加载、链接、初始化
从文件系统或网络中加载class文件,
ClassLoader只负责class文件的加载,由ExecutionEngine确定是否能运行
加载的类信息存放在方法区内存空间
加载:
- 类的全限定名获取定义该类的二进制流
- 通过字节流所代表的静态存储结构转化为方法区运行时的数据结构
- 在内存中生成代表该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
.class文件获取:
- 本地系统加载
- 网络获取,Web Applet
- Zip压缩包,jar、war
- 运行时计算生成,动态代理技术
- 其他文件生成,jsp
- 数据库提取.class
- 加密文件获取,防Class文件被反编译
链接
idea插件jclasslib Bytecode viewer打开字节码文件、Binary Viewer
verify
- 确保class文件的字节流符合虚拟机要求,保证加载类的正确性,不危害虚拟机安全
- 文件格式验证,元数据验证,字节码验证,符号引用验证
prepare
// prepare:a = 0
// initial:a = 1
private static int a = 1;
- 类变量分配内存,设置变量默认初始值
- 如果为final static,直接分配数值
- 不会为实例变量初始化,类变量会分配在方法区中,而实例变量是会随着变量一起分配到Java堆中
解析
- 将常量池中的符号引用为直接引用
初始化
- 执行类构造器方法clinit()的过程
- client()方法不需要定义,javac编译器自动收集类中的所有的类变量的赋值和静态代码块中的语句 合并到一起
- clinit不同于类的构造器
- 若该类有父类,JVM会保证父类的clinit先执行
- 虚拟机保证多线程下同步加锁
总结:
加载:加载类的二进制流,转化为方法区运行时数据结构,在内存中生成该Class对象
链接:
- 确保class文件字节流符合虚拟机要求
- 类变量分配内存
- 解析符号引用为直接引用?
初始化:
- 执行clinit(类常量初始化)
运行时数据区概述及线程
程序计数器
虚拟机栈
本地方法接口
堆
方法区
直接内存
执行引擎
String Table
垃圾回收概述
垃圾回收算法
垃圾回收概念
垃圾回收器
字节码与类的加载篇
性能监控与调优篇
常见问题
服务挂
- jps 获取进程号
- ll /proc/214517/cwd;即可查看是否在运行
- 实际上是运行jvm内存不足
- 默认是电脑的1/4,但是不足,所以可能出错,-Xmx1500M -Xms1500M
- for a in $(ps aux | grep Launcher | grep -v ‘grep’ | awk {‘print $2’}); do ll /proc/$a/cwd; done