JVM java虚拟机深度学习

一、简介

1.介绍

Java虚拟机Java Virtual Machine,JVM)是Java平台的核心组成部分之一,它是一个在计算机上运行Java字节码的虚拟机。JVM 充当了 Java 应用程序和底层操作系统之间的中间层,提供了跨平台的特性,使得 Java 程序可以在不同的操作系统和硬件上运行。

2.JVM的主要功能和特点

(1)主要功能

        执行字节码:JVM能够解释和执行Java编译后生成的字节码class文件。字节码是一种中间语言,它类似于机器码但不直接依赖于具体的硬件和操作系统,而是依赖于JVM来执行。

        内存管理:JVM负责动态分配和管理Java程序的内存。它提供了垃圾回收机制(Garbage Collection)来自动释放不再使用的内存。

        即时编译:JVM使用即时编译器Just-In-Time Compiler,JIT)来优化字节码的执行速度。即时编译器将热点代码(即被频繁执行的代码)编译为本地机器码,提高执行效率。

        异常处理:JVM提供了异常处理机制。此机制可捕获和处理Java程序中的异常。通过异常表和异常处理器来管理异常的传播和处理流程,保证程序的稳定性和可靠性。

        类加载和运行时环境:JVM负责加载、验证、链接和初始化Java类。它使用类加载器(Class Loader)将类文件加载到内存中,并在运行时创建和管理类的实例。JVM还提供了一些系统级的库和API,用于支持Java程序的运行。

(2)特点

        跨平台:跨平台性是JVM的是Java语言的重要优势之一。例如经常使用的Linux系统,MacOS系统,Win系统,一次编译,处处运行就是这样理解的。

        字节码:字节码文件即JVM可以识别并执行的二进制文件,不同的编程语言经过编译器编译处理之后,转换成统一的字节码规范文件,这样JVM就可以执行。

        跨语言:随着JVM不断的发展和优化,很多语言都使用了JVM的能力。

 总的来说,JVM提供了Java程序运行的环境和基础设施,为开发者提供了一种跨平台、高效、安全的开发和运行环境。

二、java虚拟机

1.JVM整体构成

 本地方法栈:非java写的方法(即native方法)执行时,存放于此;

虚拟机栈:每执行一个方法,都会在这里创建一个栈帧,存放操作数栈、常量表、返回值地址信息;

方法区:类加载子系统将class加载到内存中,即放在方法区,字节码指令存放在这里

程序计数器:线程间的切换原因,线程每次执行后都要记录该线程执行的地址,以便下次执行是从上次执行位置向下执行,即记录线程下次要执行的地址;

堆:存放java对象

类加载子系统:将编译后的java代码(class)文件从磁盘中加载到内存中;

解释器:用来执行方法区中存放的字节码指令;

JIT 编译器:即时编译,解释器执行字节码指令时,有些指令是多次重复执行,针对这些热点指令,翻译一次后直接缓存,不用每次都翻译,提高执行效率,因为字节码指令会由解释器翻译为机器指令,每次执行都会翻译;缓存后每次执行这个指令,就会从缓存中获取这些字节码指令的机器指令;

垃圾回收器:回收垃圾对象;

本地方法:其他语言写的方法,即native方法

1.1 类加载子系统

 1.1.1 类加载子系统介绍

类加载子系统是将class文件加载到方法区的内存中,然后对class文件进行验证,class文件是否正确,是否被修改过,格式是否正确;之后进行准备操作,准备操作有两个不走,一是为static变量分配内存,二是给变量赋值为零值,例如 int a = 10,那么准备工作会将变量赋值为 int a = 0,在初始化步骤中将a 再赋值为10;

解析: 解析(Resolution)是将符号引用解析为直接引用的过程。为了理解这个过程,我们首先需要了解符号引用和直接引用的概念。

        符号引用(Symbolic Reference):是一种编译时期的引用,他使用符号来表示所引用的目标。比如类、方法、字段等,符号引用并不直接指向内存中的位置,而是通过符号的形式描述所引用的目标;

        直接引用(Direct Reference):指向内存中的实际地址的引用。java代码再运行时,需要将符号引用(类、方法、字段等)解析为直接引用,以便操作内存中的实际对象。

