Java二进制指令码大全及解析

1、字节码指令简介

Java虚拟机的指令由一个字节长度的、代表着某种特定操作含义的数字(称为操作码,Opcode)以及跟随其后的零至多个代表此操作所需的参数(称为操作数,Operand)构成。由于Java虚拟机采用面向操作数栈而不是面向寄存器的架构,所以大多数指令都不包含操作数,只有一个操作码,指令参数都存放在操作数栈中。

2、字节码与数据类型

  • 在Java虚拟机的指令集中,大多数指令都包含其操作所对应的数据类型信息。举个例子,iload指令用于从局部变量表中加载int型的数据到操作数栈中,而fload指令加载的则是float类型的数据。这两条指令的操作在虚拟机内部可能会是由同一段代码来实现的,但在Class文件中它们必须拥有各自独立的操作码。
  • 对于大部分与数据类型相关的字节码指令,它们的操作码助记符中都有特殊的字符来表明专门为哪种数据类型服务:i代表对int类型的数据操作,l代表long,s代表short,b代表byte,c代表char,f代表float,d代表double,a代表reference。也有一些指令的助记符中没有明确指明操作类型的字母,例如arraylength指令,它没有代表数据类型的特殊字符,但操作数永远只能是一个数组类型的对象。还有另外一些指令,例如无条件跳转指令goto则是与数据类型无关的指令。

3、加载和存储指令

3.1、常量入栈指令

操作码操作数描述(操作数栈)对应代码
aconst_nullnull值入栈Object a = null;
iconst_<n>n可以为1-5表示值n(int)入栈,也可以为m1表示-1(int)入栈int a = 1;
lconst_<n>n可以为0,1表示值n(long)入栈long a = 0;
fconst_<n>n可以为0-2表示值n(float)入栈float a = 0;
dconst_<n>n可以为0、1表示值n(double)入栈dobule a = 0;
bipushvaluebytevaluebyte值带符号扩展成int值入栈。 此时int 值的范围是 -128 ~ 127int a = 10;
sipushvaluebyte1 valuebyte2(valuebyte1 << 8) | valuebyte2 值带符号扩展成int值入栈。 此时int 值的范围是 -32768~32767int a = 1000;
ldcindexbyte1常量池中的常量值(int, float, string reference, object reference)入栈。int a = Integer.MAX_VALUE;
ldc_windexbyte1 indexbyte2ldc和ldc_w指令用于访问运行时常量池中除了double和long类型的值(但是包括String类型实例)。当使用的运行时常量池的项目过多时(多余256个,一个字节能表示的范围),需要使用ldc_w来代替。
ldc2_windexbyte1 indexbyte2常量池中常量(long, double)入栈。double a = Double.MAX_VALUE;

3.2、局部变量值转移到栈指令

操作码操作数描述(操作数栈)对应代码
aload_<n>从局部变量n中装载 引用类型 值入栈。n的取值为0-3使用局部变量的前四个参数(且使用的类型是引用类型)
aloadindexbyte从局部变量indexbyte中装载 引用类型 值入栈。使用局部变量的后几个参数(且使用的类型是引用类型)
iload_<n>从局部变量n中装载 int 值入栈。n的取值为0-3使用局部变量的前四个参数(且使用的类型是int类型)
iloadindexbyte从局部变量indexbyte中装载 int 值入栈。使用局部变量的后几个参数(且使用的类型是int类型)
fload_<n>从局部变量n中装载 float 入栈。n的取值为0-3使用局部变量的前四个参数(且使用的类型是float类型)
floadindexbyte从局部变量indexbyte中装载 float 值入栈。使用局部变量的后几个参数(且使用的类型是float类型)
dload_<n>从局部变量n中装载 double 入栈。n的取值为0-3使用局部变量的前四个参数(且使用的类型是double类型)
dloadindexbyte从局部变量indexbyte中装载 double 值入栈。使用局部变量的后几个参数(且使用的类型是double类型)
aaload从引用类型数组中装载指定项的值。使用局部变量的参数(且使用的类型是引用类型数组)
iaload从int类型数组中装载指定项的值。使用局部变量的参数(且使用的类型是int类型数组)
laload从long类型数组中装载指定项的值。
faload从float类型数组中装载指定项的值。
daload从double类型数组中装载指定项的值。
baload从boolean类型数组或byte类型数组中装载指定项的值(先转换为int类型值,后压栈)。
caload从char类型数组中装载指定项的值(先转换为int类型值,后压栈)。
saload从short类型数组中装载指定项的值(先转换为int类型值,后压栈)。

3.3、栈顶值保存到局部变量表

