JVM完整笔记

2023-11-06

在这里插入图片描述

这是我在看课程《黑马程序员JVM完整教程》过程中记的笔记。我觉得该课程总时不长,并且理论+实战是一个入门JVM的好课程。
若你看完该课程可以看下面几个参看书进一步深入了解JVM

  • 深入理解Java虚拟机(第二版)
  • 实战Java虚拟机
  • 深入JAVA虚拟机第二版

这三本参考书的pdf版本已经放在下面的网盘中(只限个人看)
链接:https://pan.baidu.com/s/1iNJcbSecMwnOQuaJNxzrRQ
提取码:wcar

1.内存结构

1.1 程序计数器

image-20220720222405638

Program Counter Register 程序计数器(寄存器)

  • 作用,是记住下一条jvm指令的执行地址
  • 特点
    • 是线程私有的
    • 不会存在内存溢出

下面是一条Java程序的指令:
image-20220720221813565

1.2 虚拟机栈

image-20220720230437288

**定义:**Java Virtual Machine Stacks (Java 虚拟机栈)

  • 栈-每个线程运行时需要的内存空间。所以跟程序计数器一样线程私有的结构。
  • 栈由多个栈帧组成,每个栈帧对应每个方法运行时需要的内存,包含方法参数,局部变量,返回地址等信息。
  • 每个线程只能有一个活动栈帧,对应着当前正在执行的那个方法

image-20220720223105927

问题辨析:

1.垃圾回收是否涉及栈内存?

答案:不会

2.内存分配越大越好吗?

答案:不是,-Xss 参数来设置栈空间,如果栈空间设置过大,会降低线程数,因为机子的物理内存空间大小固定的。

3.方法内的局部变量是否线程安全?

  • 如果方法内局部变量没有逃离方法的作用访问,它是线程安全的
  • 如果是局部变量引用了对象,并逃离方法的作用范围,需要考虑线程安全

栈内存溢出

java.lang.StackOverflowError

  • 栈帧过多导致栈内存溢出
  • 栈帧过大导致栈内存溢出

线程运行诊断

  • cpu占用过多案例
    • 用top定位哪个进程对cpu的占用过高
    • ps H -eo pid,tid,%cpu | grep 进程id (用ps命令进一步定位是哪个线程引起的cpu占用过高)
    • jstack 进程id
      • 可以根据线程id 找到有问题的线程,进一步定位到问题代码的源码行号
  • 程序运行很长时间没有结果案例
    • jstack 进程id 来查看是否出现死锁问题

1.3 本地方法栈

image-20220720230522325

  • 作用:给本地方法提供运行空间

1.4 堆

image-20220720230902088

Heap 堆

  • 通过 new 关键字,创建对象都会使用堆内存

特点

  • 它是线程共享的,堆中对象都需要考虑线程安全的问题
  • 有垃圾回收机制

堆内存溢出

java.lang.OutOfMemoryError:Java head space

image-20220720231131516

  • -Xmm 设置堆空间

堆内存诊断

  • jps 工具

    查看当前系统中有哪些 java 进程

  • jmap 工具

    查看堆内存占用情况 jmap - heap 进程id

  • jconsole 工具

    图形界面的,多功能的监测工具,可以连续监测

案例

  • 垃圾回收后,内存占用依然很高
    • jps:查看当前系统中有哪些 java 进程
    • jmap:查看堆内存占用情况 jmap - heap 进程id
    • jconsole :执行垃圾回收,并实时查看
    • jvisualvm:可视化的方式显现,jvm内存结构

1.5 方法区

image-20220720232505950

定义

  • Java 虚拟机有一个在所有 Java 虚拟机线程之间共享的方法区。方法区类似于传统语言的编译代码的存储区,或者类似于操作系统进程中的“文本”段。它存储每个类的结构,例如运行时常量池、字段和方法数据,以及方法和构造函数的代码,包括类和实例初始化和接口初始化中使用的特殊方法。
  • 方法区是在虚拟机启动时创建的。尽管方法区在逻辑上是堆的一部分,但简单的实现可能会选择不进行垃圾收集或压缩它。本规范不要求方法区域的位置或用于管理已编译代码的策略。方法区域可以是固定大小,也可以根据计算需要扩大,如果不需要更大的方法区域,可以缩小。方法区的内存不需要是连续的。
  • Java 虚拟机实现可以为程序员或用户提供对方法区域初始大小的控制,以及在方法区域大小可变的情况下,对最大和最小方法区域大小的控制。

组成:

image-20220720233029357

方法区内存溢出

  • 1.8 以前会导致永久代内存溢出

    image-20220720233539735

  • 1.8 之后会导致元空间内存溢出

    image-20220720233618338

运行时常量池

  • 常量池,就是一张表,虚拟机指令根据这张常量表找到要执行的类名、方法名、参数类型、字面量等信息
  • 运行时常量池,常量池是 *.class 文件中的,当该类被加载,它的常量池信息就会放入运行时常量池,并把里面的符号地址变为真实地址

