《码出高效:Java开发手册》 四-走进JVM

前言

JVM是java中底层的知识,这里的内容比较复杂,对于一些软件编程,会经常使用,但很多业务其实碰不到这里的知识,下图为目录
在这里插入图片描述

介绍

JVM,java虚拟机,它的前身是99年的hotspot java虚拟机,之后被oracle收购后,形成了现在的OpenJDK使用的主流JVM
一些商业公司都有自己的定制版本,比如阿里有AJDK

字节码

之前讲01是最底层的信号,而再向上就是机器码,不同的操作系统,硬件都会对应不同种类的机器码,这就需要针对多种平台编写不同代码,而jvm可以做到在不同平台都字节码来运行,字节码bytecode大小为一个字节(8位),可以存储256种不同的指令,java有200个左右的指令

  • 主要的字节码指令
    1.加载和存储指令
    将局部变量加载到操作栈中:如ILOAD,ALOAD等
    将操作栈顶存储到局部变量表:ISTORE,ASTORE等
    常量加载到操作栈顶,使用频率高:ICONST,BIPUSH,SIPUSH,LDC等
    2.运算指令
    3.类型转换指令
    4.对象创建与访问指令
    5.操作栈管理指令
    6.方法调用与返回指令
    7.同步指令
    -编写好的java文件是源代码,需要转换为字节码才能给机器执行,转化流程如下
    在这里插入图片描述

字节码需要通过类加载过程加载到JVM环境后,才可以执行
执行方法有三种:
解释执行
JIT编译执行
JIT与解释执行混合(主流JVM默认执行方式)

  • JIT作用是将字节码动态编译成可以直接发给处理器指令执行的机器码,简要流程如下
  • 在这里插入图片描述
    书中讲了一个实例:机器在刚启动时,负载比较低,之后会慢慢升高,有程序员在发布时直接分为两批发布,导致前一半机器宕机,说明了JVM刚启动时,JIT动态编译和热点代码统计还没开始,此故障说明了JIT的存在

类加载过程

任何程序都需要加载到内存才能与CPU进行交流,字节码.class文件同样需要加载到内存中,才可以实例化类

  • Java的类加载器主要有三个流程,Load,Link,Init
  • Load,加载类文件的二进制流,转化为特定数据结构,校验各种参数后,创建对应类的java.lang.Class实例
  • Link,包括准备,验证,解析三步,这里的验证相比之前更加详细,对类型,变量进行检查,准备就是分配内存,布局内存结构
  • Init,执行构造器clinit方法,如果有其他类的静态方法参与,就解析另外一个类
  • 在这里插入图片描述
    这里还讲了class是Class(注意大小写)的对象,也就是类是Class的抽象
    这里不太明白,只留下大概代码,以后去理解
    在这里插入图片描述
    在这里插入图片描述
    执行结果是
    在这里插入图片描述
    这个示例说明了类的一些加载特性,类加载器把类的实现和定义解耦了,同时可以用2的方法获取注解,方法等
    类加载器的结构,分Bootstrap,platform ClassLoader(平台类加载器),Application ClassLoader的应用类加载器。用户也可以自定义类加载器,查看本地类加载器方式如下:
    在这里插入图片描述
    JDK8下的输出
    在这里插入图片描述
    下图显示的是类加载器间的关系
    在这里插入图片描述
  • 低层次的类加载器在加载类之前会向上逐级询问,这个类是否以加载
    高层次类加载器会检查是否已经加载此类,以及是是否可以加载此类
    通常流程是:向上问是否已加载此类,如果没有,之后向下问是否可加载,如果不能,就让当前类加载器去加载这个类,当然实际类库比图中要多
  • Bootstrap加载的路径可以追加,不建议修改或者删除原有路径
  • 自定义类加载器的情况
    1.隔离加载类:在某些框架中进行中间件与应用模块隔离,比如需要确保应用依赖的jar包不会影响中间件运行时使用的jar包
    2.修改类加载方式,Boostrap外的加载并非一定要引入
    3.扩展加载源,比如从数据库,网络甚至机顶盒进行加载
    4.防止源代码泄露
  • 实现自定义类加载器的步骤:继承ClassLoader ,重写findClass()方法,调用defineClass() 方法。
  • 如下为一个简单的类加载器
  • 在这里插入图片描述

内存布局