解析步骤:

  1. 定位目标:首先,JVM会根据符号引用的信息定位到目标对象,比如类、方法、字段等。这个定位过程可以通过查找类加载器的加载信息、运行时常量池等进行。

  2. 解析符号引用:一旦目标对象被定位,JVM会对符号引用进行解析。解析的过程就是将符号引用转化为直接引用的过程。在解析时,JVM会验证符号引用的正确性,并尝试将其转化为直接引用。

  3. 创建直接引用:一旦符号引用被解析为直接引用,JVM会根据直接引用创建相应的对象,比如类的实例、方法的直接引用等。

通过解析过程,JVM可以将在编译阶段生成的符号引用转化为运行时需要的直接引用,从而实现对类、方法、字段等的访问和操作。

总结起来,解析是将符号引用转化为直接引用的过程,让JVM能够准确地定位并操作内存中的实际对象。这个过程是类加载子系统中的重要环节,保证了Java程序的正确运行。

初始化:将类中的static属性和其他成员变量赋值操作(当然这些属性,有给默认值才执行赋值操作)。

 1.1.2 类加载分类

JVM 加载class文件是通过类加载器去加载的。 

类加载器分为两大类,引导类加载器(BootstrapClassLoader)自定义类加载器

引导类加载器(BootstrapClassLoader):非java代码写的代码,JVM 默认的类加载器,加载jre 下的 lib下面的jar包;

自定义类加载器:是使用java代码写的类加载器,继承ClassLoader。jvm中默认的自定义类加载器,扩展类加载器(ExtClassLoader)、应用类加载器(AppClassLoader)。ExtClassLoader加载的是 jre下的lib下的ext下面的 jar 包,AppClassLoader加载的是当前应用classpath下的类。

 1.1.3 双亲委派

作用:

  • 避免类的重复加载
  • 防止核心API被篡改

1.2 运行时数据区

1.2.1 程序计数器 

PC Register, 程序计数寄出去,简称程序计数器

(1)物理寄存器的抽象实现;

(2)记录待执行的下一条指令的地址;

(3)是程序控制流指示器,循环、if else、异常处理、线程恢复等都依赖于程序计数器实现;

(4)解释器工作时就是通过它来获取下一条需要执行的字节码的指令;

(5)程序计数器是JVM中唯一一个没有规定任何 OutOfMemoryError 情况的区域。

1.2.2 虚拟机栈(java方法栈)
1.2.2.1 虚拟机机构

每个线程再执行的时候,都会生成一个虚拟机栈,虚拟机栈保存一个个栈帧,每个栈帧对应一个方法。

每执行一个方法,都创建一个栈帧1,如果这个方法调用了另一个方法,那么会在虚拟机栈创建一个栈帧2,并且放入到虚拟机栈中,同理,再执行方法3和方法4都会生成栈帧3和栈帧4,并且存入虚拟机栈中。如果方法4执行完成,那么对应的栈帧就会出栈。依次执行方法3,2,1 都执行完成后,虚拟机栈就会消失。

虚拟机栈特点:

1. 虚拟机栈是线程私有的;

2. 每个方法执行都会创建一个栈帧存入虚拟机栈,方法执行完成,栈帧出栈。因此,虚拟机栈不需要垃圾回收;

3. 虚拟机栈存在OutOfMemoryError和StackOverflowError问题;

4. 线程太多,就可能出现OutOfMemoryError,线程创建时,没有足够内存去创建虚拟机栈了;

5. 方法调用层数太多,可能会出现StackOverflowError错误,虚拟机栈内存不足;

6. 可以通过 -Xss 来设置虚拟机栈的大小。

1.2.2.2 栈帧结构

每个栈帧由局部变量表、操作数栈、返回值地址、动态链接、附加信息组成。

1.2.2.2.1 局部变量表

 局部变量表有一个个Slot组成,一个栈帧相当于一个方法,在这个方法中我们通常会定义一些局部变量,存放再栈帧中的局部变量表当中,那么这个局部变量就是Slot

1.2.2.2.2 操作数栈

 说明:

1. java代码:定义了变量a、b、c,其中a = 10,b = 20,c = a + b;

2. 字节码指令:java代码解析为字节码后执行的指令

3. 局部变量表:存放字节码中变量 a,b,c 的表

执行过程说明:

 上图用第一个指令做了一个示例,下面将详细介绍执行流程

1.执行第一行字节码指令(bipush)将10压入到操作数栈中;

2.执行第二行字节码指令(istore_1)将10从操作数栈中拿出,存入到局部变量表索引为1的位置;

3.执行第三行字节码指令(bipush)将20压入到操作数栈中;

