深入理解Java虚拟机学习总结

深入理解java虚拟机总结

学习目标:

概述:首先我是一名学生,还没有过多过的项目经验。其次书中也没有对虚拟机的具体实现细节进行过多描述。所以我很难将书中内容对应到具体的应场景,也不会走上研究比如垃圾回收的具体代码,做到熟练阅读字节码或者背诵每个垃圾处理器的名字和特性这样的路子上。

所以偶的目标是:
1. 通读全书,做好笔记。对虚拟机中的内存管理,虚拟机执行子系统,程序编译和代码优化,高效并发这几大模块有一个概念上的认识。
2. 通过总结和整理笔记,梳理知识。对虚拟机的整体体系结构和书中所讲虚拟机的这几个部分的体系结构心中要有谱。(就是要梳理并记住本书作者的思路,因作者本来就是按照他梳理出来的虚拟机的体系结构写书的,窃取他的智慧。)
3. 由于虚拟机是java跨平台的原因。是java体系结构中的一环,所以将其和java语言联系起来。在脑子中构造一个比较完整的java体系结构。具体方法就是:描述一个java源代码从书写到编译到执行的过程中的每一个环节,越详细越好。
4. 由于虚拟机是个中间层,和操作系统有莫大的关系,底层还要调操作系统的本地服务。而且并发的实现和操作系统的并发实现也很有关系。所以将并发等内容和操作系统原理中的相关部分进行同步比照学习。来理解它们之间的关系,并构建自己的知识网络。
5. 这篇总结偏重于思路的整理,而不是知识点的复述。因为具体的知识点如G1垃圾收集器的特点在每个具体部分的笔记中有记录。

准备知识:

  1. 只要源代码最终可以被编译为符合虚拟机规范的字节码文件就行,所以java虚拟机不仅仅服务于java语言。毕竟虚拟机最终执行的字节码文件,它才不管这个字节码文件是怎么来的。换句话说,你也可以直接用字节码来写程序,这和直接用机器码来写程序是一个道理。

  2. java语言特性:

    • 面向对象,将数据和行为抽象为类,类之间通过继承和组合进行扩展
    • 安全,动态可扩展。因为由虚拟机动态连接,执行。
    • 跨平台。因为编译为统一形式的字节码,各个异构平台按照虚拟机规范实现虚拟机
    • 自动管理内存。虚拟机负责java程序运行时的内存管理,虚拟机和操作系统的内存管理进行交互
  3. Comment:

java语言的这些特性看起来高大上,可以适应问题规模的增长所带来的编码问题,将程序员的注意力集中的问题本身上,而不是其他方面(如内存的分配和释放,兼容性)等方面。 但是需要注意的是,这些观点都是从**人的角度**来出发的,可以说java语言是为了更便于人从人类的角度来解决问题才产生这些特性的。可以说,从**计算机的角度**来看,这些特性糟透了,因为降低了效率。 但是最终还是要从人的逻辑转变为机器的逻辑,这样程序才能最终运行。而虚拟机就是一个负责**逻辑转换**的角色。当然,虚拟机除了做转换,还承担了更多的责任,比如编译优化,内存管理等。 除此之外,虚拟机也有自己的逻辑,如虚拟机的指令集和执行引擎。我们要做的就是了解“虚拟机自己特有的逻辑 和 是如何实现人和机器逻辑的转换的”

  1. java虚拟机(既然要深入理解虚拟机,最起码对虚拟机得有个概念吧)
    • 一句话:jvm是一个图灵完备的程序,架在操作系统之上,用来执行字节码
    • 说说我的理解吧。我们先看没有虚拟机的情况,程序先被编译为机器码,然后在操作系统指定的规则下获取其需要的硬件资源来运行直到结束。
    • 然后如果有虚拟机呢?程序先被编译为字节码,然后虚拟机来执行字节码,虚拟机和操作系统交互,申请需要的硬件资源来运行程序直到结束

5.总结:
* jvm屏蔽了os的差异,向外提供一致的接口从而保证了java的跨平台特性。假设不使用虚拟机,针对每个操作系统都开发一个编译器行不行?可以,但是在不同的平台上执行都需要重新编译,而有了虚拟机,只需要编译为字节码就可以在不同的平台运行
* 而且虚拟机可以进一步对代码进行优化,虚拟机的改进也能提升执行速度。
* 操作系统就是对硬件资源进行管理,然后提供运行环境和开发接口。要想在该操作系统上运行,开发就得使用这个操作系统的开发接口,根据这个操作系统的规则来开发(如硬件厂商也得根据操作系统的规矩来开发驱动)。

《深入理解Java虚拟机学习总结》

知识梳理

一:自动内存管理机制

1:概述:程序运行的时候需要从外存加载到内存中去。随着进程状态切换,虚拟内存机制等可能又需要从内存换到外存。这种从进程整体的角度来看的内存管理由操作系统来负责。在程序当中也有大量的逻辑需要分配和释放内存。如变量的声明和使用,这个操作系统是不管的,需要编写程序时自己来管理,而jvm承担了这个责任。

2:虚拟机对内存的自动管理:其实就是通过对无用对象的内存空间的回收和整理过程。你想想啊,内存管理不过就是分配和回收吗。

3: 分配很easy,new个对象就分配了,关键就在于怎么回收以重用内存资源:
* 大概就是:垃圾收集主要针对java堆,因为永久代也就是方法区中的数据很少改变。虚拟机栈和本地方法栈中的内存分配在编译期可知,所这几个区域的内存分配和回收具有确定性,不必过多考虑。
* 根据对象的存活周期将内存划分为几块,进行分代内存回收。java堆分为老生代(标记整理)和新生代(复制算法)。由于新生代对象存活时间短,收集频繁。用复制算法,因为存活对象少,复制存活对象到survivor区域开销小。反之,老年代对象存活时间长,存活对象多,收集不频繁。所以用标记整理,这样不需要为survivor额外分配空间,提高内存利用率。
* 内存分布结构:虚拟机栈,本地方法栈,程序计数器为线程私有。方法区和java堆为线程共享。

