在 C 语言里面我们想执行一段自己编写的机器指令的方法大概如下:
1
2
3
|
typedef
void
(*FUNC)( int );
char *
str = "your
code" ;
FUNC
f = (FUNC)str;
|
也就是说,我们完全可以做一个工具,从一个文件中读入指令,然后将这些指令运行起来。上面代码中“编好的机器指令”当然指的是能在CPU上运行的,如果这里我还实现了一个翻译机器:从自己定义的格式指令翻译到CPU指令,那么就可以执行根据自定义格式的代码了。那么上面这段代码是不是相当于最简单的一个虚拟机了?下面来看JVM的总体结构:
ClassLoader的作用是装载能被JVM识别的指令(当然不只是从磁盘文件或内存去装载),那么我们先了解一下该格式:
魔数以及版本就不说了(满大街的文件格式都是这个东西),接着的便是常量池,其中无非是两种东西:
1. 字面常量(比如Integer、Long、String等);
2. 符号引用(方法是哪里的?什么样的?);
而我们知道,在JVM里面Class都是根据全限定名去找的,那么方法的描述当然也应该如此,那么就得到这些常量之间的关系如下:
在接下来的“访问权限”中表明了该Class是public还是private等,而this&super&interface则表面了“本类”、“继承自哪个类”、“实现了哪些接口”,实际上这里只是保存了代表这些信息的CONSTANT_Class_info的下标(u2)。
感觉这里的NameIndex和DescriptorIndex加起来和NameAndType有点像,那么为什么不直接用一个NameAndType的索引值表示?MethodInfo和FieldInfo之间最大的不同点就是Attributes。比如FieldInfo的属性表中存放的是变量的初始值,而MethodInfo的属性表中存放的则是字节码。那么我们来依次看这些Attributes,首先是Code:
有几个有意思的地方:
1. 从Class文件中可以知道在执行的过程中栈的深度;
2. 对于非静态方法,编译器会将this通过参数传递给方法;
3. 异常表中记录的范围是指令的行数(而不是源代码的);
4. 这里的异常是指try-catch中的,而与Code同级的异常表中的则是指throws出去的;
Exceptions则非常简单:
LineNumberTable保存了字节码和源码之间的关系,结构如下:
LocalVariableTable描述了栈帧中局部变量表的变量和源代码中定义的变量之间的关系,结构如下:
SourceFile指明了生成该Class文件的Java源码文件名(比如在一个Java文件中申明了很多类的时候会生成很多Class文件),结构如下:
Deprecated和Synthetic属性只存在“有”和“没有”的区别:
1. Deprecated:被程序作者定为不再推荐使用,通过@deprecated注释说明;
2. Synthetic:表示字段或方法是由编译器自动生成的,比如<init>;
这也就是为什么Code属性后面会有Attribute的原因?
类加载的时机就很简单了:在用到的时候就加载(废话!)。下来看一下类加载的过程:
执行上面这段过程的是:ClassLoader,这个东西还是非常重要的,在JVM中是通过ClassLoader和类本身共同去判断两个Class是否相同。换句话说就是:不同的ClassLoader加载同一个Class文件,那么JVM认为他们生成的类是不同的。有些时候不会从Class文件中加载流(比如Java Applet是从网络中加载),那么这个ClassLoader和普通的实现逻辑当然是不一样的,通过不同的ClassLoader就可以解决这个问题。
但是允许使用不同的ClassLoader又引发了新的问题:如果我也声明了一个java.lang.Integer,但是里面的代码非常危险,怎么办?这里就引出了双亲委派模式:
除了顶层的启动类加载器外,其余的类加载器都应该有父类加载器(通过组合实现),它在接到加载类的请求时优先委派给父类加载器去完成。
这样的话,在加载java.lang.Integer的时候会优先使用系统的类加载器,这样就不会加载用户自己写的。在Java程序员看到有3种系统提供的类加载器:
1. Bootstrap ClassLoader:负责加载<JAVA_HOME>\lib目录中的类库,无法被Java程序直接引用;
2. Extension ClassLoader:负责加载<JAVA_HOME>\lib\ext,开发者可以直接使用;
3. Application ClassLoader:加载ClassPath上所指定的类库,如果没有自己定义过自己的类加载器则会使用它;
这样默认的类会是有Application ClassLoader去加载类,然后如果发现要使用新的类型的时候则会递归地使用Application ClassLoader去加载(在前面的加载过程中提到)。这样,只有在自己的程序中能使用自己编写的ClassLoader去加载类,并且这个被加载的类是不能被别人使用的。
双亲委派模式不是一个强制性的约束,而是Java设计者推荐给开发者的类加载实现方式。双亲委派模式出现过的3次“破坏”:
1. 为了兼容JDK 1.0,建议使用者去覆盖findClass方法;
2. 在基础类要访问用户类的代码会出现问题(比如JNDI):线程山下文类加载器;
3. 用户的一些需求,比如HotSwap、OSGI等;
加载完完成后,接下来就要看程序是怎么运行的。栈帧是用于支持虚拟机进行方法调用和执行,帧的意思就是一个单位,在调用其他方法的时候会向栈中压入栈帧,结构如下:
在Class文件编译完成之后,在运行的时候需要多少个局部变量就已经确定(在前面Class文件中也已经看到过了),那么这里需要注意这个特性可能会引发GC(具体如何引发就不在这里细说了)。在栈中,总是底层的栈去调用高层的栈(并且一定的相邻的),那么他们在参数传递(返回结果)的时往往是通过将其压入操作数栈,有些虚拟机为了提高这部分的效率使得相邻栈帧“纠缠”在一起:
那么我们接下来要去看是方法是如何执行的,第一个问题就是执行哪个方法?在“面向过程”的编程中似乎不存在在个问题,但是在Java OR C++中这都是比较蛋疼的一个问题。原因就是平时不会这么用,但是你必须去搞明白= =。JVM确定目标方法的时候有两种方法:
1. 静态分派:根据参数类型和方法名称来决定调用哪个方法。但是,并不是说没有发现匹配的类型就报错,比如有:func(int a),而在调用func(‘a’)的时候也会调用该方法(当然是在没有func(char a)的前提下),这样给人的关键就有点像一个处理的链条。不管多么复杂,这些都是在编译期间确定的,因为这里是向上找的。
2. 动态分派:最普遍的就是Interface a = new Implements(),a调用方法到底应该是哪个类的在编译期间是无法确定的。其实动态分派实现起来也很简单:在调用方法的时候先拿到对象的实际类型。
其实“静态”和“动态”给人的感觉还是比较模糊的,“静态分派”给人的感觉是根据参数的类型向上查找方法,“动态分派”给人的感觉则是根据实例的真实类型向上查找。虚拟机优化动态分派的效率一般是为类在方法区中建立一个虚方法表:
虚方法表中存放各个方法实际入口地址,如果某个方法在子类中没有被重写,那么子类的虚方法表里面的地址入口和父类相同方法的地址入口是一致的,都指向父类的实现入口。如果子类重写了这个方法,子类方法表中的地址将会被替换为指向子类实现版本的入口地址。其实往简单里说,就是一个预处理。
分享到:
相关推荐
深入理解 Java 虚拟机笔记 Java 虚拟机(JVM)是 Java 语言的运行环境,它负责解释和执行 Java 字节码。下面是 Java 虚拟机相关的知识点: 虚拟机内存结构 Java 虚拟机的内存结构主要包括以下几个部分: * 方法...
Java 虚拟机(JVM)自动内存管理机制 Java 虚拟机(JVM)自动内存管理机制是 Java 语言的一大特色,它使得 Java 程序员无需手动管理内存,从而提高了开发效率和程序稳定性。JVM 自动内存管理机制主要通过 JVM 的...
《深入理解Java虚拟机》是Java开发者必读的经典之作,其中第三章主要探讨了Java安全方面的内容。在Java中,安全是一个至关重要的概念,因为Java的设计目标之一就是提供一种可以在不同环境中安全运行的代码机制。本章...
自己看《深入理解Java虚拟机》(第二版)所做的一些笔记。因为个人水平有限,能够理解的也只有前面几章的内容,后面的内容觉得看了也不是很理解,就没有记在里面。希望能对大家有所帮助,也希望能和大家一起进步。
### 学习深入理解Java虚拟机的前几章笔记 #### JVM内存模型 Java虚拟机(JVM)的内存模型主要分为两大类:线程共享区和线程私有区。 ##### 线程共享区 - **堆**:是所有线程共享的内存区域,在这里存放着对象实例...
读书笔记:学习周志明先生的深入理解Java虚拟机的笔记
读书笔记:java 虚拟机,深入理解Java虚拟机 JVM高级特性与最佳实践
读书笔记:周志明的《深入理解java虚拟机》读书笔记
读书笔记:深入理解Java虚拟机周志明
《JVM:深入理解Java虚拟机》是一本深入解析Java虚拟机工作原理和技术细节的经典书籍。这份学习笔记将涵盖JVM的关键概念、架构以及它如何影响Java程序的性能。我们将探讨以下几个方面: 1. **JVM概述** Java虚拟机...
读书笔记:深入理解Java虚拟机周志明著
读书笔记:深入理解Java虚拟机第二版_周志明读书笔记
读书笔记:深入理解Java虚拟机(周志明)源码及学习笔记
总的来说,"深入理解Java虚拟机读书笔记之:第3章 安全(2)"主要涵盖了Java安全体系的核心概念,包括类加载器、权限模型、安全管理器以及相关工具的使用。理解这些内容对于任何希望构建安全、可靠的Java应用程序的...
读书笔记:周志明老师《深入理解Java虚拟机》第三版阅读笔记
读书笔记:深入理解Java虚拟机JVM高级特性与最佳实践
读书笔记:深入理解Java虚拟机 JVM高级特性与最佳实践
读书笔记:《深入理解Java虚拟机JVM高级特性与最佳实践》笔记和理解。本笔记主要以图的形式记录。
读书笔记:深入理解Java虚拟机JVM高级特性与最佳实践第3版学习笔记