4.执行第四行字节码指令(istore_2)将10从操作数栈中拿出,存入到局部变量表索引为 2 的位置;

5.执行iload_1指令,将局部变量表下标为1的数据读出来,压入操作数栈;

6.执行iload_2指令,将局部变量表下标为2的数据读出来,压入操作数栈;

7.执行idd ,将操作数栈中的10 和20 进行加法操作,得到结果30;

8.执行istore_3,将30 从操作数栈中取出,存入局部变量表索引为3的位置;

9.执行返回return 指令,将局部变量表中的30 返回,并栈帧消失。

1.2.2.2.3 返回值地址

每个方法执行时都会创建一个栈帧,当方法结束后,将继续往下执行,那么这个栈帧中的返回值地址就是要执行下一条指令的地址。

1.2.2.2.4 动态链接

动态链接的主要目的是支持方法之间的调用和返回。为了实现动态链接,每个栈帧都包含了一个指向其调用者栈帧的引用。这个引用通常被称为"帧指针"或"前一帧指针"。通过帧指针,可以在运行时建立和维护方法调用链。当一个方法执行完成后,JVM会将其栈帧出栈,恢复到调用者方法的栈帧,并继续执行调用者方法的剩余部分。这种递归的栈帧结构和动态链接机制使得Java能够支持方法的嵌套调用和返回。

总结一下,动态链接是通过在栈帧中维护帧指针来实现的,它用于建立和维护方法之间的调用关系,支持方法的嵌套调用和返回。

1.2.3 本地方法栈

本地方法:navive method,在java中定义的方法,但是是由其他语言实现;

本地方法栈存放的是本地方法调用的栈帧;

线程私有,也可能出现OOM(OutOfMemeryError)和SOF(StackOverflowError)。

1.2.4 堆

JVM 运行时最重要的一块区域。JVM 规范中所有的对象和数组都存放在该区域,在执行字节码指令时,会将创建的对象放入堆中,对象引用的地址放入虚拟机栈的栈帧中,当方法执行完成后,引用的对象不会立即被回收,而是需要等待守护线程GC后,才能被回收。

-Xms:ms(memory start),指定堆初始化内存大小,等价于:-XX:InitialHeapSize

-Xmx:mx(memory max),指定堆最大内存大小,等价于:-XX:MaxHeapSize

一半会把 -Xms和 -Xmx 设置为一样大小,原因是JVM在GC后不需要去调整内存大小,提高效率。

默认情况下,初始化内存大小=物理内存大小 / 64,最大内存大小=物理内存 / 4

可以通过 -XX:NewRatio 参数来设置新生代和老年代的比例,默认是2,表示新生代占1,老年代占2,也就是新生代内存占堆区总大小的 1/3; 一半是不需要调整的,只有明确知道存活时间比较长的对象偏多,就需要调大 NewRatio来调整老年代占比。

Eden:伊甸园区,新的对象会存放到Eden区(除非对象大小超过了Eden区,那就只能直接进入老老年代);S0(Servivor0)、S1(Servivor1)区,也可以叫from区和to区,用来存放MinorGC(YGC)后存活的对象

默认情况下(Eden : S0 : S1 内存比例为 8 : 1 : 1), 可以通过 -XX:ServivorRatio 来调整

Yong GC / Minor GC(YGC):负责对新生代进行垃圾回收;

Old GC / Major GC:负责对老年代进行垃圾回收,目前只有CMS垃圾回收器单独对老年代进行GC,其他垃圾回收器是对整堆进行GC,同时对老年代进行GC;

Full GC:整堆回收,包括方法区。

创建新对象和回收过程详细说明:

  1.  图1中,创建多个对象,有写对象在执行完成后不在引用(灰色)即垃圾对象,有的继续引用(粉色),当Eden区域满了之后会进行一次MinorGC(YGC),存活的对象会进入到S0区,并且给存活的对象标记上数字1,表示经历过一次MinorGC,如图2所示;