StringTable

先看几道面试题:

image-20220720235226829

  • 常量池中的字符串仅是符号,第一次用到时才变为对象
  • 利用串池的机制,来避免重复创建字符串对象
  • 字符串变量拼接的原理是 StringBuilder (1.8)
  • 字符串常量拼接的原理是编译期优化
  • 可以使用 intern 方法,主动将串池中还没有的字符串对象放入串池
    • 1.8 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有则放入串池, 会把串池中的对象返回
    • 1.6 将这个字符串对象尝试放入串池,如果有则并不会放入,如果没有会把此对象复制一份,放入串池, 会把串池中的对象返回
  • 调整 -XX:StringTableSize=桶个数

1.6 直接内存

定义:Direct Memory

  • 常见于 NIO 操作时,用于数据缓冲区
  • 分配回收成本较高,但读写性能高
  • 不受 JVM 内存回收管理

基本原理

  • 不使用直接内存的时候I/O操作过程

    image-20230322131846007

  • 使用直接内存

    image-20230322132043963

分配和回收原理

  • 使用了 Unsafe 对象完成直接内存的分配回收,并且回收需要主动调用 freeMemory 方法
  • ByteBuffffer 的实现类内部,使用了 Cleaner (虚引用)来监测 ByteBuffffer 对象,一旦ByteBuffffer 对象被垃圾回收,那么就会由 ReferenceHandler 线程通过 Cleaner 的 clean 方法调用 freeMemory 来释放直接内存

2.垃圾回收器

2.1 如何判断对象可以回收

方法一:引用计数法 (早期的Python虚拟机用过)

概念:

  • 如果一个对象被引用了那么对应的引用计数器加一,反正减一,当等于零时说明该对象可以回收。

好处:

  • 简单易解

坏处:

  • 如果存在循环引用,那么该方法失效了

    image-20230322134208697

**方法二:可达性分析算法 **

  • Java 虚拟机中的垃圾回收器采用可达性分析来探索所有存活的对象
  • 扫描堆中的对象,看是否能够沿着 GC Root对象 为起点的引用链找到该对象,找不到,表示可以回收
  • 哪些对象可以作为 GC Root ?
    • 那些肯定不能当作垃圾来回收的对象

四种引用

image-20230322140044715

  1. 强引用

    只有所有 GC Roots 对象都不通过【强引用】引用该对象,该对象才能被垃圾回收

  2. 软引用(SoftReference)

    仅有软引用引用该对象时,在垃圾回收后,内存仍不足时会再次出发垃圾回收,回收软引用对象可以配合引用队列来释放软引用自身

  3. 弱引用(WeakReference)

    仅有弱引用引用该对象时,在垃圾回收时,无论内存是否充足,都会回收弱引用对象可以配合引用队列来释放弱引用自身

  4. 虚引用(PhantomReference)

    必须配合引用队列使用,主要配合 ByteBuffffer 使用,被引用对象回收时,会将虚引用入队,由 Reference Handler 线程调用虚引用相关方法释放直接内存

  5. 终结器引用(FinalReference)

    无需手动编码,但其内部配合引用队列使用,在垃圾回收时,终结器引用入队(被引用对象暂时没有被回收),再由 Finalizer 线程通过终结器引用找到被引用对象并调用它的 finalize方法,第二次 GC 时才能回收被引用对象

2.2 垃圾回收算法

算法一:标记清除算法

定义:

  • Mark Sweep,第一步用可达性标记算法标记可回收和不可回收对象。第二步,删除那些可回收的对象。

image-20230322143909247

优点:

  • 速度较快

缺点:

  • 容易产生内存碎片

算法二:标记整理算法

原理:

  • 第一步用可达性标记算法标记可回收和不可回收对象。第二步,可用对象向前移动,不可用的对象删除达到整理效果。

image-20230322144403896

好处:

  • 没有内存碎片

坏处:

  • 速度慢

算法三:复制算法

原理:

  • 第一步用可达性标记算法标记可回收和不可回收对象。From中的可用对象复制到TO内存块中,最后交换from和to的位置

image-20230322144856800

好处:

  • 不会有内存碎片

坏处:

  • 需要占用双倍内存空间

2.3 分代垃圾回收

image-20230322145333822

  • 对象首先分配在伊甸园区域

  • 新生代空间不足时,触发 minor gc,伊甸园和 from 存活的对象使用 copy 复制到 to 中,存活的对象年龄加 1并且交换 from to

  • minor gc 会引发 stop the world,暂停其它用户的线程,等垃圾回收结束,用户线程才恢复运行当对象寿命超过阈值时,会晋升至老年代,最大寿命是15(4bit)

  • 当老年代空间不足,会先尝试触发 minor gc,如果之后空间仍不足,那么触发 full gc,STW的时间更长

相关参数