《深入理解Java虚拟机学习总结》

二:虚拟机执行子系统

概述:我们知道java源代码被编译为class文件,然后虚拟机执行class文件,将class文件翻译为本地机器吗,最终执行。

我们还知道,cpu有一个微指令集,每个指令就是一个完成特定功能的门电路。然后所有的代码翻译为机器码后不过都是这些指令的线性组合而已。cpu通过一次次取指令来顺序执行机器码。

类比java虚拟机。既然虚拟机虚拟了硬件环境和完整的计算机系统。那么虚拟机当然也有自己的指令集和运行其指令集的机制。只不过它的这些指令集和运行机制最终又都被转换为真正的cpu的指令集和执行而已。

由于java是面向对象的语言,所以java虚拟机肯定是根据面向对象的特性来设计的,所以有类的加载机制。这和cpu是不一样的(开个脑洞,能不能设计制造一个支持面向对象代码结构的cpu)。

1:那么首先字节码对于虚拟机就类似于汇编代码对于cpu。也就是说字节码该如何组成是根据虚拟机的规则来形成的,然后虚拟机执行字节码。

2:我们知道程序由代码和数据组成。也就是由数据和处理数据的逻辑组成。如果将数据和处理数据的逻辑分离,就是面向过程。如果把数据和处数据的逻辑聚合在一起,看成一个整体(对象)就是面向对象。

由于面向过程是从计算机的角度来思考问题,所以最终生成的汇编代码和cpu执行的时候肯定没有对象的概念了。
面向过程是从人对现世界理解的角度来思考问题。java是面向对象的语言,所以java虚拟机执行的过程中包含了对象的概念。所以就有了类加载机制

所谓类加载就是将字节码加载到内存中供虚拟机使用。需要说的是类加载是按需加载和用类加载器加载。其他的具体细节可以看书。

3:接着就需要读取指令进行执行了,java虚拟机是采用基于栈的执行引擎。

什么是基于栈的执行引擎?
* 首先指令集中的指令就是对操作数进行算数运算和逻辑运算(不然为什么叫电子计算机呢)。因为程序的逻辑就是对数据进行处理吗。
* cpu是基于寄存器的执行结构,也就是它执行每个指令的时候将操作数保存的寄存器当中。
* 而基于栈呢就是将操作数保存到栈当中去,从栈中取操作数运算再将结保存到栈中。如果你做过数据结构中的一道题目:利用栈来实现表达式求值的话,应该能明白,如果没有,建议去做一下这道题。

三:程序编译和代码优化

概述:我们知道源代码—>字节码—>机器码的2次转换过程。所以当然可以在这两次转换过程中进行优化,产生更高质量的结果代码。

在源代码—>字节码期间的优化并非优化程序的运行效率。javac做了许多针对java语言编码过程的优化措施改善程序员的编码风格和提高编码效率。如擦除泛型类型信息类型信息。

在字节码—>机器码的阶段中字节码编译本地代码,优化就是把那些频繁执行的代码编译为本地代码以提高运行效率

四:高效并发

1: 概述:所谓并发,就是多个线程可以同时执行。在单处理中,这需要通过时间片轮转技术来模拟实现,由于cpu的执行速度远大于人的反应速度,所以在人的眼里看到的和真实的并发并无太大区别。

而在多处理器的环境下,多个处理器可以同时运行,同时每个处理器还可以采用时间片轮转技术。这样实现了并行。

2: 如何并发?

无论是单处理器还是多处理器。实现并发就需要解决以下问题:

  • 每个线程都要占用处理器,所以就需要调度线程,给每个线程分配合理的执行时间
  • 不同的线程可能会访问相同的资源,此时就面临资源的竞争,如何保证这些线程在竞争的过程中不发生死锁。不同的线程可能都要改变同一个变量,那如何保证这个变量被正确的更新
  • 线程间有时候需要合作完成问题,所以进程间还面临过着如何通信和同步的问题。

那么java如何来解决这些问题呢?

  • 对于线程调度,就java采用抢占式的调度方式。
  • 对于进程间可能死锁的问题,采用了全局资源排序的策略。就是线程要要按照一定的顺序来获取锁。这是死锁预防的策略,通过让产生死锁的充分条件循环等待无法发生来预防死锁的产生。
  • 同步问题,如对于如何保证资源被正确的更新。则使用了和操作系统相同的概念。使用互斥的方式来保证同一时刻只有一个线程对临界区的资源进行访问。当该线程退出临界区的时候,其他线程才可以进入。具体上采用了锁的机制和工作内存.—->也就是说线程不能在自己的工作内存中肆意修改共享变量,—>线程的执行顺序还是要靠同步代码来约束的—->然后才能保证主存和工作内存中的变量被正确更新。
  • 对于线程间的通信。可以通过主内存来进行。
  • 所谓高效,就是对与锁的使用上实现了一些优化—>因为使用锁会影响效率—>每次只要有限个线程可以使用资源,其他的都要排队—>当然影响效率了。这些优化策略包括编译时通过判断取消掉那些不必要的锁;先不加锁,等到出现了问题再进行补偿等.
    《深入理解Java虚拟机学习总结》
    原文作者:java虚拟机
    原文地址: https://blog.csdn.net/haiboself/article/details/53220344
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