2. 再次当Eden区对象满了之后,再次进行MinorGC,会将垃圾对象清理,如下图3。将Eden存活的对象放入S1区,并且给对象标记上数字1,表示经历过一次MinorGC;将S0区存活的对象也放入到S1区,并且给对象标记上数字2,表示又经历过一次MinorGC,如下图4所示。 

 3. 继续创建新对象,并放入Eden区,当Eden区再次满了,会再次进行MinorGC,会将垃圾对象清理,如下图5。将Eden存活的对象放入S0区,并且给对象标记上数字1,表示经历过一次MinorGC;将S0区存活的对象也放入到S0区,并且给对象标记上数字3,表示又经历过一次MinorGC,如下图6所示。

 4. 不断重复这些过程,有些对象一直存活,经历过15次MinorGC,如下图7所示;最后一直存活的对象,将会存放再老年代中,如下图8所示

 5、如果Eden有个大对象,导致Eden直接满了,执行MinorGC,由于S0和S1区内存不足于存放该大对象,则直接会放到老年代中,如图9、图10所示;

 6.如果堆中创建了一个超大对象,Eden区、S0区、S1区都放不下,则直接会放入到老年代,如图11、图12所示;

1.2.5 方法区

方法区和堆一样,是一个被其他线程共享的区域。主要存储解析器解析后的机器指令、类信息、方法信息、常量池、静态变量等。

JDK 1.8之前,称之为永久代,实际上在1.7版本的时候已经有一部分不是永久代了,比如常量池,静态变量已经移出永久代,到了1.8则完全使用元空间(本地内存)替代。