堆初始大小 -Xms
堆最大大小 -Xmx 或 -XX:MaxHeapSize=size
新生代大小 -Xmn 或 (-XX:NewSize=size + -XX:MaxNewSize=size )
幸存区比例(动态) -XX:InitialSurvivorRatio=ratio 和 -XX:+UseAdaptiveSizePolicy
幸存区比例 -XX:SurvivorRatio=ratio
晋升阈值 -XX:MaxTenuringThreshold=threshold
晋升详情 -XX:+PrintTenuringDistribution
GC详情 -XX:+PrintGCDetails -verbose:gc
FullGC 前 MinorGC -XX:+ScavengeBeforeFullGC

2.4 垃圾回收器

  1. 串行

    -XX:+UseSerialGC = Serial + SerialOld

    image-20230322152839472

    • 单线程
    • 堆内存较小,适合个人电脑
  2. 吞吐量优先

    -XX:+UseParallelGC ~ -XX:+UseParallelOldGC

    -XX:GCTimeRatio=ratio

    -XX:MaxGCPauseMillis=ms

    -XX:ParallelGCThreads=n

    image-20230322152942559

    • 多线程
    • 堆内存较大,多核 cpu
    • 让单位时间内,STW 的时间最短 0.2 0.2 = 0.4,垃圾回收时间占比最低,这样就称吞吐量高
  3. 响应时间优先

    -XX:+UseConcMarkSweepGC ~ -XX:+UseParNewGC ~ SerialOld

    -XX:ParallelGCThreads=n ~ -XX:ConcGCThreads=threads

    -XX:CMSInitiatingOccupancyFraction=percent

    -XX:+CMSScavengeBeforeRemark

    image-20230322153241451

    • 多线程

    • 堆内存较大,多核 cpu

    • 尽可能让单次 STW 的时间最短 0.1 0.1 0.1 0.1 0.1 = 0.5

  4. G1

    定义:Garbage First

    • 2004 论文发布

    • 2009 JDK 6u14 体验

    • 2012 JDK 7u4 官方支持

    • 2017 JDK 9 默认

    适用场景

    • 同时注重吞吐量(Throughput)和低延迟(Low latency),默认的暂停目标是 200 ms

    • 超大堆内存,会将堆划分为多个大小相等的 Region

    • 整体上是 标记+整理 算法,两个区域之间是 复制 算法

    相关 JVM 参数

    -XX:+UseG1GC

    -XX:G1HeapRegionSize=size

    -XX:MaxGCPauseMillis=time

    G1 垃圾回收阶段

    image-20230322155624122

    1. Young Collection

      • 会 STW
      • image-20230322155748974
      • image-20230322155855560
      • image-20230322155911310
    2. Young Collection + CM

      • 在 Young GC 时会进行 GC Root 的初始标记

      • 老年代占用堆空间比例达到阈值时,进行并发标记(不会 STW),由下面的 JVM 参数决定

        image-20230322160103156

      • image-20230322160122063

    3. Mixed Collection

      • 会对 E、S、O 进行全面垃圾回收

        • 最终标记(Remark)会 STW
        • 拷贝存活(Evacuation)会 STW
      • -XX:MaxGCPauseMillis=ms

      • image-20230322160312398

Full GC

  • SerialGC

    • 新生代内存不足发生的垃圾收集 - minor gc
    • 老年代内存不足发生的垃圾收集 - full gc
  • ParallelGC

    • 新生代内存不足发生的垃圾收集 - minor gc

    • 老年代内存不足发生的垃圾收集 - full gc

  • CMS

    • 新生代内存不足发生的垃圾收集 - minor gc

    • 老年代内存不足

  • G1

    • 新生代内存不足发生的垃圾收集 - minor gc

    • 老年代内存不足

Young Collection 跨代引用

  • 新生代回收的跨代引用(老年代引用新生代)问题

    image-20230322161255491

  • 卡表与 Remembered Set

  • 在引用变更时通过 post-write barrier + dirty card queue

  • concurrent refinement threads 更新 Remembered Set

    image-20230322161347989

Remark

  • pre-write barrier + satb_mark_queue

    image-20230322161535125

JDK 8u20 字符串去重

  • 优点:节省大量内存

  • 缺点:略微多占用了 cpu 时间,新生代回收时间略微增加

  • -XX:+UseStringDeduplication

  • String s1 = new String("hello"); // char[]{'h','e','l','l','o'}
    String s2 = new String("hello"); // char[]{'h','e','l','l','o'}
    
  • 将所有新分配的字符串放入一个队列

  • 当新生代回收时,G1并发检查是否有字符串重复

  • 如果它们值一样,让它们引用同一个 char[]

  • 注意,与 String.intern() 不一样

    • String.intern() 关注的是字符串对象

    • 而字符串去重关注的是 char[]

    • 在 JVM 内部,使用了不同的字符串表

JDK 8u40 并发标记类卸载