在这里插入图片描述
上图即为经典JVM布局

  • 堆:堆区是OOM故障多发地,大量创建对象,容易消耗完堆内存,可以调整大小,-xms,-xmx,一般保持一样大小,在线上环境可以避免GC后调整堆大小时带来额外压力
    堆里对象的晋升流程如图在这里插入图片描述
  • 给JVM 设置运行参数-XX:+HeapDumpOnOutOfMemoryError ,可以让JVM在OOM异常时能输出堆内信息
    • 元空间
      元空间的前身是永久代,由于很难调优,经常出现致命错误,后来用元空间替代后,转为再本地内存中分配
      3.JVM Stack(虚拟机栈)
      栈就是先进后出,像弹夹一样,它是线程私有的,用于进行方法调用
      栈帧:就是栈顶的,只有这里的帧才是有效的,也就是当前栈帧,它是方法运行的基本结构,执行引擎运行时,只能操作当前栈帧
      StackOverflowError表示请求的栈溢出,导致内存耗尽,通常出现在递归方法中
  • 栈帧包括了局部变量表,操作栈,动态连接,方法返回地址等
  • 局部变量表:存放方法参数和局部变量
  • 操作栈:初始状态为空的桶式结构栈,里面会压入弹出一些指令,与局部变量表进行交互
    以一个常见的面试题:i++和++i为例子,在字节码层面是这样的
    在这里插入图片描述
    左边:局部变量表中取出一个数,压入栈顶,下一步实现+1操作,对栈顶元素无影响,接下来把栈顶元素赋值给a
    右边:先做完+1操作后,压入栈顶,之istore_2后存进去的就是i+1的值
    这里i++不是原子操作,即使用volatile修饰,多个线程下也会产生数据覆盖问题
  • 动态连接:每个栈帧中有一个对当前方法的应用,就是动态连接
  • 方法返回地址:方法执行时有两种退出情况:正常退出和异常退出
    4.本地方法栈
    本地方法栈是线程私有的,虚拟机栈主JVM内部,本地方法栈则主JVM外部
    5 .程序计数器
    这里的计数器指的是CPU的寄存器,这里主要是保证指令之间连贯执行,多线程之间互不影响
    从公有私有角度可以为JVM分为以下
    在这里插入图片描述

对象实例化

从Object ref = new Object()分析,查看字节码如下
在这里插入图片描述

  • 从字节码角度看待对象创建过程
    new:无CLass对象则进行类加载,之后分配堆内存,零值初始化,将指向实例变量的引用变量压入虚拟机栈顶
    Dup:在栈顶复制该引用变量,如果 方法有参数,需要把参数压入操作栈,两个引用变量的目的不同,压至底下的用于赋值,栈顶的作为句柄调用相关方法
    INVOKESPECIAL:调用对象实例方法,通过栈顶调用init方法,clinti是类初始化方法,inti是对象初始化方法
  • 从执行步骤分析对象创建过程
    确定类元信息是否存在,这里找不到class文件就会抛出ClassNotFound异常
    分配对象内存,计算对象占用空间,在堆中划分内存,这里有同步操作,一般是CAS失败重试或者区域加锁等
    设定默认值
    设置对象头,包括哈希码,GC,锁信息,类元信息
    执行init方法,初始化成员变量,执行实例化代码块,调用类的构造方法,引用变量接收对象首地址

垃圾回收

垃圾回收,简称GC,判断内存不足且虚拟机空闲时,就会清除不再使用的对象,自动释放内存

  • 判断逻辑:GC Roots,对象与其roots之间没有引用关系(包括间接),就可以被回收
  • 回收方法:标记清除:直接清除不用的对象,会产生大量内存碎片
    标记整理:将可以存活对象整理到内存一端,把边界之外的都清除掉
    复制:MArk-coy,分两块空间,回收时把可存活对象放到另一块上,之后清除原对象
    新生代使用复制,老生代标记整理
  • 垃圾回收器:有数十种,常用的有serial,CMS,G1

在这里插入图片描述

G1采用标记清除,可以配置-XX :+UseCMSCompactAtFul!Collection参数,强制full gc后对老年代进行压缩,也就是空间碎片整理,可以配置XX : +CMSFul!GCsBeforeCompaction=n 参数,防止频繁整理引发STW(暂停整个应用程序执行)
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值