如果你尚未阅读深入java虚拟机,推荐你先阅读深入理解计算机系统。
如果你已经阅读过深入理解计算机系统,推荐你阅读深入java虚拟机。
如果这两本书你都阅读过或者都在阅读,我就不知道说什么好了。
java虚拟机(jvm)是建立于一个本地系统之上沙箱之中。
这个基础天生便决定了java的平台平台无关性。
我们所编写的java程序调用java api,由java api调用 本地方法(动态库)操作本地系统。
由此带来了内存管理,安全管理等一系列问题。
java api没有通过提供强制装换指针或者通过指针运算直接访问内存的方法。
java使用自动垃圾回收机制,通过停止对一个java对象的引用来促使垃圾收集器自动回收这个对象所占据的内存。
java使用数组边界检查而防止出现因此而引发的内存冲突。
java的平台无关性
java虚拟机规范规定了java虚拟机中基础类型的规格,因此通过使用不同平台的java虚拟机,而跨越不同的系统平台实现。
因此编写java程序一个很重要的原则就是不要直接或者间接调用不属于java api的本地方法。
对java虚拟机的依赖
编写平台独立的java程序时候,要遵循两条原则:
1 不要依赖及时终结来达到程序的正确性 。
不同的java虚拟机有不同的垃圾回收机制的实现。
2 不要依赖线程的优先级来达到程序的正确性。
不同的java虚拟机有不同的线程优先级的实现。为了保障多线程java程序的平台无关性,必须依赖同步机制而不是优先级机制来协调线程。
java沙箱机制是java程序运行在一个受限的内存空间。
例如某个java程序产生了outofmemory的异常,只会导致这个java程序终止。并不会导致其他java程序或者其他应用程序终止。
java沙箱由以下几部分组成:
类装载器体系结构在三个方面对java沙箱起作用:
1.防止恶意代码去干涉善意代码。
2.守护了被信任的类库的边界
3.将代码归入了保护域,该保护域确定了代码可以进行哪些操作。
类装载器通过为不同的类提供不同的类装载命名空间来防止恶意代码干涉善意代码。相同命名空间内的类可以相互访问而不同命名空间内的类彼此并不可视因而也无法相互访问。
类装载器守护被信任的类库边界通过使用不同的类装载器装载不同的包而实现的。
class文件校验器用来检验class文件的字节码。防止阻止方法内的跳转指令跳到方法之外。
class文件会对一个class文件的字节码序列进行四次扫描
1 class文件的结构检查
java的class文件是以0xCAFEBABE开头作为文件类型标识的。主要检查class文件的完整性,保障这个字节序列遵从class文件的文件格式,这样才能编译成在方法区的内部数据结构。第2,3,4次扫描是在方法区内进行的。
2 类型数据的语言检查
检验器检查每个组成部分,确认其是否是所属类型的实例,它们的结构是否正确。其目的是为了确认每个方法描述符都是符合特定格式,格式正确的字符串。还会检查这个类本身是否符合特定的条件。总之,检验器会检验有编译器产生的class文件是否遵守一些java语言在编译时必须遵守的强制规则。
3字节码验证
字节码流代表了java的方法,它是由被称为操作码的单字节指令组成的序列。每个操作码后面都跟着一个或者多个操作数(这一点很类似汇编语言的指令)。操作数用于在java虚拟机执行操作码指令时提供所需的额外的数据。执行字节码时,依序执行每个操作码,这样就在java虚拟机内部形成了执行的线程。每个线程被授予自己的java栈。这个栈就是由不同的栈帧组成的。每一个方法的调用将获得一个自己的栈帧(其实就是一个内存片段),其中存储着局部变量和计算的中间结果。在栈帧中用于存储方法的中间结果的部分被称为该方法的操作数栈。操作码和它的(可选)操作数可能指存储在操作数栈中的数据或者存储在方法栈帧中的局部变量中的数据。这样,在执行一个操作码时,除了可以使用紧随其后的操作数,虚拟机还可以使用操作数栈中的数据,或存储在方法栈帧中的局部变量中的数据,或两者都使用。
字节码验证要校验直接码流符合java的操作码和操作数能够被java虚拟机安全地执行。
4符号引用的验证
java虚拟机将追踪直接码流中的符号引用,并且确保符号应用的资源可用。如果该资源不可用并不立即抛出noclassdeffounderror错误,而是在运行时,这个类被首次引用时才抛出。
动态连接是将一个符号引用解析为直接引用的过程。当java虚拟机执行字节码时,如果遇到一个操作码,这个操作码第一次使用一个指向另一个类的引用,那么java虚拟机就要解析这个引用。
1)查找被引用的类,如果必要的话就装载它。
2)将符号引用替换为直接引用,例如一个指向一个类,方法或者字段的指针或者偏移量。