码出高效-java笔记-JVM

字节码

  • java所有指令200个左右,一个字节(8位)可以保存256种不同的指令信息
  1. 加载或存储指令

    • 将局部变量加载到操作栈中
    • 从操作栈顶存储到局部变量表
    • 将常量加载到操作栈顶(极为高频)
  2. 运算指令

  3. 类型转换指令

  4. 对象创建与访问指令

  5. 操作栈管理指令

  6. 方法调用与返回指令

  7. 同步指令

    jvm使用方法结构中的acc_synchronized标志同步方法,Synchronize语义通过monitorenter和monitorexit实现

  • .java文件不是可执行文件,需要编译成为字节码甚至是机器码文件

    java源文件->词法解析->语法解析->语义分析->生成字节码->字节码

  • 字节码需要通过类加载过程加载到jvm环境中才可执行,执行模式三种:

    1. 解释执行
    2. jit编译执行
    3. jit编译与解释混合执行(主流jvm默认模式)
  • 分批方式进行发布的原因是:机器在热状态下的负载大于冷机状态,冷机状态的服务可能因无法承载流量而假死,每个发布批次之多占到整个集群集群数量的1/8。

类加载过程

双亲委派模型

  • 步骤

    1. Load阶段,读取类文件产生二进制流,转化为特定的数据结构,初步校验,创建对应类的java.lang.Class实例
    2. Link阶段,验证、准备、解析三个步骤
    3. Init阶段,执行类构造器方法
  • 意义

    类加载是将.class字节码文件实例化成Class对象并进行相关初始化的过程

new与newInstance区别

  1. new是强类型校验,可以调用任何构造方法,在使用new时,这个类可以没有被加载过
  2. Class类下的newInstance是弱类型,只能调用无参构造方法,如果没有无参构造方法就会抛出InstantiationException异常;如果此构造方法没有权限访问,则抛出IllegalAccessException异常。

类加载器

  • java通过类加载器把类的实现与类的定义进行解耦,是实现面向接口编程、依赖倒置的必然选择
  • 三层结构:
    1. 最高层Bootstrap,于jvm启动时创建,是最根基的类加载器,装载最核心的java类(Object、System、String等)
    2. 第二层在JDK9是Platform ClassLoader平台类加载器,加载一些扩展的系统类;JDK9之前是Extension ClassLoader
    3. 第三层是Application ClassLoader应用类加载器,加载用户定义的CLASSPATH路径下的类
  • 自定义类加载器步骤
    1. 继承ClassLoader
    2. 重写findClass方法
    3. 调用defineClass方法

内存布局

  1. Heap堆区

    OOM故障的主要发源地,储存几乎所有的实例对象,堆区由各子线程共享使用,占用的空间最大,可以通过-Xms、-Xmx设置,线上一般设置成一样大小,避免在GC后调整堆大小时有额外的压力

    堆分成两大块:新生代和老年代,新生代=1个Eden区+2个Survivor区。

    Eden区填满后会触发YGC(YoungGC),存活的对象放入Survivor区,

    Survivor分为S0和S1两块空间。

    每个对象都有一个计数器,每次YGC都会加一,-XX:MaxTunuringThreshold设置达到某个阈值直接从Eden移到老年代,默认是15。

  2. Metaspace元空间

    JDK11版本JVM为Hotspot,JDK8中元空间的前身Perm区已经被淘汰,-XX:MaxPermSize=1280m指定参数,通过调整此参数防止运行过程中,因为不断动态加载类导致的OOM。

    JDK8使用元空间代替永久代,元空间在本地内存中分配,jdk8中Perm区中的所有内容中字符串常量移至堆内存,其他信息包括类元信息、字段、静态属性、方法常量等都移动至元空间。

  3. JVM Stack(虚拟机栈)

    是描述java方法执行的内存区域,是线程私有的。

    栈帧是方法运行的基本结构;StackOverflowError表示请求的栈溢出,导致内存耗尽,通常出现在递归方法中。

    1. 局部变量表

      存放方法参数和局部变量的区域。局部变量需要显示初始化

    2. 操作栈

      是一个初始状态为空的桶式结构栈。jvm的执行引擎是基于栈的执行引擎。

    3. 动态连接

      每个栈帧中包含一个在常量池中对当前方法的引用,目的是支持方法调用过程的动态连接

    4. 方法返回地址

      方法退出的两种情况:一是正常退出;二是异常退出;方法退出过程相当于弹出当前栈帧。

  4. Native Method Stacks(本地方法栈)

    线程对象私有;

  5. 程序计数寄存器

    用来存放执行指令的偏移量和行号指示器等,程序执行或恢复都要依赖程序计数器。

    程序计数器在各个线程之间互不影响,也不会发生内存泄露。

对象实例化

对象创建执行步骤:

  1. 确认类元信息是否存在

    在metaspace内检查需要创建的类元信息是否存在。

  2. 分配对象内存

    引用变量仅分配引用变量空间,4个字节大小。

  3. 设定默认值

  4. 设置对象头

  5. 执行init方法

垃圾回收

典型的三种垃圾回收器:Serial、CMS、G1

Serial

主要应用于YGC,采用串行单线程的方式,STW(stop the world)

CMS

回收停顿时间比较短、比较常用;

经历初始标记、并发标记、重新标记、并发清除四个步骤;

采用了标记-清除算法,会产生大量空间碎片;

通过配置-XX:+UseCMSCompactAtFullCollection参数,强制JVM在FGC完成后对老年代进行压缩;

G1

采用Mark-Copy的方式;

一大优势在于可预测的停顿时间,能够尽可能快的在指定时间内完成垃圾回收任务;

JDK11将G1设为默认垃圾回收器;

G1的Concurrent Marking的步骤:

  1. Initial Mark,其实就是YoungGC,会引起STW
  2. Root Region Scan,根区域扫描
  3. Concurrent Mark并发标记
  4. Remark 重新标记,会STW
  5. Cleanup,为Mixed GC做准备,统计所有区域中的存活对象,并将待回收区域按回收价值排序,优先回收垃圾最多的区域。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值