操作码操作数描述(操作数栈)
astoreindexbyte将栈顶引用类型值保存到局部变量indexbyte中。
astore_<n>将栈顶引用类型值保存到局部变量n中。n取值为0-3
istoreindexbyte将栈顶int类型值保存到局部变量indexbyte中。
istore_<n>将栈顶int类型值保存到局部变量n中。n取值为0-3
(wide)lstoreindexbyte将栈顶long类型值保存到局部变量indexbyte中。
lstroe_<n>将栈顶long类型值保存到局部变量n中。n取值为0-3
(wide)fstoreindexbyte将栈顶float类型值保存到局部变量indexbyte中。
fstore_<n>将栈顶float类型值保存到局部变量n中。n取值为0-3
(wide)dstoreindexbyte将栈顶double类型值保存到局部变量indexbyte中。
dstore_<n>将栈顶double类型值保存到局部变量n中。n取值为0-3
aastore将栈顶引用类型值保存到指定引用类型数组的指定项。
iastore将栈顶int类型值保存到指定int类型数组的指定项。
lastore将栈顶long类型值保存到指定long类型数组的指定项。
fastore将栈顶float类型值保存到指定float类型数组的指定项。
dastore将栈顶double类型值保存到指定double类型数组的指定项。
bastroe将栈顶boolean类型值或byte类型值保存到指定boolean类型数组或byte类型数组的指定项。
castore将栈顶char类型值保存到指定char类型数组的指定项。
sastore将栈顶short类型值保存到指定short类型数组的指定项。

3.4、对象操作指令

操作码操作数描述(操作数栈)
newindexbyte1 indexbyte2创建新的对象实例。
checkcastindexbyte1indexbyte
instanceofindexbyte1 indexbyte2判断类型。
getfieldindexbyte1 indexbyte2获取对象字段的值。
putfieldindexbyte1 indexbyte2给对象字段赋值。
getstaticindexbyte1 indexbyte2获取静态字段的值。
putstaticindexbyte1 indexbyte2给静态字段赋值。

3.5、方法调用指令

操作码操作数描述(操作数栈)
invokespecialindexbyte1 indexbyte2编译时方法绑定调用方法。
invokevirtualindexbyte1 indexbyte2运行时方法绑定调用方法。
invokestaticindexbyte1 indexbyte2调用静态方法。
invokeinterfaceindexbyte1 indexbyte2 count 0调用接口方法。

3.6、线程同步指令

操作码操作数描述(操作数栈)
monitorenter进入并获得对象监视器。
monitorexit释放并退出对象监视器。

3.7、运算指令

算术指令用于对两个操作数栈上的值进行某种特定运算,并把结果重新存入到操作栈顶。大体上运算指令可以分为两种:对整型数据进行运算的指令与对浮点型数据进行运算的指令。整数与浮点数的算术指令在溢出和被零除的时候也有各自不同的行为表现。无论是哪种算术指令,均是使用Java虚拟机的算术类型来进行计算的,换句话说是不存在直接支持byte、short、char和boolean类型的算术指令,对于上述几种数据的运算,应使用操作int类型的指令代替。所有的算术指令包括:

  • 加法指令:iadd、ladd、fadd、dadd
  • 减法指令:isub、lsub、fsub、dsub
  • 乘法指令:imul、lmul、fmul、dmul
  • 除法指令:idiv、ldiv、fdiv、ddiv
  • 求余指令:irem、lrem、frem、drem
  • 取反指令:ineg、lneg、fneg、dneg
  • 位移指令:ishl、ishr、iushr、lshl、lshr、lushr
  • 按位或指令:ior、lor
  • 按位与指令:iand、land
  • 按位异或指令:ixor、lxor
  • 局部变量自增指令:iinc
  • 比较指令:dcmpg、dcmpl、fcmpg、fcmpl、lcmp

4、字节码文件解读

4.1、源java代码

public class ByteCodeTest {
    
    private static final String X = "xxx";
    
    public String test(String a) {
        return X + a;
    }
}

4.2、编译之后的class文件

public class ByteCodeTest {
    private static final String X = "xxx";

    public ByteCodeTest() {
    }

    public String test(String a) {
        return "xxx" + a;
    }
}

4.3、使用javap命令计算字节码指令

使用以下命令

javap -verbose -p ByteCodeTest.class 

结果代码清单如下

Classfile /Users/huxiaolong/Desktop/person/sync/target/classes/ByteCodeTest.class
  Last modified 2022-11-5; size 610 bytes
  MD5 checksum 299ef8b31b427e36fb1f58c12c155f32
  Compiled from "ByteCodeTest.java"
