JVM的运行过程和Java回收机制(转)

  在阅读张孝祥的《Java就业培训》一书中,里面关于JVM的运行过程和Java回收机制的描述挺不错,特此摘录下来。

   Java编译程序将Java源程序翻译为JVM可执行代码--Java字节码。这一编译过程同c/c++的编译有些不同。当C编译器编译生产一个对象的代码时,该代码是为在某一特定硬件平台运行而产生的。因此,在编译过程中,编译程序通过查表将所有对符号的引用转换为特定的内存偏移量,以保证程序运行。Java编译器却不将对变量和方法的引用编译为数值引用,也不确定程序执行过程中的内存布局,而是将这些符号引用信息保留在字节码中,由解释器在运行过程中创立内存布局,然后再通过查表来确定一个方法所在的地址。这样就有效地保证了Java的可移植性和安全性。

  运行JVM字节码的工作是由解释器来完成的。解释执行过程分三步进行:代码的装入、代码的校验和代码的执行。装入代码的工作由“类装载器(class loader)”完成。类装载器负责装入运行一个程序需要的所有代码,这也包括程序代码中的类所继承的类和被其调用的类。当类装载器装入一个类时,该类被放在自己的名字空间中。除了通过符号引用自己名字空间以外的类,类之间没有其他办法可以影响其他类。在本台计算机上的所有类都在同一地址空间内,而所有从外部引进的类,都有一个自己独立的名字空间。这使得本地类通过共享相同的名字空间获得较高的运行效率,同时又保证它们与从外部引进的类不会相互影响。当装入了运行程序需要的所有类后,解释器便可确定整个可执行程序的内存布局。解释器为符号引用与特定的地址空间建立对应关系及查询表。通过在这一阶段确定代码的内存布局,Java很好地解决了由超类改变而使子类崩溃的问题,同时也防止了代码对地址的非法访问。

  随后,被装入的代码由字节码校验器进行检查。校验器可发现操作数栈溢出、非法数据类型转化等多种错误。通过校验后,代码便开始执行了。

  Java字节码的执行有两种方式:

  (1)即时编译方式:解释器先将字节码编译成机器码,然后再执行该机器码。

  (2)解释执行方式:解释器通过每次解释并执行一小段代码来完成Java字节码程序的所有操作。

  通常采用的第二种方法。由于JVM规格描述具有足够的灵活性,这使得将字节码翻译为机器代码的工作具有较高的效率。对于那些对运行速度要求较高的应用程序,解释器可将Java字节码即时编译为机器码,从而很好地保证了 Java代码的可移植性和高性能。

  为了便于读者更加容易地理解,我们用下图来概括JVM(虚拟机)的运行过程。

JVM运行过程

 

  垃圾回收器的描述如下:

  Java的一个重要特点就是具有一个垃圾回收器,并且能够自动回收垃圾,这也是Java相对于其他语言有优势的地方。

  Java类的实例对象和数组所需的存储空间是在堆上分配的,解释器具体承担为类实例分配空间的工作。解释器在为一个实例对象分配完成存储空间后,便开始记录对该实例对象所占用的内存区域的使用。一旦对象使用完毕,便将其回收到垃圾箱中。

  在Java语言中,除了new语句外没有其他办法为一个对象申请和释放内存。对内存进行释放和回收的工作是由Java运行系统承担的,这允许Java运行系统的设计者自己决定碎片回收的方法。在SUN公司开发的Java解释器和HOT JAVA环境中,碎片回收用后台线程的方式来执行,这不但为运行系统提供了良好的性能,而且使程序设计人员摆脱了自己控制内存使用的风险。Java的自动垃圾回收功能解决了两个最常见的应用程序错误:内存泄露和无效内存的使用。

  初始化的重要性是不言而喻的,程序员们都能体会到。可是清理垃圾数据的重要性常常被忽视,当程序的某个部件完成使用后,程序员往往都弃置不顾,这是很危险的,这些垃圾会占据系统资源,一直到系统资源(尤其是内存)被耗尽。Java提供了一种叫做垃圾回收的机制来避免程序员忽略垃圾的处理,Java自动帮我们完成垃圾回收的工作,而不需程序员再去考虑。

  在Java程序运行过程中,一个垃圾回收器会不定时地被唤起检查是否有不再被使用的对象,并释放它们所占用的内存空间。垃圾回收器的启用不由程序员控制,也无规律可循,并不会一产生了垃圾,它就被唤起,甚至有可能到程序终止,它都没有启动的机会。因此这并不是一个很可靠的机制,这或许不是坏事,因为垃圾回收器会给系统资源带来额外负担。它被启用的几率越小,带来额外负担的几率也就越小,当然如果它永远都不被启动,也就永远不必付出额外的代价了。

  不同的Java虚拟机采用不同的回收策略,一般有两种比较常用,一种叫做复制式回收策略。这种策略的执行模式是先将正在运行中的程序暂停,然后把正在被使用的所有对象从他们所在的堆内存里复制到另一块堆内存,那些不再被使用的对象所占据的内存空间就被释放掉。

  这种机制需要两块堆内存用于将内存中的内容搬运复制,这就需要维护所需内存数量的两倍的内存空间,更麻烦的是即使程序只产生了少量垃圾甚至没有垃圾,回收器仍然会把堆内存里的内容复制到另一块堆内存中,这就使得这种策略效率低下,为解决这种问题,另一种叫做“自省式”的策略被采用。

  自省式回收器检测所有正在使用的对象,并为它们标注,完成这项工作后再将所有不再被使用的对象所占据的内存空间一次释放。可想而知,这种方式的速度仍然很慢,不过如果程序只产生少量垃圾甚至不产生垃圾时,这种策略就极具优势了。

  这两种方式颇具互补性,因此在一些JVM里两种方式被有机地结合运用,在实际应用中,JVM会监督者两种模式的运行效率,如果程序中的对象长期被使用,JVM就转换至“自省式”回收模式,而当产生大量垃圾或对象所占内存不连续情况严重时,又会转换至“复制式”模式,如此循环,实现两种机制的交互。

posted @ 2017-06-06 21:46  reedom1991  阅读(156)  评论(0编辑  收藏  举报