`

1.3 垃圾回收

1.3.1 为什么要有垃圾回收

在JVM中,没有任何引用指向的对象称之为垃圾。垃圾不清理,就会一直占用着内存,内存中的对象越来越多,最终会导致OOM。

1.3.2 垃圾回收算法
1.3.2.1 引用计数法

每个对象都记录一个引用计数器属性,用来记录被引用的次数。

优点:实现简单,只要引用计数器为0,就是垃圾

缺点:

  • 无法解决循环引用问题
  • 需要额外空间存储
  • 需要额外时间维护

1.3.2.2 可达性分析

可达性分析是一GC root 为起点,一层一层地找到所引用的对象,被找到的就是存活的对象,其他不可达的对象称为垃圾对象。 

GC Roots 是一组引用,包括:

  • 虚拟机栈中正在执行的方法对应的参数,局部变量表中对象的引用;
  • 本地方法栈中正在执行的方法对应的参数,局部变量表中对象的引用;
  • 方法区中保存的类中的static属性所对应的对象引;
  • 方法区中保存的类中的常量属性所对应的对象引用等等;
1.3.3 回收算法
1.3.3.1 标记-清除

 

常用的垃圾回收算法,当新生代或者老年代内存不足,则会暂停所有用户线程,即STW(Stop The World),执行算法进行垃圾回收。

A.标记阶段:从GC roots 开始遍历,找到可达对象,并在对象头中进行记录;

B.清除阶段:堆内存空间进行线性遍历,如果发现对象头中没有记录此对象是可达的对象,则进行回收。

标记-清除算法优缺点:

优:思路简单

 缺:

        A.效率不高;

        B.内存碎片;由图可以看出来,内存是分散的,不是连续的,会导致有些对象没法存入。

1.3.3.2 复制

将内存分为A、B两块,每次只使用一块,在垃圾回收的时候,将可达对象存入到另一块空的内存中,然后再清除当前内存块中不可达对象。

复制算法优缺点:

优:

1.没有标记-清除阶段,直接将可达对象复制,不需要修改对象头,效率高

2.不会出现内存碎片

缺: 

1.需要更多的内存,总有一块内存没有被使用

2.对象复制后,内存地址发生改变,需要修改栈帧中对象引用的地址

3.如果可达对象比较多,那么复制效率就很低,所以适合垃圾对象多的情况(通常用在新生代)

1.3.3.3 标记-整理

 

分为三个阶段:

1. 第一阶段,从GC Roots开始遍历,找到可达对象,并在对象头中记录;

2. 第二阶段,将可达对象移动到内存的另一端;

3. 第三阶段,清理边界外所有空间。

优缺点:

优:

1. 不会产生内存碎片

2. 不需要额外的内存空间

缺:

1.效率低下

2.需要修改栈帧中的内存地址

三个算法比较,如下图:

 1.3.4 常见垃圾收集器

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
深入java虚拟机第二版 第1章 Java体系结构介绍 1.1 为什么使用Java 1.2 网络带来的挑战和机遇 1.3 体系结构 1.3.1 Java虚拟机 1.3.2 类装载器的体系结构 1.3.3 Java class文件 1.3.4 Java API 1.3.5 Java程序设计语言 1.4 Java体系结构的代价 1.5 结论 1.6 资源页 第2章 平台无关 2.1 为什么要平台无关 2.2 Java的体系结构对平台无关的支持 2.2.1 Java平台 2.2.2 Java语言 2.3.3 Java class文件 . 2.2.4 可伸缩性 2.3 影响平台无关性的因素 2.3.1 Java平台的部署 2.3.2 Java平台的版本 2.3.3 本地方法 2.3.4 非标准运行时库 2.3.5 对虚拟机的依赖 2.3.6 对用户界面的依赖 2.3.7 Java平台实现中的bug 2.3.8 测试 2.4 平台无关的七个步骤 2.5 平台无关性的策略 2.6 平台无关性和网络移动对象 2.7 资源页 第3章 安全 3.1 为什么需要安全性 3.2 基本沙箱 3.3 类装载器体系结构 3.4 class文件检验器 3.4.1 第一趟:class文件的结构检查 3.4.2 第二趟:类型数据的语义检查 3.4.3 第三趟:字节码验证 3.4.4 第四趟:符号引用的验证 3.4.5 二进制兼容 3.5 Java虚拟机中内置的安全特性 3.6 安全管理器和Java API 3.7 代码签名和认证 3.8 一个代码签名示例 3.9 策略 3.10 保护域 3.11 访问控制器 3.11.1 implies()方法 3.11.2 栈检查示例 3.11.3 一个回答“是”的栈检查 3.11.4 一个回答“不”的栈检查 3.11.5 doPrivileged()方法 3.11.6 doPrivileged()的一个无效使用 3.12 Java安全模型的不足和今后的发展 方向 3.13 和体系结构无关的安全性 3.14 资源页 第4章 网络移动性 4.1 为什么需要网络移动性 4.2 一种新的软件模式 4.3 Java体系结构对网络移动性的支持 4.4 applet:网络移动性代码的示例 4.5 Jini服务对象:网络移动对象的示例 4.5.1 Jini是什么 4.5.2 Jini如何工作 4.5.3 服务对象的优点 4.6 网络移动性:Java设计的中心 4.7 资源页 第5章 Java虚拟机 5.1 Java虚拟机是什么 5.2 Java虚拟机的生命周期 5.3 Java虚拟机的体系结构 5.3.1 数据类型 5.3.2 字长的考量 5.3.3 类装载器子系统 5.3.4 方法区 5.3.5 堆 5.3.6 程序计数器 5.3.7 Java栈 5.3.8 栈帧 5.3.9 本地方法栈 5.3.10 执行引擎 5.3.11 本地方法接口 5.4 真实机器 5.5 一个模拟:“Eternal Math” 5.6 随书光盘 5.7 资源页 第6章 Java class文件 6.1 Java class文件是什么 6.2 class文件的内容 6.3 特殊字符串 6.3.1 全限定名 6.3.2 简单名称 6.3.3 描述符 6.4 常量池 6.4.1 CONSTANT_Utf8_info表 6.4.2 CONSTANT_Integer_info表 6.4.3 CONSTANT_Float_info表 6.4.4 CONSTANT_Long_info表 6.4.5 CONSTANT_Double_info表 6.4.6 CONSTANT_Class_info表 6.4.7 CONSTANT_String_info表 6.4.8 CONSTANT_Fieldref_info表 6.4.9 CONSTANT_Methodref_info表 6.4.10 CONSTANT_InterfaceMethodref_ info表 6.4.11 CONSTANT_NameAndType_info 表 6.5 字段 6.6 方法 6.7 属性 6.7.1 属性格式 6.7.2 Code属性 6.7.3 ConstantValue属性 6.7.4 Deprecated属性 6.7.5 Exceptions属性 6.7.6 InnerClasses属性 6.7.7 LineNumberTable属性 6.7.8 LocalVariableTable属性 6.7.9 SourceFile属性 6.7.10 Synthetic属性 6.8 一个模拟:“Getting Loaded” 6.9 随书光盘 6.10 资源页 第7章 类型的生命周期 7.1 类型装载、连接与初始化 7.1.1 装载 7.1.2 验证 7.1.3 准备 7.1.4 解析 7.1.5 初始化 7.2 对象的生命周期 7.2.1 类实例化 7.2.2 垃圾收集和对象的终结 7.3 卸载类型 7.4 随书光盘 7.5 资源页 第8章 连接模型 8.1 动态连接和解析 8.1.1 解析和动态扩展 8.1.2 类装载器与双亲委派模型 8.1.3 常量池解析 8.1.4 解析CONSTANT_Class_info入口 8.1.5 解析CONSTANT_Fieldref_info 入口 S.1.6 解析CONSTANT_Methodref_info 入口 8.1.7 解析CONSTANT_Interface- Methodref_info入口 8.1.8 解析CONSTANT_String_info入口 8.1.9 解析其他类型的入口 8.1.10 装载约束 8.1.11 编译时常量解析 8.1.12 直接引用 8.1.13 _quick指令 8.1.14 示例:Salutation程序的连接 8.1.15 示例:Greet程序的动态扩展 8.1.16 使用1.1版本的用户自定义类装 载器 8.1.17 使用1.2版本的用户自定义类装 载器 8.1.18 示例:使用forName()的动态扩展 8.1.19 示例:卸载无法触及的greeter类 8.1.20 示例:类型安全性与装载约束 8.2 随书光盘 8.3 资源页 第9章 垃圾收集 9.1 为什么要使用垃圾收集 9.2 垃圾收集算法 9.3 引用计数收集器 9.4 跟踪收集器 9.5 压缩收集器 9.6 拷贝收集器 9.7 按代收集的收集器 9.8 自适应收集器 9.9 火车算法 9.9.1 车厢、火车和火车站 9.9.2 车厢收集 9.9.3 记忆集合和流行对象 9.10 终结 9.11 对象可触及性的生命周期 9.11.1 引用对象 9.11.2 可触及性状态的变化 9.11.3 缓存、规范映射和临终清理 9.12 一个模拟:“Heap of Fish” 9.12.1 分配鱼 9.12.2 设置引用 9.12.3 垃圾收集 9.12.4 压缩堆 9.13 随书光盘 9.14 资源页 第10章 栈和局部变量操作 10.1 常量入栈操作 10.2 通用栈操作 10.3 把局部变量压入栈 10.4 弹出栈顶部元素,将其赋给局部变量 10.5 wide指令 10.6 一个模拟:“Fibonacci Forever” 10.7 随书光盘 10.8 资源页 第11章 类型转换 11.1 转换操作码 11.2 一个模拟:“Conversion Diversion” 11.3 随书光盘 11.4 资源页 第12章 整数运算 12.1 二进制补码运算 12.2 Inner Int:揭示Java int类型内部性质 的applet 12.3 运算操作码 12.4 一个模拟:“Prime Time” 12.5 随书光盘 12.6 资源页 第13章 逻辑运算 13.1 逻辑操作码 13.2 一个模拟:“Logical Results” 13.3 随书光盘 13.4 资源页 第14章 浮点运算 14.1 浮点数 14.2 Inner Float:揭示Java float类型内部 性质的applet 14.3 浮点模式 14.3.1 浮点值集合 14.3.2 浮点值集的转换 14.3.3 相关规则的本质 14.4 浮点操作码 14.5 一个模拟:“Circle of Squares” 14.6 随书光盘 14.7 资源页 第15章 对象和数组 15.1 关于对象和数组的回顾 15.2 针对对象的操作码 15.3 针对数组的操作码 15.4 一个模拟:“Three—Dimensional Array” 15.5 随书光盘 15.6 资源页 第16章 控制流 16.1 条件分支 16.2 五条件分支 16.3 使用表的条件分支 16.4 一个模拟:“Saying Tomato” 16.5 随书光盘 16.6 资源页 第17章 异常 17.1 异常的抛出与捕获 17.2 异常表 17.3 一个模拟:“Play Ball!” 17.4 随书光盘 17.5 资源页 第18章 finally子句 18.1 微型子例程 18.2 不对称的调用和返回 18.3 一个模拟:“Hop Around” 18.4 随书光盘 18.5 资源页 第19章 方法的调用与返回 19.1 方法调用 19.1.1 Java方法的调用 19.1.2 本地方法的调用 19.2 方法调用的其他形式 19.3 指令invokespecial 19.3.1 指令invokespecial和[init]()方法 19.3.2 指令invokespecial和私有方法 19.3.3 指令invokespecial和super关键字 19.4 指令invokeinterface 19.5 指令的调用和速度 19.6 方法调用的实例 19.7 从方法中返回 19.8 随书光盘 19.9 资源页 第20章 线程同步 20.1 监视器 20.2 对象锁 20.3 指令集中对同步的支持 20.3.1 同步语句 20.3.2 同步方法 20.4 Object类中的协调支持 20.5 随书光盘 20.6 资源页 附录A 按操作码助记符排列的指令集 附录B 按功能排列的操作码助记符 附录C 按操作码字节值排列的操作码助

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值