public class ByteCodeTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #8.#24         // java/lang/Object."<init>":()V
   #2 = Class              #25            // java/lang/StringBuilder
   #3 = Methodref          #2.#24         // java/lang/StringBuilder."<init>":()V
   #4 = Class              #26            // ByteCodeTest
   #5 = String             #27            // xxx
   #6 = Methodref          #2.#28         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #7 = Methodref          #2.#29         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #8 = Class              #30            // java/lang/Object
   #9 = Utf8               X
  #10 = Utf8               Ljava/lang/String;
  #11 = Utf8               ConstantValue
  #12 = Utf8               <init>
  #13 = Utf8               ()V
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               this
  #18 = Utf8               LByteCodeTest;
  #19 = Utf8               test
  #20 = Utf8               (Ljava/lang/String;)Ljava/lang/String;
  #21 = Utf8               a
  #22 = Utf8               SourceFile
  #23 = Utf8               ByteCodeTest.java
  #24 = NameAndType        #12:#13        // "<init>":()V
  #25 = Utf8               java/lang/StringBuilder
  #26 = Utf8               ByteCodeTest
  #27 = Utf8               xxx
  #28 = NameAndType        #31:#32        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #29 = NameAndType        #33:#34        // toString:()Ljava/lang/String;
  #30 = Utf8               java/lang/Object
  #31 = Utf8               append
  #32 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #33 = Utf8               toString
  #34 = Utf8               ()Ljava/lang/String;
{
  private static final java.lang.String X;
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
    ConstantValue: String xxx

  public ByteCodeTest();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 11: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LByteCodeTest;

  public java.lang.String test(java.lang.String);
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC
    Code:
      stack=2, locals=2, args_size=2
         0: new           #2                  // class java/lang/StringBuilder
         3: dup
         4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
         7: ldc           #5                  // String xxx
         9: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        12: aload_1
        13: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        16: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        19: areturn
      LineNumberTable:
        line 16: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      20     0  this   LByteCodeTest;
            0      20     1     a   Ljava/lang/String;
}
SourceFile: "ByteCodeTest.java"

4.3.1 类的基本信息

Classfile /Users/huxiaolong/Desktop/person/sync/target/classes/ByteCodeTest.class
  Last modified 2022-11-5; size 610 bytes
  MD5 checksum 299ef8b31b427e36fb1f58c12c155f32
  Compiled from "ByteCodeTest.java"
public class ByteCodeTest
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER

前三行描述了.class文件的所在路径、最后修改时间、校验和。
后三行描述了类的基本信息。

  • minor:此版本号
  • major:主版本号
  • flags:描述了类的相关修饰符,ACC_PUBLIC表示类是public公开类型,flag可以有以下取值:
    访问标志

4.3.2 常量池

Constant pool:
   #1 = Methodref          #8.#24         // java/lang/Object."<init>":()V
   #2 = Class              #25            // java/lang/StringBuilder
   #3 = Methodref          #2.#24         // java/lang/StringBuilder."<init>":()V
   #4 = Class              #26            // ByteCodeTest
   #5 = String             #27            // xxx
   #6 = Methodref          #2.#28         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #7 = Methodref          #2.#29         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #8 = Class              #30            // java/lang/Object
   #9 = Utf8               X
  #10 = Utf8               Ljava/lang/String;
  #11 = Utf8               ConstantValue
  #12 = Utf8               <init>
  #13 = Utf8               ()V
  #14 = Utf8               Code
  #15 = Utf8               LineNumberTable
  #16 = Utf8               LocalVariableTable
  #17 = Utf8               this
  #18 = Utf8               LByteCodeTest;
  #19 = Utf8               test
  #20 = Utf8               (Ljava/lang/String;)Ljava/lang/String;
  #21 = Utf8               a
  #22 = Utf8               SourceFile
  #23 = Utf8               ByteCodeTest.java
  #24 = NameAndType        #12:#13        // "<init>":()V
  #25 = Utf8               java/lang/StringBuilder
  #26 = Utf8               ByteCodeTest
  #27 = Utf8               xxx
  #28 = NameAndType        #31:#32        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #29 = NameAndType        #33:#34        // toString:()Ljava/lang/String;
  #30 = Utf8               java/lang/Object
  #31 = Utf8               append
  #32 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #33 = Utf8               toString
  #34 = Utf8               ()Ljava/lang/String;

这一部分就是类中使用到的常量,每个常量都有编号,以#号开头,font color=“#660000”>=号后面就是常量类型,类型后就是常量的值。每个常量都由三部分组成——编号、类型、常量值组成。

例如#1常量的类型是Methodref,表示这个常量描述的是方法相关的信息;#4变量的类型是Class,表示这个常量描述的是类相关的信息。