所有对象都经过并发标记后,就能知道哪些类不再被使用,当一个类加载器的所有类都不再使用,则卸载它所加载的所有类 -XX:+ClassUnloadingWithConcurrentMark 默认启用

JDK 8u60 回收巨型对象

  • 一个对象大于 region 的一半时,称之为巨型对象

  • G1 不会对巨型对象进行拷贝

  • 回收时被优先考虑

  • G1 会跟踪老年代所有 incoming 引用,这样老年代 incoming 引用为0 的巨型对象就可以在新生代垃圾回收时处理掉

JDK 9 并发标记起始时间的调整

  • 并发标记必须在堆空间占满前完成,否则退化为 FullGC

  • JDK 9 之前需要使用 -XX:InitiatingHeapOccupancyPercent

  • JDK 9 可以动态调整

    • -XX:InitiatingHeapOccupancyPercent 用来设置初始值
    • 进行数据采样并动态调整
    • 总会添加一个安全的空档空间

JDK 9 更高效的回收

  • 250+增强
  • 180+bug修复
  • https://docs.oracle.com/en/java/javase/12/gctuning

2.5 垃圾回收调优

预备知识

  • 掌握 GC 相关的 VM 参数,会基本的空间调整
  • 掌握相关工具
  • 明白一点:调优跟应用、环境有关,没有放之四海而皆准的法则

调优领域

  • 内存
  • 锁竞争
  • cpu 占用
  • io

确定目标

  • 【低延迟】还是【高吞吐量】,选择合适的回收器
  • CMS,G1,ZGC
  • ParallelGC
  • Zing

最快的 GC 是不发生 GC

  • 查看 FullGC 前后的内存占用,考虑下面几个问题

    • 数据是不是太多?

      • resultSet = statement.executeQuery(“select * from 大表 limit n”)
    • 数据表示是否太臃肿?

      • 对象图
      • 对象大小 16 Integer 24 int 4
    • 是否存在内存泄漏?

      • static Map map
      • 第三方缓存实现

新生代调优

新生代的特点

  • 所有的 new 操作的内存分配非常廉价
    • TLAB thread-local allocation buffer
  • 死亡对象的回收代价是零
  • 大部分对象用过即死
  • Minor GC 的时间远远低于 Full GC

越大越好吗?

-Xmn 
Sets the initial and maximum size (in bytes) of the heap for the young generation (nursery).GC is performed in this region more often than in other regions. If the size for the young generation is too small, then a lot of minor garbage collections are performed. If the size is too large, then only full garbage collections are performed, which can take a long time to complete.Oracle recommends that you keep the size for the young generation greater than 25% and less than 50% of the overall heap size.
  • 新生代能容纳所有【并发量 * (请求-响应)】的数据

  • 幸存区大到能保留【当前活跃对象+需要晋升对象】

  • 晋升阈值配置得当,让长时间存活对象尽快晋升

-XX:MaxTenuringThreshold=threshold

-XX:+PrintTenuringDistribution

Desired survivor size 48286924 bytes, new threshold 10 (max 10)
- age 1: 28992024 bytes, 28992024 total
- age 2: 1366864 bytes, 30358888 total
- age 3: 1425912 bytes, 31784800 total
...

老年代调优

以 CMS 为例

  • CMS 的老年代内存越大越好

  • 先尝试不做调优,如果没有 Full GC 那么已经…,否则先尝试调优新生代

  • 观察发生 Full GC 时老年代内存占用,将老年代内存预设调大 1/4 ~ 1/3

    -XX:CMSInitiatingOccupancyFraction=percent

案例

  • 案例1 Full GC 和 Minor GC频繁

  • 案例2 请求高峰期发生 Full GC,单次暂停时间特别长 (CMS)

  • 案例3 老年代充裕情况下,发生 Full GC (CMS jdk1.7)

3.类加载与字节码技术

image-20230322174528353

3.1 类文件结构

一个简单的 HelloWorld.java

package org.example;

/**
 * HelloWorld 示例
 */
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("hello world");
    }
}

编译为 HelloWorld.class 在Idea中打开后是这个样子的:

image-20230322221345650

HelloWorld.class 文件转二进制文件:

image-20230322182734241

根据 JVM 规范,类文件结构如下

