JVM

机器语言

一个指令由操作码和操作数组成

方法调用等于一个压栈的过程

  • 栈有 BP寄存器 和 SP寄存器来占用空间

    • BP -> Base Point 栈基址(栈底)
    • SP -> Stack Point 栈顶

字节序用于规定数据在内存单元如何存放,二进制位的高位和低位映射到地址内存的高位和地位

  • 高地址放在低地址的前面叫大端序
  • 低地址放在高地址的前面叫小端序
//翻译成汇编语言
//b 8位   w 16位  l 32位  q 64位
//sub $4, %esp;
//movl $1, -4(%ebp);
int i = 1;

字节码文件

Magic Number:CAFE BABE

Minor Number:小版本号

Major Number:大版本号

Constant Pool Count:常量池长度

Constant Pool:常量池字节码 -> 常量池长度-1个常量,每个常量为1个字节的标志位+2个字节的实际值

Access Flag:修饰符

This_class 存储常量池的引用

super_class 存储常量池的引用

Interface Count 接口数量

Interfaces 接口,常量池的引用

Fields Count 属性数量

Fields 属性 常量池的引用

Methods Count 方法数量

Methods 方法 常量池引用

Attribute Count

Attribute :code Java的汇编代码

类加载

Loading

  • 类加载主要是将.class文件通过二进制字节流的方式读入JVM中。加载阶段JVM需要完成三件事

    • 通过classloader将.class文件读入内存
    • 将字节流所代表的静态存储结构转化为方法区的运行时数据结构
    • 在内存中生成一个该类的java.lang.Class对象,作为方法区这个类各种数据的访问入口
  • 类加载器

    • Bootstrap(rt.jar、charset.jar等核心类 加载器由c++实现)
    • Extension(lib/ext/*.jar)
    • Application(classpath/ *.jar… *.class)
    • 自定义类加载器,只需要重写findClass方法即可
  • 双亲委派

    • 优先使用上层加载器进行加载,捕获异常再尝试使用下层加载器

    • 优点:

      • 有效避免了某些恶意类的加载,安全问题是主要原因
      • 每个类只被加载一次,避免了重复加载,提高效率

    • 线程的默认加载器是applicationClassLoader

  • LazyLoading 五种情况

    • –new getstatic putstatic invokestatic指令,访问final变量除外(final变量不需要加载就能读取到)
    • –java.lang.reflect对类进行反射调用时
    • –初始化子类的时候,父类首先初始化
    • –虚拟机启动时,被执行的主类必须初始化
    • –动态语言支持java.lang.invoke.MethodHandle解析的结果为REF_getstatic REF_putstatic REF_invokestatic的方法句柄时,该类必须初始化

Linking

  • Verification 验证

    • 验证文件是否符合JVM规定
  • Preparation 准备
    • 将静态变量在方法区分配内存,并设置默认值
  • Resolution 解析
    • 将类、方法、属性等符号引用解析为直接引用(将符号引用转换为指向内存地址的指针)
    • 常量池中的各种符号引用解析为指针、偏移量等内存地址的直接引用

Initializing

  • 调用类初始化代码

    • 父类初始化
    • static变量赋初始值/static块赋初始值

小总结:

  1. load - 默认值 - 初始值
  2. new - 申请内存 - 默认值 - 初始值

是ldc、iconst、lconst、fconst、dconst还是ldc指令,是根据赋值的大小来进行判定,在编译时进行处理

// 10在哪儿存?
// 在操作数栈中,字节码的指令中    bipush 10
static int a = 10;// 10在哪儿存?
// 在静态常量池,因为java的指令中没有bfipush,只能复用 ldc #2 + putstatic
// float double 对象等初始化放到常量池中,运行时通过ldc指令,将他们的地址放入操作数栈来操作
static float a = 10.0;

Java执行引擎

混合模式

Java默认使用解释器+编译器组合来执行代码

起始阶段采用解释执行

热点代码会进行编译成本地文件执行

检测热点代码:

  • 多次被调用的方法(方法计数器:检测方法执行频率)
  • 多次被调用的循环(循环计数器:检测循环执行频率)
  • 进行编译

JIT即时编译器

JIT内部包含:中间代码生成器、代码优化器、目标代码生成器、探测分析器。

JIT会将多个字节码文件生成的指令进行打包、优化、编译成本地代码然后放到方法区中,当执行代码时直接执行机器代码or汇编代码即可,不再需要一行行进行解释执行,增加代码执行效率。

可以通过配置参数来指定Java引擎执行模式

  • -Xmixed 默认为混合模式 开始解释执行热点代码编译执行
  • -Xint 使用解释执行,启动速度很快,执行稍慢
  • -Xcomp 使用纯编译模式,执行很快,启动很慢
    • 热点代码检测阈值 -XX:CompileThreshold = 10000

JMM

硬件层数据一致性

现代计算机存储器

MESI

协议很多

intel 用MESI(CPU给每个缓存行使用4种状态标记)

  • Modified 当前缓存行在当前cpu被修改过
  • Exclusive 当前缓存行只在当前cpu中被缓存,为当前cpu独享
  • Shared 当前缓存行被多个cpu共享,且在多个cpu中当前缓存行数据一致
  • Invalid 当前cpu持有的当前缓存行无效。(被其他cpu修改过)

参考文档:https://www.cnblogs.com/z00377750/p/9180644.html

现代CPU的数据一致性实现 = 缓存锁(MESI …) + 总线锁

缓存行

读取缓存以cache line为基本单位,目前64bytes

位于同一缓存行的两个不同数据,被两个不同CPU锁定,产生互相影响的伪共享问题

伪共享问题:JUC/c_028_FalseSharing

使用缓存行的对齐能够提高效率(填充到64bytes保证不会与其他线程发生缓存行伪共享)

乱序问题

CPU为了提高指令执行效率,会在一条指令执行过程中(比如去内存读数据(慢100倍)),去同时执行另一条指令,前提是,两条指令没有依赖关系

写操作也可以进行合并(WCBuffer 合并写 是另一种形式的缓存,更新后数据直接发送到L2级别缓存,一般只有4个字节)

参考文档:https://www.cnblogs.com/liushaodong/p/4777308.html

参考代码:JUC/029_WriteCombining

Java中的乱序执行

原始参考:https://preshing.com/20120515/memory-reordering-caught-in-the-act/

public class T04_Disorder {private static int x = 0, y = 0;private static int a = 0, b =0;public static void main(String[] args) throws InterruptedException {int i = 0;for(;;) {i++;x = 0; y = 0;a = 0; b = 0;Thread one = new Thread(new Runnable() {public void run() {//由于线程one先启动,下面这句话让它等一等线程two. 读着可根据自己电脑的实际性能适当调整等待时间.//shortWait(100000);a = 1;x = b;}});Thread other = new Thread(new Runnable() {public void run() {b = 1;y = a;}});one.start();other.start();one.join();other.join();String result = "第" + i + "次 (" + x + "," + y + ")";if(x == 0 && y == 0) {// 理论上如果不发生指令重排是不可能出现x,y都为0的情况System.err.println(result);break;} else {//System.out.println(result);}}}public static void shortWait(long interval){long start = System.nanoTime();long end;do{end = System.nanoTime();}while(start + interval >= end);}
}

如何保证特定情况下不乱序

硬件内存屏障 X86

在内存屏障指令前后的读or写不可乱序执行

sfence: store| 在sfence指令前的写操作当必须在sfence指令后的写操作前完成。
lfence:load | 在lfence指令前的读操作当必须在lfence指令后的读操作前完成。
mfence:modify/mix | 在mfence指令前的读写操作当必须在mfence指令后的读写操作前完成。

原子指令,如x86上的”lock …” 指令是一个Full Barrier,执行时会锁住内存子系统来确保执行顺序,甚至跨多个CPU。Software Locks通常使用了内存屏障或原子指令来实现变量可见性和保持程序顺序

JVM级别如何规范(JSR133)

LoadLoad屏障:
对于这样的语句Load1; LoadLoad; Load2,

在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。

StoreStore屏障:

对于这样的语句Store1; StoreStore; Store2,

在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。

LoadStore屏障:

对于这样的语句Load1; LoadStore; Store2,

在Store2及后续写入操作被刷出前,保证Load1要读取的数据被读取完毕。

StoreLoad屏障:

对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。

volatile的实现细节

  1. 字节码层面
    ACC_VOLATILE

  2. JVM层面
    volatile内存区的读写 都加屏障

    StoreStoreBarrier

    volatile 写操作

    StoreLoadBarrier

    LoadLoadBarrier

    volatile 读操作

    LoadStoreBarrier

  3. OS和硬件层面
    参考文章:https://blog.csdn.net/qq_26222859/article/details/52235930
    hsdis - HotSpot Dis Assembler
    windows lock 指令实现 | MESI实现

synchronized实现细节

  1. 字节码层面
    ACC_SYNCHRONIZED
    monitorenter + monitorexit
  2. JVM层面
    C C++ 调用了操作系统提供的同步机制
  3. OS和硬件层面
    X86 : lock cmpxchg / xxx
    https

    JVM-从熟悉到精通相关推荐

    1. 清华毕业大牛教你涨薪5K的JVM调优骚操作是什么!如何在简历写上熟悉(精通)JVM调优,有过线上调优经验!

      就在前天的时候,马士兵老师讲了一节公开课,分享了涨薪5K的JVM调优骚操作是什么!如何在简历写上熟悉(精通)JVM调优,有过线上调优经验! 主要包含的内容如下: 1. 为什么一个百万级TPS系统会频繁 ...

    2. JVM从入门到精通(尚硅谷宋红康)

      不动笔墨不读书,先把书读厚,再把书读薄是我学习方式. 所以等理解了再整理一次笔记,目前笔记和视频一一对应. 笔记连载中 <尚硅谷2020最新版宋红康JVM> 第1章:JVM与Java体系结 ...

    3. JVM 从入门到“精通”,妥妥的

      作为一名优秀的 Java 开发程序员,以及想那些想要学习 Java 更深层一点的知识的同学,对 JVM 的熟悉与熟练使用是必不可缺的核心技能了,也是每个 Java 程序员应该要做到的. 深入学习 JV ...

    4. 程序员找工作遇到的“了解、熟悉、精通”的三种技能要求实际上是这样的标准!

      本篇文章主要讲解:程序员找工作遇到的"了解.熟悉.精通"的三种技能要求的标准说明. 日期:2021-7-19 作者:任聪聪 编程语言以及技能的了解.熟悉.精通的概念标准: 一.编程 ...

    5. 技术水平:了解、熟悉、精通各是什么意思以及如何深入研究一门技术

      技术水平:了解.熟悉.精通各是什么意思以及如何深入研究一门技术 一.入门与了解: 根据官网介绍或者网上的一些技术文章可以写出Demo出来,这时对该项技术的进本原理有所了解,这就达到了了解入门的地步了. ...

    6. 【并行计算-CUDA开发】从熟悉到精通 英伟达显卡选购指南

      举报 说到显卡,就不免令人想到英伟达和AMD两家面向个人消费级和企业级最大的显示芯片生产企业,英伟达和AMD,今天小编为大家简单的介绍一下英伟达的显卡选购方面的攻略,为一些想要购买显卡的用户提供一些参 ...

    7. JVM从入门到精通(七):GC常用参数,Method Area,JVM调优案例分析

      GC常用参数 -Xmn -Xms -Xmx -Xss 年轻代 最小堆 最大堆 栈空间 -XX:+UseTLAB 使用TLAB,默认打开 -XX:+PrintTLAB 打印TLAB的使用情况 -XX:T ...

    8. JVM从入门到精通(六):JVM调优必备理论知识 - 3种垃圾清除算法,常见的垃圾回收器

      JVM调优是一层窗户纸,只是看起来很难.学完本节课,让你: 熟悉 GC 常用算法,熟悉常见垃圾回收器,具有实际 JVM 调优实战经验 What is garbage 什么是垃圾?没有引用指向的对象就是 ...

    9. 【jvm系列-04】精通运行时数据区共享区域---堆

      JVM系列整体栏目 内容 链接地址 [一]初识虚拟机与java虚拟机 https://blog.csdn.net/zhenghuishengq/article/details/129544460 [二 ...

    10. 熟悉,掌握,精通? BOSS直聘你真棒!

      比面试官懂,就叫精通. 和面试官懂得一样,就叫掌握. 比面试官少,就叫熟悉. 敬畏未知,保持学习的渴望和热情,所以我都写的熟悉.

    最新文章

    1. 数据结构 - 有两个链表,第一个升序,第二个降序,合并为一个升序链表(C++)
    2. java安全例外_java – 本地Applet安全例外
    3. gpio模拟pwm_模拟智能台灯
    4. 解决torch.cuda.is_available()为False的问题
    5. oracle内外链接混合用,混合在一起通过连接,内部连接和总结与Oracle
    6. Codeforces Round #712 (Div. 2) E. Travelling Salesman Problem 思维转换
    7. dubbo 消费者也要暴露端口吗_一文详细解读 Dubbo 中的 http 协议
    8. DOM BOM document window 区别
    9. background-size在IE8不兼容问题
    10. 图像入门二之视频操作
    11. 低代码开发平台_什么是低代码和无代码开发平台?
    12. 西门子uss通讯实例_西门子plc1200系列的功能特点有哪些?
    13. 基于层序+中序遍历序列构建二叉树
    14. latex 插图排版
    15. java通过jdbc访问数据库
    16. Windows下如何远程连接Linux图形化桌面-教你两招
    17. mysql:赋予用户权限、查看及修改端口号
    18. BPSK调制在AWGN信道下,卷积码
    19. 【高等数学笔记】拉格朗日乘数法(Lagrange Multiplier Method):其实也没那么难嘛
    20. 【Pigeon源码阅读】服务注册发布流程(四)

    热门文章

    1. 成考自考本科生可以申请德国大学吗?
    2. HTC手机傻瓜式安装Google Play服务
    3. 分布式配置中心设计——思维导图总结
    4. Python 去除图片中多种颜色或者单一颜色
    5. 微信投票微信刷票的技巧和意义
    6. 成熟好用的电池供电切换电路
    7. 【渝粤题库】陕西师范大学100111 计算机应用基础 作业 (高起专)
    8. ____ To All Girls  Boys
    9. 佳博 TSC打印机 TSPL指令开发
    10. 用友nc65 uap开发找产品功能源代码