注意:一个常量还可以引用其他常量,例如#1就引用了#8#24常量的值。而最终#1得到的值就是 java/lang/Object.“”:\()V

4.3.3、包含方法与类的私用量

{
  // X 静态常量
  private static final java.lang.String X;
  	// 描述 包括访问权限 类型
    descriptor: Ljava/lang/String;
    flags: ACC_PRIVATE, ACC_STATIC, ACC_FINAL
    ConstantValue: String xxx

// 。。。。


public java.lang.String test(java.lang.String);
	// descriptor:精简的描述了方法的入参和出参。
    descriptor: (Ljava/lang/String;)Ljava/lang/String;
    flags: ACC_PUBLIC
    // 方法的代码
    Code:
     // locals 是该方法中的本地变量有多少个
     // stack 是方法在执行过程中,操作数栈中最大深度
     // args_size 是参数数量
      stack=2, locals=2, args_size=2
         0: new           #2                  // class java/lang/StringBuilder
         3: dup
         4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
         7: ldc           #5                  // String xxx
         9: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        12: aload_1
        13: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        16: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        19: areturn
      // 代码对照表 line 16: 0为例,表示源.java文件的第16行代码,对应的是0偏移量
      LineNumberTable:
        line 16: 0
      // 本地变量表
      LocalVariableTable:
        // start:为这个变量可见的起始偏移位置,它的值必须是在 Code 中存在的偏移量值。(也就是这个变量第一次出现在哪个偏移量)
        // length:为该变量的有效长度,描述的是该变量在方法中的有效范围大小。
        // slot:为变量在 local variable(本地变量表) 中的位置,这可以帮助我们在指令中确定对应的变量。
        // Name 则是变量名
        // Signature 为该变量的类型
        Start  Length  Slot  Name   Signature
            0      20     0  this   LByteCodeTest;
            0      20     1     a   Ljava/lang/String;

 // ....
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 虚拟机面试题全面解析,《深入理解Java虚拟机》干货版,自己总结,希望能够帮助大家,免费下载~什么是类加载机制? 虚拟机和物理机的区别是什么? 运行时栈帧结构 Java方法调用 什么是方法调用? Java的方法调用,有什么特殊之处? Java虛拟机调用字节码指令有哪些? 虚拟机是如何执行方法里面的字节码指令的? 解释执行 基于栈的指令集和基于寄存器的指令集 什么是基于栈的指令集? 什么是基于寄存器的指令集? 基于栈的指令集的优缺点? Javac编译过程分为哪些步骤? 什么是即时编译器? 解释器和编译器 为什么要采用分层编译? 分层编译器有哪些层次? 编译对象与触发条件 热点代码有哪些? 如何判断一段代码是不是热点代码? Hotspot虚拟机使用第二种,有两个计数器: 方法调用计数器统计方法 有哪些经典的优化技术(即时编译器)? 公共子表达式消除 数组边界检查消除 方法内联 逃逸分析 如果对象不会逃逸到方法或线程外,可以做什么优化? Java与C/C++的编译器对比 物理机如何处理并发问题? Java内存模型 什么是Java内存模型? Java内存模型的目标? 主内存与工作内存 内存间的交互操作 原子性、可见性、有序性 volatile 什么是 volatile? 为什么基于 volatile变量的运算在并发下不一定是安全的? 为什么使用 volatile? 并发与线程 并发与线程的关系? 什么是线程? 实现线程有哪些方式? Java线程的实现 Java线程调度 什么是线程调度? 线程调度有哪些方法? 线程安全的定义? Java语言操作的共享数据,包括哪些? 不可变 如何实现线程安全? 阻塞同步(互斥同步) 非阻塞同步 锁优化是在DK的那个版本? 为什么要提出自旋锁? 自旋锁的原理? 自旋的缺点? 什么是自适应自旋? 锁消除 锁粗化 轻量级锁 偏向锁 JDK是什么? JDK是用于支持Java程序开发的最小环境。 1.Java程序设计语言 2.Java虚拟机 3. Java ap类库 JRE是什么? JRE是支持Java程序运行的标准环境。 1. Java SE aPi子集 2.Java虚拟机 Java历史版本的特性? Java∨ ersion se5.0 引入泛型; 增强循环,可以使用迭代方式; 自动装箱与自动拆箱; 类型安全的枚举 ·可变参数; 静态引入 元数据(注解); 引入 Instrumentation Java∨ ersion se6 支持脚本语言 引入JDBC40API; 引入 Java Compiler API; 可插拔注解; 增加对 Native PKi( Public Key Infrastructure)、 Java gss( Generic Security Service) Kerberos和 LDAP(Lightweight Directory Access Protocol的支持; 继承 Web services 做了很多优化。 Java∨ ersion se7 switch语句块中允许以字符串作为分支条件; 在创建泛型对象时应用类型推断 ·在一个语句块中捕获多种异常; ·支持动态语言; 支持try-with- resources 引入 Java nio.2开发包; ·数值类型可以用2进制字符串表示,并且可以在字符串表示中添加下划线; 钻石型语法; nu值的自动处理。 Java 8 函数式接口 Lambda表达式 接口的增强 运行时数据区域包括哪些? 1.程序计数器 2.Java虚拟机栈 3.本地方法栈 4.Java堆 5.方法区 6.运行时常量池 7.直接内存 程序计数器(线程私有) 程序计数器( Program Counter Register)是一块较小的内存空间,可以看作是当前线程所 执行字节码的行号指示器。分支、循环、跳转、异常处理、线程恢复等基础功能都需要依赖这 个计数器完成。 由于Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式实现的。为了线 程切换后能恢复到正确的执行位置,每条线程都需要一个独立的程序计数器,各线程之间的计 数器互不影响,独立存储。 1.如果线程正在执行的是一个Java方法,计数器记录的是正在执行的虚拟机字节码指令的地 址 2.如果正在执行的是 Native方法,这个计数器的值为空 程序计数器是唯一—个没有规定任何 OutofMemoryError的区域 Java虚拟机栈(线程私有) Java虚拟机栈( Java virtual machine stacks)是线程私有的,生命周期与线程相同。 虛拟机栈描述的是Ja阳a方法执行的内存模型:每个方法被执行的时候都会创建一个栈 帧( Stack frame),存储 1.局部变量表 2.操作栈 3.动态链接 4.方法出口 每—一个方法被调用到执行完成的过程,就对应着一个栈帧在虛拟机栈中从入栈到出栈的过程。 这个区域有两种异常情况: 1. StackOverflow error:线程请求的栈深度大于虚拟机所允许的深度 2. OutOfMemoryError:虚拟机栈扩展到无法申请足够的内存时 本地方法栈(线程私有 虚拟机栈为虚拟机执行Java方法(字节码)服务。 本地方法栈( Native method stacks)为虚拟机使用到的 Native方法服务。 Java堆(线程共享) Java堆( Java Heap)是Java虚拟机中内存最大的一块。Java堆在虚拟机启动时创建,被所 有线程共享。 作用:存放对象实例。垃圾收集器主要管理的就是Java堆。Java堆在物理上可以不连续,只 要逻辑上连续即可。 方法区(线程共亨) 方法区( Method area)被所有线程共享,用于存储已被虛拟机加载的类信息、常量、静态 变量、即时编译器编译后的代码等数据。 和Java堆一样,不需要连续的内存,可以选择固定的大小,更可以选择不实现垃圾收集。 运行时常量池 运行时常量池( Runtime Constant pool)是方法区的一部分。保存 Class文件中的符号引 用、翻译岀来的直接引用。运行时常量池可以在运行期间将新的常量放入池中 Java中对象访问是如何进行的? Object ob j new Object( 对于上述最简单的访问,也会涉及到Java栈、Java堆、方法区这三个最重要内存区域。 Object obj 如果出现在方法体中,则上述代码会反映到Java栈的本地变量表中,作为 reference类型数 据出现。 new Object( 反映到Java堆中,形成一块存储了 bject类型所有对象实例数据值的内存。Java堆中还包 含对象类型数据的地址信息,这些类型数据存储在方法区中, 如何判断对象是否“死去”? 1.引用计数法 2.根搜索算法 什么是引用计数法? 给对象添加一个引用计数器,每当有一个地方引用它,计数器就+1,;当引用失效时,计数器 就-1;任何时刻计数器都为0的对象就是不能再被使用的 引用计数法的缺点? 很难解决对象之间的循环引用问题。 什么是根搜索算法? 通过一系列的名为" GC Roots"的对象作为起始点,从这些节点开始向下搜索,搜索所走过 的路径称为引用链( Reference chain),当一个对象到 GC Roots没有任何引用链相连(用 图论的话来说就是从 GC Roots到这个对象不可达)时,则证明此对象是不可用的。 object 5 object 6 object 7 仍然存活的对象 □判定可回收的对象 Java的4种引用方式? 在」DK1.2之后,Java对引用的概念进行了扩充,将引用分为 1.强引用 Strong reference 2.软引用 Soft reference 3.弱引用 Weak Reference 4.虚引用 Phantom reference 强引用

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值