深入java虚拟机

如果你尚未阅读深入java虚拟机,推荐你先阅读深入理解计算机系统。

如果你已经阅读过深入理解计算机系统,推荐你阅读深入java虚拟机。

如果这两本书你都阅读过或者都在阅读,我就不知道说什么好了。

 

java虚拟机(jvm)是建立于一个本地系统之上沙箱之中。

这个基础天生便决定了java的平台平台无关性。

我们所编写的java程序调用java api,由java api调用 本地方法(动态库)操作本地系统。

由此带来了内存管理,安全管理等一系列问题。

java的内存管理

java api没有通过提供强制装换指针或者通过指针运算直接访问内存的方法。

java使用自动垃圾回收机制,通过停止对一个java对象的引用来促使垃圾收集器自动回收这个对象所占据的内存。

java使用数组边界检查而防止出现因此而引发的内存冲突。

 

java的平台无关性

java虚拟机规范规定了java虚拟机中基础类型的规格,因此通过使用不同平台的java虚拟机,而跨越不同的系统平台实现。

因此编写java程序一个很重要的原则就是不要直接或者间接调用不属于java api的本地方法。

 

对java虚拟机的依赖

编写平台独立的java程序时候,要遵循两条原则:

1 不要依赖及时终结来达到程序的正确性 。

   不同的java虚拟机有不同的垃圾回收机制的实现。

2 不要依赖线程的优先级来达到程序的正确性。

  不同的java虚拟机有不同的线程优先级的实现。为了保障多线程java程序的平台无关性,必须依赖同步机制而不是优先级机制来协调线程。

 

java的安全管理机制

java沙箱机制是java程序运行在一个受限的内存空间。

例如某个java程序产生了outofmemory的异常,只会导致这个java程序终止。并不会导致其他java程序或者其他应用程序终止。

 

java沙箱由以下几部分组成:

类装载器体系结构

class文件检验器

内置于java虚拟机(及语言)的安全特性

安全管理器及java api


类装载器体系结构

类装载器体系结构在三个方面对java沙箱起作用:

1.防止恶意代码去干涉善意代码。

2.守护了被信任的类库的边界

3.将代码归入了保护域,该保护域确定了代码可以进行哪些操作。

类装载器通过为不同的类提供不同的类装载命名空间来防止恶意代码干涉善意代码。相同命名空间内的类可以相互访问而不同命名空间内的类彼此并不可视因而也无法相互访问。

类装载器守护被信任的类库边界通过使用不同的类装载器装载不同的包而实现的。

 

class文件校验器

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)将符号引用替换为直接引用,例如一个指向一个类,方法或者字段的指针或者偏移量。

 

 





java虚拟机内置的安全特性

 

java虚拟机在执行字节码时还进行其他一些内置的安全机制的操作。

1.类型安全的引用转换

2.结构化的内存访问(无指针算法)

3.自动化垃圾收集(不必显式地释放被分配的内存)

4.数组边界检查

5.空引用检查

 

java程序只能使用类型安全,结构化的方法去访问内存

这样可以避免黑客知道一个类装载器在内存中的位置,赋予一个指针指向那块内存,从而对类装载器的数据进行操作,而达到破坏安全系统的目的。


未指明运行时数据空间在java虚拟机内部是怎样分布的

运行时数据空间是指一些java虚拟机用来存储一个运行时java程序所需的内存空间。包括:java栈(每个线程一个),一个存储字节码的方法区,以及一个垃圾收集堆(用来存储由运行的程序创建的对象)。java虚拟机规范并未决定使用什么数据结构来表示运行时的数据空间,并且存放在内存中的哪个位置。这样,黑客就不能够凭借class文件中的内容,知道内存中数据的内存布局。这样,即使能够突破java虚拟机的内存访问限制,也会无法找到他们想找到的内容。但是可以使用本地方法来达到破坏内存的目的。


异常的结构化错误处理

当一个违反安全的行为发生时,java虚拟机会做一些异常的结构化处理,java虚拟机将抛出一个异常或者错误,而不是崩溃。这个异常或者错误将导致这个线程死亡,而不是整个系统陷入瘫痪。异常机制使程序能够将控制从发生异常的地方转到处理这个异常的情况。

 

安全管理器和java api

java安全模型的前三个组成部分-类装载器体系结构,class文件检验器和java内置的安全特性一起达到一个共同的目的:

保持java虚拟机实例和它正在运行的应用程序的内部完整性,使得它们不受恶意或者有漏洞的代码侵犯。

 

java安全模型的第四部分:安全管理器:主要用于保护虚拟机的外部资源不被虚拟机内运行的文件或者有漏洞的代码侵犯。

安全管理器是一个单独的对象,在运行的java虚拟机中,它在访问控制--对于外部资源的访问控制--中起中枢作用。

 

安全管理器负责两个方面的工作:

1)说明一个安全策略

2)执行这个安全策略

 

java安全模型的不足

无法阻止恶意移动代码不断分配内存直到内存耗尽

无法阻止恶意代码不断生成线程

 

其实不仅是恶意代码,我们自己写代码的时候也要避免不断分配内存和不断生成线程的现象的产生。


你可能感兴趣的:(深入java虚拟机)