多看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确定是否能运行

加载的类信息存放在方法区内存空间

加载:

  1. 类的全限定名获取定义该类的二进制流
  2. 通过字节流所代表的静态存储结构转化为方法区运行时的数据结构
  3. 在内存中生成代表该类的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