Java Module System:大型项目中的利器
Java 平台模块化系统(Module System)是从 Java 9 开始引入的一项新特性。它允许将一个 Java 应用程序或库划分为多个模块,每个模块都是一个独立的单元,可以定义自己的接口和实现,同时明确规定与其他模块之间的依赖关系。这种方法具有诸多优点,能够更好地控制代码的可见性和访问权限,减少代码冲突和依赖问题,提高代码的可维护性、可测试性和可扩展性。
模块是一个可重用的、独立的、自包含的代码单元,由一组相关的类、接口、枚举和注解组成。模块可以用来封装代码、提供抽象接口和实现、控制访问权限和可见性,以及明确规定与其他模块之间的依赖关系。每个模块都必须包含一个模块描述文件(module-info.java),用于指定模块的名称、版本、依赖关系和导出的包。模块描述文件是一个 Java 源代码文件,可以使用 Java 编译器进行编译,并打包成一个 JAR 文件,可以在 Java 虚拟机中加载和执行。
例如,一个名为 “com.example.myapp” 的模块可以包含以下模块描述文件:
上面的模块描述文件指定了该模块的名称为 “com.example.myapp”,它需要 Java 的基础模块(java.base)和自定义的 “com.example.mylib” 模块。同时,它将 “com.example.myapp.api” 包导出给其他模块使用,但只将 “com.example.myapp.impl” 导出给 “com.example.myapp.plugin” 使用。
模块是一个可重用的、独立的、自包含的代码单元,由一组相关的类、接口、枚举和注解组成。每个模块都必须包含一个模块描述文件(module-info.java),用于指定模块的名称、版本、依赖关系和导出的包。
模块是 Java 9 引入的新特性,它提供了一种组织和部署 Java 代码的方法。模块类似于一个容器,可以将相关的类、资源等封装在一起,同时明确规定与其他模块之间的依赖关系。这种封装性使得代码更加清晰、易于维护,同时也提高了代码的安全性和可扩展性。
例如,一个名为 “com.example.myapp” 的模块可以包含以下模块描述文件:
在这个模块描述文件中,“module com.example.myapp” 定义了模块的名称为 “com.example.myapp”。“requires java.base; requires com.example.mylib;” 声明了该模块依赖于 Java 的基础模块(java.base)和自定义的 “com.example.mylib” 模块。“exports com.example.myapp.api; exports com.example.myapp.impl to com.example.myapp.plugin;” 则表示将 “com.example.myapp.api” 包导出给其他模块使用,同时只将 “com.example.myapp.impl” 导出给 “com.example.myapp.plugin” 使用。
- 解决传统 Java 开发中的技术痛点,如代码冲突、版本不兼容、难以控制的类路径等问题。
在传统的 Java 开发中,开发人员通常需要将所有的 Java 类和依赖库打包成一个 JAR 文件,并将这个 JAR 文件添加到 classpath 中。这种方式虽然简单易用,但是在复杂的应用程序中,很容易出现类冲突和版本不兼容问题。例如,当一个应用程序同时使用了多个依赖库,这些依赖库中可能会有相同的类名或者不同版本的类,这就会导致运行时的类冲突和异常。
为了解决这些问题,Java 平台模块化系统被设计出来了。它将一个 Java 应用程序或库划分为多个模块,每个模块都是一个独立的单元,可以定义自己的接口和实现,同时明确规定与其他模块之间的依赖关系。这种方式更加灵活,可以控制代码的可见性和访问权限,减少代码冲突和依赖问题,提高代码的可维护性、可测试性和可扩展性。
- 提供更好的封装、强制模块化、缩小 JDK 的体积、提高安全性以及支持 Java 平台的演进。
(1)更好的封装:在传统的 Java 中,包级别的访问控制机制不足以严格控制 API 的访问。Java Module System 允许模块间定义明确的导出和依赖关系,从而提供更强的封装和信息隐藏。
(2)强制模块化:通过 Java Module System,可以强制项目模块化,使得代码更容易维护、测试和重用。模块化可以提高开发效率和代码质量。
(3)缩小 JDK 的体积:JDK 9 中的模块化使得 JDK 本身可以被分解为多个模块。这样,开发者可以只包含他们需要的模块,而不是整个 JDK,从而减少应用程序的体积和启动时间。
(4)提高安全性:通过模块化,敏感的内部 API 可以被隐藏,只有明确导出的 API 才对外可见。这可以减少潜在的安全风险,因为内部实现细节不再暴露给外部代码。
(5)支持 Java 平台的演进:随着 Java 平台的发展,引入新的特性和 API 变得更加容易,因为模块化系统提供了更清晰的结构和依赖管理机制。
Java Module System 的适用范围非常广泛,适用于所有使用 JDK 开发的项目,涵盖了 Java SE、Java EE、Java ME 等各种 Java 平台的应用程序和库。
一些知名的开源项目已经采用了模块化系统,充分展示了其在实际开发中的价值。例如,Apache Maven 4.x 作为流行的 Java 构建工具,支持 Java 9 的模块化系统并计划在未来增加更多功能,这使得构建过程更加灵活和可管理。
Spring Framework 5.x 作为广泛应用的 Java 应用程序开发框架,也采用了模块化系统来组织代码。Spring 的核心容器由多个模块组成,如 spring-core、spring-beans、spring-context、spring-context-support 和 spring-expression 等。这些模块相互协作,为开发人员提供了强大的功能和灵活性。通过模块化系统,Spring Framework 能够更好地控制代码的可见性和访问权限,提高代码的可维护性和可扩展性。
Eclipse IDE 2021.x 作为流行的 Java IDE,使用模块化系统来组织插件和功能。这使得 Eclipse 的扩展和定制更加方便,开发人员可以根据自己的需求选择和加载特定的模块,提高开发效率。
JUnit 5.x 作为流行的 Java 测试框架,使用模块化系统来组织代码。这使得测试用例的编写和管理更加清晰,提高了测试的可维护性和可扩展性。
Guava 29.x 作为流行的 Java 库,提供了许多实用的工具类和函数,也采用了模块化系统来组织代码。这使得开发人员可以更加方便地使用 Guava 的功能,同时也提高了库的可维护性和可扩展性。
总之,Java Module System 为各种使用 JDK 开发的项目提供了一种更加灵活、可控制、可维护的代码组织方式,有助于提高开发效率和代码质量。
在 Java 的模块化系统中,模块描述文件module-info.java起着至关重要的作用。它允许开发者明确指定模块的名称、版本、依赖关系以及导出的包。例如,一个名为 “com.example.myapp” 的模块可以有如下的module-info.java文件:
在这个文件中,“module com.example.myapp” 定义了模块的名称。“requires java.base; requires com.example.mylib;” 声明了该模块依赖于 Java 的基础模块(java.base)和自定义的 “com.example.mylib” 模块。“exports com.example.myapp.api; exports com.example.myapp.impl to com.example.myapp.plugin;” 则表示将 “com.example.myapp.api” 包导出给其他模块使用,同时只将 “com.example.myapp.impl” 导出给 “com.example.myapp.plugin” 使用。
假设我们有一个应用程序,它有两个模块:一个模块用于处理数据库连接,另一个模块用于处理用户界面。
首先,创建数据库模块。数据库模块包含一个简单的类DatabaseConnection:
为该模块创建一个module-info.java文件,声明该模块的名称:
这个模块不依赖于其他模块,所以module-info.java文件中没有声明任何依赖关系。
接下来,创建用户界面模块。用户界面模块包含一个简单的类UserInterface:
为该模块创建一个module-info.java文件,声明该模块的名称和对数据库模块的依赖:
这个模块声明了对databaseModule的依赖,这意味着它可以使用databaseModule中的类和资源。
现在,我们可以使用 Java 9 编译器来编译这两个模块:
javac -d out/ui –module-path out databaseModule/*.java uiModule/*.java
然后,我们可以运行UserInterface类来启动我们的应用程序:
这将会输出:
User interface displayed.
- 强封装性:精准明确地指定模块的公开 API 以及内部实现,强化封装性。
在大型企业级应用中,例如跨国公司的管理系统,对于敏感的员工薪资数据存储模块,通过模块化可以将数据结构和算法严密封装,仅对外暴露必要接口,避免外部对内部实现的不必要访问,保障数据安全。
- 清晰的依赖管理:每个模块必须清晰明确地声明其依赖的其他模块,形成稳定的依赖管理机制。
以电商平台开发为例,订单模块依赖于用户模块和商品模块。通过明确声明依赖关系,在构建和运行时,相关模块能够准确无误地加载,杜绝因依赖不明确而引发的运行时错误,提升项目开发效率。
- 提高性能:助力 JVM 和编译器做出更优化的决策,减少资源消耗,提升应用响应速度。
在微服务和云原生应用场景中,模块系统优势显著。如微服务架构的在线教育平台,每个微服务作为独立模块,运行时只有被调用的微服务模块才会被加载,极大减少资源消耗,提升应用响应速度,为用户带来更流畅的体验。
- 更易于构建大型系统:鼓励将大型复杂程序拆分为更小、更易于管理的部分,提高代码重用性。
模块化系统使得大型系统的构建、测试和维护变得更加轻松。通过将大型复杂程序拆分为小模块,提高了代码的可管理性和可维护性,同时也提高了代码的重用性。
- 更好的安全性:限制模块之间的访问权限,隐藏敏感的内部 API。
模块化可以限制模块之间的访问权限,提供更好的封装性和安全性。只有导出的包才可以被其他模块访问,其他的包是不可见的,减少了潜在的安全风险。
- 减少应用体积:JDK 9 中的模块化使得 JDK 本身可以被分解为多个模块,开发者可以只包含需要的模块。
JDK 9 的模块化使得 JDK 可以被分解为多个模块,开发者可以根据实际需求只包含需要的模块,而不是整个 JDK,从而减少应用程序的体积和启动时间。
- 促进模块间的明确界限:每个模块都有清晰的功能边界,提高模块内部代码逻辑的紧密性。
模块化使得每个模块都有清晰的功能边界,模块内部代码逻辑更加紧密。这有助于提高代码的可维护性和可扩展性,同时也使得模块之间的交互更加清晰和可控。
- 清晰的依赖管理可能在复杂项目中变得难以维护。
在大型项目中,随着模块数量的增加和模块之间依赖关系的复杂化,清晰的依赖管理可能会变得极具挑战性。尽管 Java Module System 通过模块描述文件(module-info.java)明确声明了模块的依赖关系,但在实际的大型项目中,由于业务需求的不断变化和项目的持续演进,模块之间的依赖关系可能会频繁变动。这就需要开发人员不断地更新模块描述文件,以确保依赖关系的准确性。然而,在大规模的项目中,手动维护这些依赖关系可能会变得非常繁琐,容易出现错误。例如,在一个拥有数百个模块的企业级应用中,当一个模块的功能发生变化,可能会影响到多个其他模块的依赖关系。这就需要开发人员仔细分析每个受影响的模块,更新其依赖关系声明,确保整个系统的正常运行。这种手动维护的方式不仅效率低下,而且容易出现遗漏或错误,导致系统在运行时出现各种问题。
- 模块之间的可见性控制可能导致开发过程中的复杂性增加。
Java Module System 通过 exports 和 requires 关键字严格控制了模块之间的可见性。虽然这提高了系统的安全性和封装性,但在开发过程中也可能带来一些复杂性。例如,当一个开发人员需要在一个模块中使用另一个模块的功能时,必须确保目标模块正确地导出了所需的包,并且当前模块正确地声明了对目标模块的依赖。如果开发人员不熟悉模块之间的可见性规则,可能会花费大量时间来调试为什么无法访问其他模块的功能。此外,在大型项目中,模块之间的可见性控制可能会导致模块之间的交互变得更加复杂。开发人员需要仔细考虑哪些包应该被导出,哪些模块应该被依赖,以避免出现不必要的依赖关系和潜在的安全问题。
- 自动模块可能带来一些不确定性和兼容性问题。
在 Java 9 中,位于类路径上的非模块化代码被称为 “自动模块”。自动模块默认是可见的,但只能访问其他模块公开的部分。虽然自动模块在一定程度上方便了从传统的 Java 项目向模块化项目的过渡,但也可能带来一些不确定性和兼容性问题。例如,自动模块的依赖关系可能不明确,因为它们没有像模块化模块那样通过 module-info.java 文件明确声明依赖关系。这可能导致在运行时出现意外的依赖错误。此外,自动模块可能与模块化模块之间存在兼容性问题,特别是当自动模块依赖于旧的库或框架时。开发人员需要花费额外的时间来解决这些兼容性问题,确保整个系统的稳定运行。
- 模块层次结构的构建和维护需要一定的技术水平和经验。
在大型项目中,构建和维护模块层次结构是一项具有挑战性的任务。模块层次结构的设计需要考虑到项目的业务需求、功能模块的划分以及模块之间的依赖关系。开发人员需要具备一定的技术水平和经验,才能设计出合理的模块层次结构。例如,在一个复杂的企业级应用中,开发人员可能需要根据不同的业务领域将项目划分为多个模块,然后再根据模块之间的依赖关系构建层次结构。这需要开发人员对项目的业务逻辑有深入的理解,同时还需要熟悉 Java Module System 的特性和用法。此外,随着项目的不断演进,模块层次结构也需要不断地调整和优化。这就要求开发人员能够及时发现模块层次结构中存在的问题,并采取有效的措施进行改进。否则,不合理的模块层次结构可能会导致系统的性能下降、可维护性降低等问题。
- 清晰的依赖管理使得大型项目的代码结构更加清晰,易于维护。
在大型项目中,Java Module System 的清晰依赖管理发挥着重要作用。例如在一个大型企业级应用中,可能涉及众多模块,每个模块都有明确的功能定位。通过模块描述文件(module-info.java),可以清晰地指定每个模块所依赖的其他模块。这种明确的依赖关系使得开发人员能够快速了解项目的整体结构,当需要进行代码维护时,可以更准确地定位问题所在。比如在一个金融系统项目中,交易模块依赖于账户管理模块和风险评估模块。通过 Java Module System,开发人员可以清楚地看到这种依赖关系,当交易模块出现问题时,可以首先检查与之相关的账户管理模块和风险评估模块是否正常工作,从而提高维护效率。
- 增强的封装性提高了大型项目的安全性,减少了内部实现细节的暴露。
对于大型项目而言,安全性至关重要。Java Module System 的增强封装性可以有效地提高项目的安全性。在大型项目中,不同的模块可能由不同的团队开发,通过 exports 和 requires 关键字,可以严格控制模块之间的可见性。只有明确导出的包才可以被其他模块访问,其他的包是不可见的,减少了潜在的安全风险。例如在一个医疗信息管理系统中,患者信息模块可以将敏感的患者数据进行严格封装,只对外暴露必要的接口,其他模块无法直接访问内部实现细节,从而保护患者隐私。
- 更好的性能使得大型项目的启动速度更快,资源消耗更少。
在大型项目中,性能是一个关键问题。Java Module System 可以提高大型项目的性能,使得启动速度更快,资源消耗更少。由于模块之间的依赖关系明确,JVM 可以仅加载运行应用程序所需的模块,减少了内存占用。例如在一个电商平台项目中,商品展示模块、订单处理模块和支付模块等可以根据用户的操作动态加载,当用户浏览商品时,只有商品展示模块和相关的模块被加载,其他模块暂不加载,从而提高启动速度,减少资源消耗。
- 通过模块的划分,可以更好地组织大型项目的代码,提高开发效率和可维护性。
大型项目通常具有复杂的业务逻辑和庞大的代码量,通过模块的划分,可以更好地组织代码。每个模块都可以独立开发、测试和维护,提高了开发效率。例如在一个物流管理系统中,可以将运输管理模块、仓储管理模块、订单管理模块等分别进行开发,开发人员可以专注于自己负责的模块,减少了代码之间的干扰。同时,当需要进行功能扩展或修改时,也可以更加方便地进行,提高了可维护性。例如,如果需要对运输管理
Java 源代码动态编译、类加载和代码执行(Java 8)
Java 的一个重要特性是动态的类加载机制。通过在运行时动态地加载类,Java 程序可以实现很多强大的功能。下面通过一个具体的实例来说明 Java 程序中,如何动态地编译 Java 源代码、加载类和执行类中的代码。这里的代码示例适用的版本是 Java 8。
示例所实现的功能很简单,就是对表达式求值。输入的是类似 1 + 1 或 3 * (2 + 3) 这样的表达式,返回的是表达式的值。示例的做法是动态创建一个 Java 源文件,编译该文件生成 class 文件,加载 class 文件之后再执行。比如,需要求值的表达式是 1 + 1,那么所生成的 Java 源文件如下所示,其中 1 + 1 的部分是动态的。
我们只需要编译该源文件,加载编译之后的 class 文件,再通过反射 API 来调用其中的 calculate 方法就可以得到表达式求值的结果。
第一步是动态生成 Java 源代码并编译。生成 Java 源代码比较简单,直接用字符串连接就可以了。当然了,在生成逻辑比较复杂时,推荐的做法是使用字符串模板引擎,如 Handlebars。在下面的代码中,getJavaSource 方法生成 Java 源代码,compile 方法进行编译。
在进行编译的时候,使用的是 JDK 标准的 JavaCompiler 接口。从源代码字符串中创建了一个 JavaFileObject 对象作为编译时的源代码单元。编译时的选项 -d 指定了编译结果的输出路径,这里是一个临时文件夹。compile 方法的返回值是一个 Pair 对象,包含了 class 文件的路径,以及随机生成的 Java 包的名称。
上面的代码用到了一个帮助类 StringContentJavaFileObject,表示从字符串创建的 JavaFileObject 对象。
编译完成之后的第二步是动态加载类。这一步并没有实现自定义的类加载器,而且使用内置的系统类加载器。系统类加载器通过 ClassLoader.getSystemClassLoader() 方法来获取。系统类加载器在 classpath 上查找类。这里用了一个比较 hack 的技巧来动态修改系统类加载器的 classpath。
在下面的代码中,ClasspathUpdater 的 addPath 方法可以把一个 Path 对象表示的路径,添加到系统类加载器的查找路径中。这是因为系统类加载器自身是 URLClassLoader 类型的加载器,其中的 addURL 方法可以添加新的查找路径。只不过 addURL 方法是 protected,这里通过反射 API 来进行调用。
上面介绍的 ClasspathUpdater 类中的使用技巧,只对 Java 8 生效。在 Java 9 引入模块系统时,对系统类加载器进行了修改。系统类加载器被替换成了应用类加载器。应用类加载器不再是 URLClassLoader 类型了,就不能使用这个技巧了。
最后一步就是执行动态加载的 Java 类。这一步比较简单,只需要用 Class.forName 方法来查找 Java 类,再找到对应的 Method 对象,直接调用即可。下面的代码给出了示例。
最后把整个流程串起来。在下面的代码中,需要求值的表达式是 (1 + 1) * 3 / 5.0。首先调用 DynamicCompilation.compile 方法进行动态编译,得到 class 文件的路径和完整的类名。class 文件的路径通过 ClasspathUpdater.addPath 方法添加到 classpath 中。完整的类名则传递给 Invoker.invoke 方法来执行。最后输出的结果是表达式的值。
听说你还不知道Java代码是怎么运行的?
作者:Jay_huaxiao
作为一名Java程序员,我们需要知道Java代码是怎么运行的。最近复习了深入理解Java虚拟机这本书,做了一下笔记,希望对大家有帮助,如果有不正确的地方,欢迎提出,感激不尽。
java 代码运行主要流程
本文主要讲解流程如下:
- java源文件编译为class字节码
- 类加载器把字节码加载到虚拟机的方法区。
- 运行时创建对象
- 方法调用,执行引擎解释为机器码
- CPU执行指令
- 多线程切换上下文
编译
我们都知道,java代码是运行在Java虚拟机上的。但是java是一门面向对象的高级语言,它不仅语法非常复杂,抽象程度也非常高,并不能直接运行在计算机硬件机器上。
Java虚拟机(Java Virtual Machine 简称JVM)是运行所有Java程序的抽象计算机,是Java语言的运行环境。
因此,在运行Java程序之前,需要编译器把代码编译成java虚拟机所能识别的指令程序,这就是Java字节码,即class文件。
所以,Java代码运行的第一步是:把Java源代码编译成.class 字节码文件。
类加载
在Class文件中描述的各种信息,需要被加载到虚拟机之后才能运行和使用。因此,需要把class字节码文件加载到Java虚拟机来。
虚拟机把描述类的数据从 Class 文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型,这就是虚拟机的类加载机制。
加载
在加载阶段,虚拟机需要完成以下3件事情:
- 通过一个类的全限定名来获取定义此类的二进制字节流。
- 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
- 在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口
加载阶段完成后,这些二进制字节流按照虚拟机所需的格式存储在方法区之中。
验证
为了确保Class文件的字节流中包含的信息符合当前虚拟机的要求,不会危害虚拟机的安全,Java虚拟机对输入的字节流走验证过程。
验证阶段包括四个阶段:文件格式验证、元数据验证、字节码验证、符号引用验证。
- 文件格式验证: 验证字节流是否符合Class文件格式规范,如:是否以魔数0xCAFEBABE开头。
- 元数据验证: 对字节码描述的信息进行语义分析,如:这个类的父类是否继承了不允许被继承的类(被final修饰的类);
- 字节码验证: 主要目的是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。如:保证跳转指令不会跳转到方法体以外的字节码指令上。
- 符号引用验证: 发生在虚拟机将符号引用转化为直接引用的时候,如:校验符号引用中通过字符串描述的全限定名是否能找到对应的类。
准备
准备阶段是正式为类变量分配内存并设置类变量初始值,这些变量所使用的内存都将在方法区中进行分配。如:
public static int value =123;
变量value在准备阶段过后的初始值是0而不是123。
解析
解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。
比如:com.User类引用com.Tool类,在编译时,User类不知道Tool类的实际内存地址,因此只能使用符号com.Tool(假设)来表示。而在类加载加载User类的时候,可以通过虚拟机获取Tool类的实际内存地址,因此便可以将符号com.Tool替换为Tool类的实际内存地址,即直接引用地址。
解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符 7 类符号引用进行。
初始化
到了初始化阶段,才真正开始执行类中定义的Java字节码。在这个阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源。
创建对象
Java虚拟机是如何执行字节码的呢?我们先来看一下运行时创建对象。
Java是面向对象的编程语言,程序的运行是以对象为调用单位的。
- 字节码文件加载到虚拟机的方法区后,在程序运行过程,通过 class字节码文件创建与其对应的对象信息 。
- 创建对象的方式有:new关键字,反射等。
- Java堆内存是线程共享的区域,创建后的对象信息就保存在Java堆内存中。
方法调用
JVM的调用单位是对象,但是真正执行功能性的代码还是对象上的方法。
在运行过程中,每当调用进入一个java方法,java虚拟机会在当前线程的java方法栈中生成一个栈帧,用以存放局部变量以及字节码的操作数。方法栈内存是线程私有的,每个线程都有自己的方法栈。如果对应的方法是本地方法,则对应的就是本地方法栈。
java运行时数据区域如下:
解释
当调用Java对象的某个方法时,JVM执行引擎会将该方法的字节码文件翻译成计算机所能识别的机器码,机器码信息保存在方法区中。翻译有解释执行和即时编译两种方式。
两种翻译方式的区别如下:
解释执行来一行代码,解释一行,大部分不常用的代码,都是采用这种方式。
即使编译
对于部分热点代码,将一个方法包含的所有字节码翻译成机器指令,以提高java虚拟机的运行效率。
即时编译是建立经典的二八定律上,即20%代码占据了80%的计算资源。
执行指令
- Java程序被加载入内存后,指令也在内存中了。
- 指令的指令寄存器IP,指向下一条待执行指令的地址。
- CPU的控制单元根据IP寄存器的指向,将主存中的指令装载到指令寄存器,这些加载的指令就是一串二进制码,还需要译码器进行解码。
- 解码后,如果需要获取操作数,则从内存中取数据,调用运算单元进行计算。
多线程上下文切换
CPU一通上电,就会周而复始从内存中获取指令、译码、执行。
- 为了支持多任务,CPU 将执行时间这个资源划分成时间片,每个程序执行一段时间。
- java虚拟机的多线程是通过线程轮流切换分配处理执行时间的方式来实现的,在任何一个确定的时刻,一个处理器(对于多核处理器来说是一个内核)都只会执行一条程序中的指令。
- 假设当前线程在运行中,CPU分配的时间执行完了,总得保存运行过的结果信息吧,要不然白白浪费之前的工作了,因此,程序计数器(PC寄存器)作用体现出来了,它是一块较小的内存空间,线程私有,可以看作当前线程执行的字节码的行号指示器。当CPU又给它分配时间跑的时候,可以把数据恢复,接着上一次执行到的位置继续执行就可以了。
原文:https://juejin.im/entry/5e6ccc05e51d4527110aa25f
本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com
文章为作者独立观点不代本网立场,未经允许不得转载。