ClassFile {
	u4			magic; 									//魔数
	u2			minor_version; 			 				 //jdk的副版本号
	u2 			major_version:		    				//jdk的主版本号
	u2 			constant_pool_count:					//常量池数量
	cp_info 	constant_pool[constant_pool_count-1]//常量池具体信息
	u2   		access_flags:						    //访问权限标识
	u2      	this_class:								//类名
	u2 			super_class:                              //父类名
	u2       	interfaces_count:                         //接口数量
	u2       	interfaces[interfaces_count]//接口类信息
	u2 			fields_count:      						//字段数量
	field_info 	fields[fields_count]//字段信息
	u2 			methods_count:							//方法数量
	method_info  methods[methods_count]//方法具体信息
	u2 			attributes_count:						//属性数量
	attribute_info attributes[attributes_count]//属性具体信息
}
  1. 魔数

    • 0~3 字节,表示它是否是【class】类型的文件

    image-20230322182955796

  2. 版本

    • 4~7 字节,表示类的版本 00 34(52) 表示是 Java 8

    image-20230322183208061

  3. 常量池

    Constant Type Value
    CONSTANT_Class 7
    CONSTANT_Fieldref 9
    CONSTANT_Methodref 10
    CONSTANT_InterfaceMethodref 11
    CONSTANT_String 8
    CONSTANT_Integer 3
    CONSTANT_Float 4
    CONSTANT_Long 5
    CONSTANT_Double 6
    CONSTANT_NameAndType 12
    CONSTANT_Utf8 1
    CONSTANT_MethodHandle 15
    CONSTANT_MethodType 16
    CONSTANT_InvokeDynamic 18
    • 8~9 字节,表示常量池长度,00 22(34) 表示常量池有 #1~#33项,注意 #0 项不计入,也没有值

      image-20230322184540536

    • 第#1项 0a 表示常量池中的类型,因为0a(10) 对应上面表格中的CONSTANT_Methodref 类型,所以它表示 Method 信息,00 06 和 00 14(20) 表示它引用了常量池中 #6 和 #20 项来获得这个方法的【所属类】和【方法名】

      image-20230322185150186

    • 第#2项 09 表示一个 Field 信息,00 15(21)和 00 16(22) 表示它引用了常量池中 #21 和 # 22 项来获得这个成员变量的【所属类型】和【成员变量名】

      image-20230322190403165

    • 第#3项 08 表示一个字符串常量名称,00 17(23)表示它引用了常量池中 #23 项

      image-20230322190647360

    • 第#4项 0a 表示一个 Method 信息,00 18(24) 和 00 19(25)表示它引用了常量池中 #24 和 #25项来获得这个方法的【所属类】和【方法名】

      image-20230322190807282

    • 第#5项 07 表示一个 Class 信息,00 1a(26) 表示它引用了常量池中 #26 项

      image-20230322191054297

    • 第#6项 07 表示一个 Class 信息,00 1b(27) 表示它引用了常量池中 #27 项

      image-20230322191226622

    • 第#7项 01 表示一个 utf8 串,00 06 表示长度,3c 69 6e 69 74 3e 是【 】

      image-20230322191353244

    • 第#8项 01 表示一个 utf8 串,00 03 表示长度,28 29 56 是【()V】其实就是表示无参、无返回值

      image-20230322191903217

    • 第#9项 01 表示一个 utf8 串,00 04 表示长度,43 6f 64 65 是【Code】

      image-20230322191958863

    • 第#10项 01 表示一个 utf8 串,00 0f(15) 表示长度,4c 69 6e 65 4e 75 6d 62 65 72 54 61 62 6c 65是【LineNumberTable】

      image-20230322192123600

    • 第#11项 01 表示一个 utf8 串,00 12(18) 表示长度,4c 6f 63 61 6c 56 61 72 69 61 62 6c 65 54 61 62 6c 65是【LocalVariableTable】

      image-20230322192333501

    • 第#12项 01 表示一个 utf8 串,00 04 表示长度,74 68 69 73 是【this】

      image-20230322192553931

    • 第#13项 01 表示一个 utf8 串,00 18(24) 表示长度,是【Lorg/example/HeloWorld;】

      image-20230322192722291

    • 第#14项 01 表示一个 utf8 串,00 04 表示长度,6D 61 69 6E是【main】

      image-20230322193142581

    • 第#15项 01 表示一个 utf8 串,00 16(22) 表示长度,是【([Ljava/lang/String;)V】其实就是参数为字符串数组,无返回值

      image-20230322193348562

    • 第#16项 01 表示一个 utf8 串,00 04 表示长度,是【args】

      image-20230322193635966

    • 第#17项 01 表示一个 utf8 串,00 13(19) 表示长度,是【[Ljava/lang/String;】

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GHE5d5zF-1679980231499)(https://pic-1307534554.cos.ap-chongqing.myqcloud.com/img/image-20230322193804022.png)]

    • 第#18项 01 表示一个 utf8 串,00 0A(10) 表示长度,是【SourceFile】

      image-20230322194200172

    • 第#19项 01 表示一个 utf8 串,00 0f(15) 表示长度,是【HelloWorld.java】

      image-20230322194729624

    • 第#20项0c 表示一个 【名+类型】,00 07 00 08 引用了常量池中 #7 #8 两项

      image-20230322194910846

    • 第#21项 07 表示一个 Class 信息,00 1c(28) 引用了常量池中 #28 项

      image-20230322195113486

    • 第#22项 0c 表示一个【名+类型】00 1d(29) 00 1e (30)引用了常量池中 #29 #30两项

      image-20230322195215374

    • 第#23项 01 表示一个 utf8 串,00 0b(11) 表示长度,是【hello world】

      image-20230322195444628

    • 第#24项 07 表示一个 Class 信息,00 1F(31) 引用了常量池中 #31 项

      image-20230322195606493

    • 第#25项 0c 表示一个【名+类型】00 20(32) 00 21(33)引用了常量池中 #32 #33 两项

      image-20230322195830948

    • 第#26项 01 表示一个 utf8 串,00 16(22) 表示长度,是【org/example/HelloWorld】

      image-20230322200020321

    • 第#27项 01 表示一个 utf8 串,00 10(16) 表示长度,是【java/lang/Object】

      image-20230322200337409

    • 第#28项 01 表示一个 utf8 串,00 10(16) 表示长度,是【java/lang/System】

      image-20230322200448928

    • 第#29项 01 表示一个 utf8 串,00 03 表示长度,是【out】

      image-20230322200551345

    • 第#30项 01 表示一个 utf8 串,00 15(21) 表示长度,是【Ljava/io/PrintStream;】

      image-20230322200648865

    • 第#31项 01 表示一个 utf8 串,00 13(19) 表示长度,是【java/io/PrintStream】

      image-20230322200842783

    • 第#32项 01 表示一个 utf8 串,00 07 表示长度,是【println】

      image-20230322201004865

    • 第#33项 01 表示一个 utf8 串,00 15(21) 表示长度,是【(Ljava/lang/String;)V】

      image-20230322201114055

  4. 访问标识与继承信息

    访问标识符:

    00 21 表示等价于【0x0001 + 0x0020】表示该Class是一个公共的类【public class】

    image-20230322210828403

    Flag Name Value Interpretation
    ACC_PUBLIC 0x0001 Declared public ; may be accessed from outside its package.
    ACC_FINAL 0x0010 Declared final ; no subclasses allowed.
    ACC_SUPER 0x0020 Treat superclass methods specially when invoked by the invokespecial instruction.
    ACC_INTERFACE 0x0200 Is an interface, not a class.
    ACC_ABSTRACT 0x0400 Declared abstract ; must not be instantiated.
    ACC_SYNTHETIC 0x1000 Declared synthetic; not present in the source code.
    ACC_ANNOTATION 0x2000 Declared as an annotation type.
    ACC_ENUM 0x4000 Declared as an enum type.

    类名:

    00 05 表示根据常量池中 #5 找到本类全限定名

    image-20230322211439671

    父类名:

    00 06 表示根据常量池中 #6 找到父类全限定名

    image-20230322211531895

    接口数量:

    00 00 表示接口的数量,本类为 0

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qEDlWg4V-1679980231512)(https://pic-1307534554.cos.ap-chongqing.myqcloud.com/img/image-20230322211704581.png)]

  5. Field 信息

    00 00 表示成员变量数量,本类为 0

    image-20230322212055037

    FieldType Type Interpretation
    B byte signed byte
    C char Unicode character code point in the Basic Multilingual Plane,encoded with UTF-16
    D double double-precision flfloating-point value
    F float single-precision flfloating-point value
    I int integer
    J long long integer
    L ClassName ; reference an instance of class ClassName
    S short signed short
    Z boolean true or false
    [ reference one array dimension
  6. Method 信息

    方法数量:

    00 02 表示方法数量,本类为 2

    image-20230322212741225

    方法信息:

    一个方法由 访问修饰符,名称,参数描述,方法属性数量,方法属性组成:

    方法一:

    • 访问修饰符: 00 01 ,本类中是 【ACC_PUBLIC 】public

      image-20230322214356199

    • 方法名称:00 07 【】 代表引用了常量池 #07 项作为方法名称

      image-20230322214706845

    • 参数描述:00 08 【()V】代表引用了常量池 #08 项作为方法参数描述

      image-20230322214944575

    • 方法属性数量:00 01 代表方法属性数量,本方法是 1

      image-20230322215324390

    • 代表方法属性:

      • 00 09 表示引用了常量池 #09 项,发现是【Code】属性

        image-20230322215439589

      • 00 00 00 2f 表示此属性的长度是 47

        image-20230322215556222

      • 00 01 表示【操作数栈】最大深度 本方法是 1

        image-20230322215732004

      • 00 01 表示【局部变量表】最大槽(slot)数 本方法是1

        image-20230322220215816

      • 00 00 00 05 表示字节码长度,本例是 5

        image-20230322220229046

      • 2A B7 00 01 B1 是字节码指令

        image-20230322220254073

      • 00 00 00 02 表示方法细节属性数量,本例是 2

        image-20230322220750842

      • 00 0A 表示引用了常量池 #10 项,发现是【LineNumberTable】属性

        image-20230322220824191

        • 00 00 00 06 表示此属性的总长度,本例是 6

          image-20230322220934196

        • 00 01 表示【LineNumberTable】长度

          image-20230322221004311

        • 00 00 表示【字节码】行号 00 09 表示【java 源码】行号

          image-20230322221029665

          image-20230322221038072

      • 00 0b 表示引用了常量池 #11 项,发现是【LocalVariableTable】属性

        image-20230322221438278

        • 00 00 00 0c 表示此属性的总长度,本例是 12

          image-20230322222009659

        • 00 01 表示【LocalVariableTable】长度

          image-20230322222019094

        • 00 00 表示局部变量生命周期开始,相对于字节码的偏移量

          image-20230322222027355

        • 00 05 表示局部变量覆盖的范围长度

          image-20230322222037416

        • 00 0c 表示局部变量名称,本例引用了常量池 #12 项,是【this】

          image-20230322222044734

        • 00 0d 表示局部变量的类型,本例引用了常量池 #13 项,是【Lorg/example/HeloWorld;】

          image-20230322222052895

        • 00 00 表示局部变量占有的槽位(slot)编号,本例是 0

          image-20230322222104380

    方法二:

    • 访问修饰符:00 09 ,本类中是【00 01 + 00 08】 public static
    • 名称:00 0E 代表引用了常量池 #14 项作为方法名称【main】
    • 参数描述:00 0F 代表引用了常量池 #15 项作为方法参数描述【([Ljava/lang/String;)V】
    • 方法属性数量: 00 01 代表方法属性数量,本方法是 1
    • 方法属性组成:
      • 00 09 表示引用了常量池 #09 项,发现是【Code】属性
      • 00 00 00 37 表示此属性的长度是 55
      • 00 02 表示【操作数栈】最大深度
      • 00 01 表示【局部变量表】最大槽(slot)数
      • 00 00 00 09 表示字节码长度,本例是 9
      • b2 00 02 12 03 b6 00 04 b1 是字节码指令
      • 00 00 00 02 表示方法细节属性数量,本例是 2
      • 00 0a 表示引用了常量池 #10 项,发现是【LineNumberTable】属性
        • 00 00 00 0a 表示此属性的总长度,本例是 10
        • 00 02 表示【LineNumberTable】长度
        • 00 00 表示【字节码】行号 00 0B 表示【java 源码】行号
        • 00 08 表示【字节码】行号 00 0C 表示【java 源码】行号
      • 00 0b 表示引用了常量池 #11 项,发现是【LocalVariableTable】属性
        • 00 00 00 0c 表示此属性的总长度,本例是 12
        • 00 01 表示【LocalVariableTable】长度
        • 00 00 表示局部变量生命周期开始,相对于字节码的偏移量
        • 00 09 表示局部变量覆盖的范围长度
        • 00 10 表示局部变量名称,本例引用了常量池 #16 项,是【args】
        • 00 11 表示局部变量的类型,本例引用了常量池 #17 项,是【[Ljava/lang/String;】
        • 00 00 表示局部变量占有的槽位(slot)编号,本例是 0
  7. 附加属性

    • 00 01 表示附加属性数量

    • 00 12 表示引用了常量池 #18 项,即【SourceFile】

    • 00 00 00 02 表示此属性的长度

    • 00 13 表示引用了常量池 #19 项,即【HelloWorld.java】

      image-20230322224439774

3.2 字节码指令

3.2.1 入门

接着上一节,研究一下两组字节码指令,一个是

public org.example.HeloWorld();构造方法的字节码指令: 2A B7 00 01 B1

  1. 2a => aload_0 加载 slot 0 的局部变量,即 this,做为下面的 invokespecial 构造方法调用的参数
  2. b7 => invokespecial 预备调用构造方法,哪个方法呢?
  3. 00 01 引用常量池中 #1 项,即【 Method java/lang/Object.“”
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JVM完整笔记 的相关文章

随机推荐

  • Linux中用gdb 查看代码堆栈的信息

    core dump 一般是在segmentation fault 段错误 的情况下产生的文件 需要通过ulimit来设置才会得到的 调试的话输入 gdb filename core filename就是产生core文件的可执行文件 core
  • Windows server 远程桌面连接用户不活动自动注销配置

    Windows server 远程桌面连接用户不活动自动注销配置
  • VMware三种网络模式配置详解。

    VMware网络 本质上不会配置VMware网络 是因为对其不够熟悉 VMware提供了三种可靠的网络模式 我相信只要了解了他们的区别 配置起来应该是如鱼得水的 如果还在为你的虚拟机上不了网而发愁 那么请跟随我的脚步 让我们来探究他们到底有
  • Flowable 用户问题

    Flowable用户和系统用户问题 springboot集成flowable modeler 实现免登 权限管理 Flowable引擎使用统一权限管理
  • 《信号与系统》解读 第4章 连续信号的离散化:采样与采样定理、奈奎斯特准则、脉冲编码调制PCM

    前言 如果你对采样定理和奈奎斯特准则一知半解 本文将给茅塞顿开 如果你对为什么采样频率必须大于等于原始信号的带宽的2倍 本文将给你答案 目录 1 信号与系统的模型 2 为什么要对连续信号离散化 3 连续信号离散化 采样 的模型 3 1 采样
  • 使用Hexo搭建博客并部署到Github

    一 博客环境搭建 Hexo 是一个静态博客框架 基于 Node js 将 Markdown 文章通过渲染引擎 生成一个静态网页 再结合 Git 命令 ssh Hexo 是一个快速 简洁且高效的博客框架 Hexo 使用 Markdown 或其
  • 深入理解 Java 垃圾回收机制

    一 垃圾回收机制的意义 java 语言中一个显著的特点就是引入了java回收机制 是c 程序员最头疼的内存管理的问题迎刃而解 它使得java程序员在编写程序的时候不在考虑内存管理 由于有个垃圾回收机制 java中的额对象不在有 作用域 的概
  • 解决Excel打开UTF-8编码的CSV文件乱码的问题

    解决Excel打开UTF 8编码的CSV文件乱码的问题 引用自 https en wikipedia org wiki Comma separated values CSV formats are not limited to a part
  • 透彻解析Qt入门级项目——贪吃蛇游戏

    1 项目目的 本项目主要通过编写贪吃蛇游戏来学习 熟悉Qt中封装的类 2 编译环境 VS2019 Qt5 9 3 功能实现 主要实现下面所列基本功能 控制贪吃蛇吃食物 表示蛇 控制贪吃蛇上下左右移动 控制食物的随机分配 控制蛇的增长 暂停游
  • MATLAB上关于复数矩阵的转置与共轭知识详解

    首选随机用A randn 2 2 randn 2 2 1i生成一个二维的复数矩阵 执行后结果如下 A 3 5784 0 7254i 1 3499 0 7147i 2 7694 0 0631i 3 0349 0 2050i 执行 A 得到如下
  • DRM(Direct Rendering Manager)学习简介

    DRM DRM是Linux目前主流的图形显示框架 相比FB架构 DRM更能适应当前日益更新的显示硬件 比如FB原生不支持多层合成 不支持VSYNC 不支持DMA BUF 不支持异步更新 不支持fence机制等等 而这些功能DRM原生都支持
  • 堆栈内存地址

    catalog 有两个信息 B gt A 即栈空间地址 gt 堆空间地址 比如 你的临时变量的地址 是 0x666666 全局变量的地址0x222222 总之 栈空间地址 gt 堆空间地址 栈空间 依据申请变量的次序 地址是 从B地址开始递
  • Ubuntu 20.04及pytorch安装

    转载自 https blog csdn net xjgao75 article details 105869699 侵权可删 只作为笔记 Ubuntu 20 04安装及pytorch深度学习框架配置 Ubuntu 20 04安装 启动U盘制
  • Unity-NGUI物体扫光特效

    先看效果 最近搞了多张卡牌合成新卡牌后 新卡牌在放回背包后闪亮一下 没搞过Shader 但前人已经造好轮子 研究后 记录下 方便以后使用 效果如下 文章详情请移步 Unity NGUI物体扫光特效
  • leecode刷算法C++

    共11道数组 字符串相关题目 大多数为简单 我分为三类 分别为翻转问题 元素重复问题和其它类 使用C 翻转问题包括轮转数组 翻转字符串 整数翻转这3道题 元素重复题目包括删除有序数组中的重复项 存在重复元素 只出现一次的数字 字符串中的第一
  • 详解c# Emit技术

    我们常常有一个应用场景 由我们的C 代码 动态生成一个EXE 其应用场景可以非常多 比如软件授权 可以输入授权信息后 生成一个授权的DLL等 那如何实现这个功能呢 就要提到一个技术Emit 1 Emit概述 Emit 可以称为发出或者产生
  • 计算机网络笔记(一)

    什么是计算机网络 什么是计算机网络 计算机网络就是互连 互联互通 的 自治 无主从关系 的计算机集合 那么 距离远 数据大如何保证互连 通过交换网络互连主机 什么 是 Internet 组成 计算机设备 通信链路 分组交换 数据包转发分组
  • linux unix域socket_python3从零学习-5.8.1、socket—底层网络接口

    源代码 Lib socket py 这个模块提供了访问BSD 套接字 的接口 在所有现代Unix系统 Windows macOS和其他一些平台上可用 这个Python接口是用Python的面向对象风格对Unix系统调用和套接字库接口的直译
  • Kaldi-MFCC模块源码主流程分析

    那么趁着这个机会 研究一下kaldi源码中MFCC部分的内容 不说废话 我们从 compute mfcc feats cc开始讲解 这里是个main函数 需要携带参数 具体使用样例如下 1 compute mfcc feats 其实看到这里
  • JVM完整笔记

    这是我在看课程 黑马程序员JVM完整教程 过程中记的笔记 我觉得该课程总时不长 并且理论 实战是一个入门JVM的好课程 若你看完该课程可以看下面几个参看书进一步深入了解JVM 深入理解Java虚拟机 第二版 实战Java虚拟机 深入JAVA