文档库 最新最全的文档下载
当前位置:文档库 › 深入Java虚拟机——本地方法栈

深入Java虚拟机——本地方法栈

深入Java虚拟机——本地方法栈
深入Java虚拟机——本地方法栈

深入Java虚拟机——本地方法栈

本地方法栈

前面提到的所有运行时数据区都是在Java虚拟机规范中明确定义的,除此之外,对于一个运行中的Java程序而言,它还可能会用到一些跟本地方法相关的数据区。当某个线程调用一个本地方法时,它就进入了一个全新的并且不再受虚拟机限制的世界。本地方法可以通过本地方法接口来访问虚拟机的运行时数据区,但不止于此,它还可以做任何它想做的事情。比如它甚至可以直接使用本地处理器中的寄存器,或者直接从本地内存的堆中分配任意数量的内存等等。总之,它和虚拟机拥有同样的权限(或者说能力)。

本地方法本质上是依赖于实现的,虚拟机实现的设计者们可以自由地决定使用怎样的机制来让Java程序调用本地方法。

任何本地方法接口都会使用某种本地方法栈。当线程调用Java方法时,虚拟机会创建一个新的栈帧并压入Java栈。然而当它调用的是本地方法时,虚拟机会保持Java栈不变,不再在线程的Java栈中压入新的帧,虚拟机只是简单地动态链接并直接调用指定的本地方法。可以把这看作是虚拟机利用本地方法来动态扩展自己。就如共Java虚拟机是的实现在按照其中运行的Java程序的吩咐,调用属于虚拟机内部的另一(动态链接的)方法。

如果某个虚拟机实现的本地方法接口是使用C连接模型的话,那么它的本地方法栈就是C栈。我们知道,当C程序调用一个C函数时,其栈操作都是确定的。传递给该函数的参数以某个确定的顺序压入栈,它的返回值也以确定的方式传回调用者。同样,这就是该虚拟机实现中本地方法栈的行为。

很可能本地方法接口需要回调Java虚拟机中的Java方法(这也是由设计者决定的),在这种情形下,该线程会保存本地方法栈的状态并进入到另一个Java栈。

图 5-13描绘了这种情况,就是当一个线程调用一个本地方法时,本地方法又回调虚拟机中的另一个Java方法。该图展示了Java虚拟机内部线程运行的全景图。一个线程可能在整个生命周期中都执行Java方法,操作它的Java栈;或者它可能毫无障碍地在Java栈和本地方法栈之间跳转。

如图 5-13所示,该线程首先调用了两个Java方法,而第二个Java方法又调用了一个本地方法,这样导致虚拟机使用了一个本地方法栈。图中的本地方法栈显示为一个连续的内存空间。假设这是一个C语言栈,期间有两个C函数,它们都以保卫在虚线中的灰色块表示。第一个C函数被第二个Java方法当作本地方法调用,而这个C函数又调用了第二个C函数。之后第二个C函数又通过本地方法接口回调了一个Java方法(第三个Java方法),最终这个Java方法又调用了一个Java方法(它成为图中的当前方法)。

就像其他运行时内存区一样,本地方法栈占用的内存区也不必是固定大小的,它

可以根据需要动态扩展或者收缩。某些实现也允许用户或者程序员指定该内存区的初始大小以及最大,最小值。

5.3.10 执行引擎

任何Java虚拟机实现的核心都是它的执行引擎。在Java虚拟机规范中,执行引擎的行为使用指令集来定义。对于每条指令、规范都详细规定了当实现执行到该指令时应该处理什么,但是却对如何处理言之甚少。在前面的章节中提到过,实现的设计者有权决定如何执行字节码,实现可以采取解释、即时编译或者直接用芯片上的指令执行,还可以是它们的混合,或任何你能想到的新技术。

和本章开头提到的对“Java虚拟机”这个术语有三种不同的理解一样,“执行引擎”这个属于也可以有三种解释:一个是抽象的规范,一个是具体的实现,另一个是正在运行的实力。抽象规范使用指令集规定了执行引擎的行为。具体实现可能使用多种不同的技术——包括软件方面、硬件方面或数种技术的集合。作为运行时实例的执行引擎就是一个线程。

运行中Java程序的每一个线程都是一个独立的虚拟机执行引擎的实例。从线程生命周期的开始到结束,它要么在执行字节码,要么在执行本地方法。一个线程可能通过解释或者使用芯片级指令直接执行字节码,或者间接通过即时编译器质性编译过的本地代码。Java虚拟机的实现可能用一些对用户程序不可见的线程,比如垃圾收集器。这样的线程不需要是实现的执行引擎的实例,所有属于用户运行程序的线程,都是在实际工作的执行引擎。

指令集方法的字节码流是由Java虚拟机的指令序列构成的,每一条指令包含一个单字节的操作吗,后面跟随0个或多个操作数。操作码表明需要执行的操作;操作数向Java虚拟机提供执行操作码需要的额外信息。操作码本身就已经规定了它是否需要跟随操作数,以及如果有操作数的话,它是什么形式的。很多Java 虚拟机的指令不包含操作数,仅仅是由一个操作码字节构成的。根据额操作码的需要,虚拟机可能除了跟随操作码的操作数之外,还需要从另外一些存储区域得到操作数。当虚拟机执行一条指令的时候,可能使用当前常量池中的项,当前帧的局部变量中的值,或者位于当前帧操作数栈顶端的值。

抽象的执行引擎每次执行一条字节码指令。Java虚拟机中运行的程序的每个线程(执行引擎实例)都执行这个操作。执行引擎取得操作码,如果操作码有操作数,取得它的操作数。它执行操作码和跟随的操作数规定的动作,然后再取得下一个操作码。这个执行字节码的过程在线程完成前将一直持续,通过从它的初始方法返回,或者没有捕获抛出的异常都可以标志着线程的完成。

执行引擎会不时遇到请求本地方法调用的指令。在这个时候,虚拟机负责试着发起这个本地方法调用。如果本地方法返回了(假设是正常返回,而不是抛出了一个异常),执行引擎会继续执行字节码流中的下一条指令。

可以这样来看,本地方法是Java虚拟机指令集的一种可编程扩展。如果一条指令请求一个对本地方法的调用,执行引擎就会调用这个本地方法。运行这个本地方法就是Java虚拟机对这条指令的执行。当本地方法返回了,虚拟机继续执行下一条指令。如果本地方法异常中止了(抛出了一个异常),虚拟机就按照好比是这条指令抛出这个异常一样的步骤来处理这个异常。

执行一条指令包含的任务之一就是决定下一条要执行的是什么指令。执行引擎决定下一个操作码时有三种方法。很多指令的下一个操作码就是在当前操作码和操作数之后紧跟的那个字节(如果字节码流里面还有的话)。另外一些指令,比如goto或者 return,执行引擎决定下一个操作码时把它当作当前执行指令的一部分。假若一条指令抛出了一个异常,那么执行引擎将搜索合适的catch子句,以决定下一个执行的操作码是什么。

有些指令可以抛出异常。比如,athrow指令,就明确地抛出一个异常。这条指令就是Java源代码中的 throw语句的编译后形式。每当执行一条athrow指令的时候,它都将抛出一个异常。其他抛出异常的指令都只有在满足某些特定条件的时候才抛出异常。比如,假若Java迅即发现程序试图用0除一个整数,它就会抛出一个ArithmeticException异常。这只有在执行四条特定的除法(idev,idiv,irem和lrem)的时候,或者计算int或者long的余数的时候,才可能发生。

Java虚拟机指令集的每种操作码都有助记符。使用典型的汇编语言风格,Java 字节码流可以用助记符跟着(可选的)操作数值来表示。

方法的字节码流和助记符的例子如下所示,考虑这个类里的doMathForever()方法:

public class Act {

public static void doMathForever(){

int i = 0;

for (;;) {

i += 1;

i+= 2;

}

}

}

doMathForever() 方法的字节码流可以被反汇编成下面的助记符。Java虚拟机

规范中没有定义正式的方法字节码的助记符的语法。下面显示的代码说明了本书所采用的用助记符表示字节码的方式。左边的列表表示每条指令开始时从字节码的开头开始算起的每天指令的字节偏移量;中间的列表表示指令和它的操作数;右边的列包含注释,用双斜杠隔开,如同Java源代码的格式。

这种表示助记符的方式和Sun的Java 2 SDK里的javap程序的输出很相似。使用javap可以查看任何class文件中方法的字节码助记符。请注意跳转地址是按照从方法起始开始算起的偏移量来给出的。Goto指令导致虚拟机跳转到从方法起始计算的位于偏移量2的指令。实际上操作数是负7.要执行这条指令,虚拟机在当前PC寄存器的内容上加上这个操作数。记过就是iinc指令的地址:偏移量2.为了让助记符更加易读,所看到的这条跳转指令后面的操作数已经是经过计算后的结果了。主机符显示的是“goto2”,而不是”goto-7”。

Java虚拟机指令集关注的中心是操作数栈。一般是把将要使用的值会压入栈中。虽然Java虚拟机没有保存任意值的寄存器,但每个方法都有一个局部变量集合。指令集实际的工作方式就是把局部变量当作寄存器,用索引来访问。不过,不同于iinc指令——它可以直接增加一个局部变量的值,要是用吧偶你在局部变量中的值之前,必须先将它压入操作数栈。

举例来说,用一个局部变量除另外一个,虚拟机必须把它们都压入栈,执行除法,然后把结果重新保存到局部变量。要把数组元素或对象的字段保存到局部变量中,虚拟机必须先把值压入栈,然后保存到局部变量中去。要把保存在局部变量中的值赋予数组元素或者对象字段,虚拟机必须按照相反的步骤操作。它首先必须把局部变量的值压入栈,然后从栈中弹出,再放入位于堆上的数组元素或对象字段中。

Java虚拟机指令集的设计遵循几个不同的目标,但它们之间是有冲突的。这几个目标就是本书的前面所描述的整个Java体系结构的目的所在:平台无关性、网络移动性以及安全性。

平台无关性是影响指令集设计的最大因素。指令集的这种以栈为中心、而非以寄存器为中心的设计方法,使得在那些只有很少的寄存器、或者寄存器很没有规律的机器上实现Java更便利,Intel80X86就是一个例子。由于指令集具有这种以栈为中心的特征,所以在很多平台体系结构上都很容易实现Java虚拟机。

Java 以栈为中心设计指令集的另一个动机是,编译器一般采用以栈为基础的结构向连接器或优化器传递编译的中间结果。Javaclass文件在很多方面都和C编译器产生的o文件或者.obj文件很相似,实际上他表示了一种Java程序的中间编译结果形式。对于Java的情况来,虚拟机是作为(动态)连接器使用的,也可以作为优化器。在Java虚拟机指令集设计中,以栈为中心的体系结构可以将运行时进行的优化工作与执行即时编译或者自适应优化的执行引擎结合起来。

在第4章中讲过,设计中一个主要考虑因素是class文件的紧凑性。紧凑性对于

提高在网络上传递class文件的速度是很重要的。在class文件中保存的字节码,除了两个处理表跳转的指令之外,都是按照字节对齐的。操作码的总数很小,所以操作码可以只占据一个字节。这种设计策略有助于class文件的紧凑,但却是以可能影响程序运行的性能为代价的。某些Java虚拟机实现,特别是那些在芯片上执行字节码的实现,但字节的操作码可能使得一些可以提高性能的优化无法实现。同样,假若字节码流是以字对齐而非字节对齐的话,某些实现可能会得到更好的性能。(实现可以重新对齐字节码流,或者在装载类的时候把操作码转换成更加有效的形式。字节码在class文件中是按字节对齐的,在抽象方法区和执行引擎的规范中也是这么规定的。不同的具体实现可以用它们喜欢的任何形式保存装载后的字节码流。)

指导指令集设计的另一个目标就是进行字节码验证的能力,特别是使用数据流分析器进行的一次性验证。Java的安全框架需要这种验证能力。在装载字节码的时候使用数据流分析器进行一次性验证,而非在执行每条指令的时候进行验证,这样做有助于提高执行速度。在指令集中体现这个目标的表现之一,就是绝大部分操作码都指明了它们需要操作的类型。

比如说,不是简单地采用一条指令(该指令从操作数栈中取出一个字并保存到局部变量中),Java虚拟机的指令集而是采用两条指令。一条指令是istore——弹出并保存int类型;另一条指令是fstore——弹出并保存float类型。在执行的时候这两条指令所完成的功能是完全一致的;弹出一个字并保存。要区分弹出并保存的到底是int类型还是float类型,只对验证过程有重要作用。

对于某些指令,虚拟机需要知道被操作的类型,以决定如果执行操作。比如,Java 虚拟机支持两种把两个字加起来并得到一个结果字的操作。一种是把字当作int处理,另一种是当作float处理。这两条指令的区别在于方便验证,同时也告诉虚拟机需要的整数操作还是浮点数操作。

有一些指令可以操作任何类型。比如说dup指令不管栈顶的字是什么类型都可以复制它。还有一些指令不操作有类型的值,比如goto。但是大部分指令都操作特定的类型。这种“有类型”的指令,可以使用助记符通过一个字符前缀来表明它们操作的类型。表5-2列举了不同类型的前缀。有一些指令不包含前缀,比如arraylength或instanceof,因为他们的类型是再明显不过的。Arraylength操作码需要一个数组引用。 Instanceof操作码需要一个对象引用。

操作数栈中的数值必须按照适合它们类型的方式使用。比如说压入4个int,但却把它们当作两个long来做加法,这是非法的。把一个float值从局部变量压入操作数栈,然后把它作为int保存到堆中的数组中去,这也是非法的。从一个位于堆中的对象字段压入一个double值,然后把栈中最顶端的两个字作为类型引用保存到局部变量,这也是非法的。Java编译器所坚持的强类型规则对Java虚拟机实现同样也是适用的。

当执行那些与类型无关的一般性栈操作指令时,实现也必须遵守一些规则。前面

讲过,不管是什么类型,dup指令压入栈中顶端那个字的拷贝。这条指令可以用在任何占据一个字的值类型上,如int,flot、引用或returnAddress。但是,如果栈顶包含的是long或者double类型,它们占据了两个连续的栈空间,这时使用dup就是非法的。位于栈顶的long或者double需要用 dup2指令复制两个字,在操作数栈中压入栈顶的两个字的拷贝。一般性指令不能用来切割双字值。

为了使指令集足够小,用单字节表示每一个操作码,但并不是在所有类型上都支持所有的操作。很多操作对byte、short和char都不支持。这些类型在从堆或者方法区转移到栈帧的时候被转换成 int,当它们被当作int来进行操作,然后在操作完成后重新保存到堆或方法去的时候再转换为byte、short或者char。

表5-3展示了Java虚拟机中保存的每个类型所对应的计算类型。这里,保存类型是堆中类型值所体现的形式。保存类型对应Java源代码中变量的类型。计算类型是这些类型在Java栈帧中体现的形式。

Java虚拟机实现必须以某种方法确保数值是被对应其类型的指令所操作。实现可以在类验证过程中就预先验证字节码,或者在执行的时候验证,或者采用前两种验证方式的混合方式。字节码验证在第7章详细描述。第10章到第20章详细描述了整个指令集。

执行技术实现可以使用多种执行技术:解释、即时编译、自适应优化、芯片级直接执行,这些在第1章中介绍过。关于执行技术要记住的最主要的一点就是,实现可以自由选择任何技术来执行字节码,只要它遵守Java虚拟机指令集的定义。

最有意义也是最迅速的执行技术之一是自适应优化。自适应优化已经在几种现有的Java虚拟机实现中使用了,如Sun的Hotspot虚拟机。它们都从早期虚拟机实现所使用的技术中得到了很多借鉴。最初的虚拟机每次解释一条字节码;第二代虚拟机加入了即时编译器,在第一次执行方法的时候先编译本地代码,然后执行这段本地代码。也就是说,不管什么时候调用方法,总是执行本地代码。自适应优化器搜集那些只在运行时才有效的信息,试图以某种方式把字节码解释和编译成本地代码结合起来,以得到最优化的性能。

自适应优化的虚拟机开始的时候对所有的代码都是解释运行,但是它会监视代码的执行情况。大多数程序花费80%~90%的时间用来执行10%~20%的代码,它们占整个执行时间的80%~90%。

当自适应优化的虚拟机判断出某个特定的方法是瓶颈的时候,它启动一个后台线程,把字节码编译成本地代码,非常仔细地优化这些本地代码。同时,程序仍然通过解释来执行字节码。因为程序没有中途挂起,并且只编译和优化那些“热区”虚拟机可以比传统的即时编译更注重优化性能。

自适应优化技术使程序最终能把原来占80%~90%运行时间的代码变为极度优化

的,静态链接的C++本地代码,而使用的总内存数并不比全部解释Java程序大多少。换句话说,就是更快了。自适应优化的虚拟机可以保留原来的字节码,等待方法从热区移出(程序的热区在执行的过程中可能会转移)。当方法变得不再是热区的时候,取消那些编译过的代码,重新开始解释执行那些字节码。

读者可能会注意到,自适应优化方法令Java程序运行得更快,它采取的办法和程序员用来提高程序性能的方法是很相似的。不同于通常的即时编译虚拟机,自适应优化的虚拟机并不进行“过早的优化”。自适应优化的虚拟机通过解释执行字节码开始,当程序运行的时候,虚拟机统计程序,找到程序的热区——就是那10%~20%的代码,它们花费了80%~90%的运行时间。就如同一个优秀的程序员那样,自适应优化的虚拟机只对那些对性能产生重大影响的代码进行仔细优化。

但是自适应优化的情况不止这些,它还有另外的着眼点。自适应优化器可以在运行时根据Java程序的特征进行微调——特别是对“设计良好”的Java程序。根据JavaSoftHotspot的经历 DavidGriswold的说法,“Java比C++更加面向对象。你可以测量它,可以发现方法调用的频度,动态派发的频度,等等。这些频度要比C++ 中高的多。”现在,在一个设计良好的Java程序中,这种方法调用和动态派发的频度更加高了,因为Java程序良好设计的尺度之一就是高效率、高产出的设计——换句话就是,使方法和对象更紧凑及内聚性更高。

这些Java程序的运行时特征,就是方法调用和动态派发的高频度发生,它们从两个方面影响性能。首先,每次动态派发都会产生相关的管理费用,其次,更重要的是方法调用降低了编译器优化的有效性。

方法调用会使优化器的有效性降低,因为优化器在不同的方法调用间不能够有效地工作,因此优化器在方法调用的时候就无法专注于代码了。方法调用频度越高,方法调用之间可以用来优化的代码就越少,优化器就变得越低效。

这个问题的标准解决方案就是内嵌——把被调用方法的方法体直接拷贝到发起调用的方法中。内嵌调出了方法调用,因此可以让优化器处理更多的代码。这可能令优化器工作更有效,代价就需要更多的运行时内存。

麻烦之处在于,在面向对象的语言(比如Java和C++)中实现内嵌,要比非面向对象的语言(比如C)更加困难,因为面向对象语言使用了动态派发。在Java 中比在C++中更加严重,因为Java的方法调用和动态派发的频度要比C++高得多。

一个C程序的标准优化静态编译器可以直接使用内嵌,因为每一个函数调用都有一个函数实现。对于面向对象语言来说,内嵌就变得复杂了,因为动态方法派发以为着一个函数调用可能有多个函数实现(方法)。换句话说,虚拟机运行时根据方法调用的对象类,可能会有很多不同的方法实现可供选择。

内嵌一个动态派发的方法调用,一种解决办法就是把所有可能在运行时被选择的方法实现都内嵌进去,这种思路的问题在于,如果有很多方法实现,就会让优化后的代码变得非常大。

自适应编译比静态编译的优点在于,因为它是在运行时工作的,它可以使用静态编译器所无法得到的信息。比如说,对于一个特定的方法调用,就算有30个可能的方法实现,运行时可能只会有其中的两个被调用。自适应方法就可以只把这两个方法内嵌,有效地减少过了优化后的代码大小。

线程 Java虚拟机规范定义了线程模型,这个模型的目标是要有助于在很多体系结构上都实现它。Java线程模型的一个目标就是使实现的设计者,在可能的情况下使用本地线程。否则,设计者可以在他们的虚拟机实现内部实现线程机制。在一台多处理器的主机上使用本地线程的好处就是,Java程序不同的线程可以在不同的处理器上并行工作。

Java线程模型的折中之一就是优先级的规范考虑最小公分母问题。Java线程可以运行于10个优先级的任何一个。级别1是优先级最低的,而级别10是最高的。如果设计者使用本地线程,他可以用合适的方法把10个Java优先级映射到机器本地的优先级上。Java虚拟机规范对于不同优先级别的线程行为,只规定了所有高优先级的线程会得到大多数的CPU时间。低级别的线程在级别高的线程没有被阻塞的时候也可能得到CPU时间,但是这没有任何保证。

规范没有假设不同优先级的线程采用时间分片方式。因为并不是所有的体系结构都采用时间片(在这里,时间分片的含义是:就算没有线程被阻塞,所有优先级的所有线程都会保证得到一些CPU时间)。就算在那些采用时间片的体系结构上,用来分配时间片给不同优先级线程的算法也存在非常大的差异。

第2章中讲到,程序的正确运行不能依靠时间分片。只有在向Java虚拟机给出提示,某个线程应该比其他线程使用更多的时间,这时候才使用线程优先级。要协调多线程之间的活动,应该使用同步。

任何Java虚拟机的线程实现都必须支持同步的两个方面:对象锁定,线程等待和通知。对象锁定使独立运行的线程访问共享数据的时候互斥。线程等待和通知使得线程为了达到同一个目标而互相协同工作。运行中的程序通过Java虚拟机指令集来访问上锁机制,还通过Object类的wait()方法、 notify()方法和notifyAll()方法来访问线程等待和通知机制。

在Java虚拟机规范中,Java线程的行为是通过术语——变量、内存和工作内存——来定义的。每一个Java虚拟机实例都有一个主存,用于保存所有的程序变量(对象的实例变量、数组的元素以及类变量)。每一个线程都有一个工作内存,线程用它保存所使用和赋值的变量的“工作拷贝”,局部变量和参数,因为他们是每个线程私有的,可以从逻辑上看成是工作内存或者主存的一部分。

Java虚拟机规范定义了许多规则,用来管理线程和主存之间的额低层交互行为。比如,一条规则声明:所有对基本类型的操作,除了某些对long类型和double 类型的操作之外,都必须是源自级的。再比如,如果两个线程竞争,对一个int 变量写了不同的两个值,就算不存在同步,变量最终会采用二者之一。变量不会包含一个不正确的值。或者说,如果一个线程赢得了竞争,把它要写的值先写入到了变量。但失败的那个线程也可以重写那个变量,覆盖那个以为自己“胜

利”的线程所写入的值。

这条规则也有例外情况,即任何没有生命为volatile的long或者double变量。某些实现可能把它们作为两个原子性的32位值对待,而非一个原子性的64位值。比如说,把一个非volatile的long保存到内存,可能是两次32位的写操作。这种对于long和double的非原子操作可能导致两个竞争性的线程在试图写入不同的值到一个long或者double变量时,最终得到的是一个不正确的结果。

虽然实现的设计者不是必须对非volatile的long和double进行原子处理,但Java虚拟机规范鼓励他们这么做。这种对long和double的非源自操作,对那条“对所有基本类型的操作都必须是原子级的”的规则而言,就是一个例外,这个例外的目的是,如果处理器不有效的支持和内存交换64位的值,线程模型也能经济的实现。将来这个例外可能被终止。然而现在,Java程序员必须确保通过同步来操作共享的long 和double。

基本上,管理低层线程行为的规则,规定了一个线程合适可以做及何时必须做以下的事情:

1. 把变量的值从主存拷贝到它的工作内存。

2. 把值从它的工作内存写回到主存。

在特定条件下,规则指定了精确的和可预言的读写内存的顺序。然而另外一些条件下,规则没有规定任何顺序。规则,是设计来让Java程序员利用可以预期的行为建立多线程程序,而给实现的设计者更多的灵活性的。这种灵活性使Java虚拟机实现的设计者从标准硬件和软件技术中得到好处,它们可以提高多线程程序的性能。

所有管理线程行为的低层规则的高层含义是:如果访问某个没有被同步的变量,允许线程用任何顺序来更新主存。不使用同步,多线程程序可能在某些Java 虚拟机实现上表现出令人惊讶的行为。但是通过正确的使用同步,可以创建多线程的Java程序,它们按照可以预期的方式,可以在任何Java 虚拟机上工作。

5.3.11 本地方法接口

并不强求Java虚拟机实现支持任何特定的本地方法接口。有些实现可以根本不支持本地方法接口,还有一些可能支持少数几个,每一个对应一种不同的需求。

Sun 的Java本地接口,或者成为JNI,是为可移植性准备的。JNI设计的可以被任何Java虚拟机实现支持,而不管它们使用何种垃圾洲际或者对象表示技术。这样它能使开发者在一个特定的主机平台上,把同样的(与JNI兼容的)本地方法二进制形式连接到任何支持JNI的虚拟机实现上。

实现设计者可以选择创建一些私有的本地方法接口,扩展或者取代JNI。为了实现可移植性,JNI在指针和指针之间,指针和方法之间使用了很多间接方法。为了得到最好的性能,实现设计者可以提供他们自己的低层本地方法接口,以便和他们所使用的特定实现结构能更加紧密地结合。设计者也可以提供比JNI 更能高层的本地方法就诶口,比如把Java对象加入到一种组建软件模型中。

为了做好工作,本地方法必须能够和Java虚拟机实例的某些内部状态有某种程度的交互。比如,本地方法接口允许本地方法完成下列部分或全部工作:

1. 传递或返回数据

2. 操作实例变量或者调用使用垃圾收集的堆中的对象的方法

3. 操作类变量或者调用类方法

4. 操作数组

5. 对堆中的对象加锁,以便被当前线程独占使用

6. 在使用垃圾收集的堆中创建新的对象

7. 装载新的类

8. 抛出新的异常

9. 捕获本地方法调用的Java方法抛出的异常

10. 捕获虚拟机抛出的异步异常

11. 指示垃圾收集器某个对象不再需要

设计一个提供这些服务的本地方法接口是非常复杂的,需要确认垃圾收集器没有释放那些正在被本地方法使用的对象。如果实现的垃圾收集器为了减少堆碎片移动了一个对象,本地方法设计必须保证下面二者之一:

1. 当对象的引用被传递给了一个本地方法之后,它可以移动

2. 任何其引用传递给了本地方法的对象都被钉住,直到本地方法返回,或者它表明自己已经完成了对象的操作。

3. 由此可见,本地方法接口和Java虚拟机内部工作纠缠在了一起。

JVM原理以及JVM内存管理机制

一、 JVM简介 JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,是通过在实际的计算机上仿真模拟各种计算机功能来实现的。JVM工作原理和特点主要是指操作系统装入JVM是通过jdk中Java.exe来完成, 首先来说一下JVM工作原理中的jdk这个东西, .JVM 在整个jdk中处于最底层,负责于操作系统的交互,用来屏蔽操作系统环境,提供一个完整的Java运行环境,因此也就虚拟计算机. 操作系统装入JVM是通过jdk中Java.exe来完成。 通过下面4步来完成JVM环境. 1.创建JVM装载环境和配置 2.装载JVM.dll 3.初始化JVM.dll并挂界到JNIENV(JNI调用接口)实例 4.调用JNIEnv实例装载并处理class类。 对于JVM自身的物理结构,我们可以从下图了解:

JVM的一个重要的特征就是它的自动内存管理机制,在执行一段Java代码的时候,会把它所管理的内存划分 成几个不同的数据区域,其中包括: 1. 程序计数器,众所周知,JVM的多线程是通过线程轮流切换并 分配CPU执行时间的方式来实现的,那么每一个线程在切换 后都必须记住它所执行的字节码的行号,以便线程在得到CPU 时间时进行恢复,这个计数器用于记录正在执行的字节码指令的地址,这里要强调的是“字节码”,如果执行的是Native方法,那么这个计数器应该为null; 2.

3. Java计算栈,可以说整个Java程序的执行就是一个出栈入栈 的过程,JVM会为每一个线程创建一个计算栈,用于记录线程中方法的调用和变量的创建,由于在计算栈里分配的内存出栈后立即被抛弃,因此在计算栈里不存在垃圾回收,如果线程请求的栈深度大于JVM允许的深度,会抛出StackOverflowError 异常,在内存耗尽时会抛出OutOfMemoryError异常; 4. Native方法栈,JVM在调用操作系统本地方法的时候会使用到 这个栈; 5. Java堆,由于每个线程分配到的计算栈容量有限,对于可能会 占据大量内存的对象,则会被分配到Java堆中,在栈中包含了指向该对象内存的地址;对于一个Java程序来说,只有一个Java堆,也就是说,所有线程共享一个堆中的对象;由于Java堆不受线程的控制,如果在一个方法结束之后立即回收这个方法使用到的对象,并不能保证其他线程是否正在使用该对象;因此堆中对象的回收由JVM的垃圾收集器统一管理,和某一个线程无关;在HotSpot虚拟机中Java堆被划分为三代:o新生代,正常情况下新创建的对象会被分配到新生代,但如果对象占据的内存足够大以致超过了新生代的容量限 制,也可能被分配到老年代;新生代对象的一个特点是最 新、且生命周期不长,被回收的可能性高;

Java虚拟机(JVM)参数配置说明

Java虚拟机(JVM)参数配置说明 在Java、J2EE大型应用中,JVM非标准参数的配置直接关系到整个系统的性能。 JVM非标准参数指的是JVM底层的一些配置参数,这些参数在一般开发中默认即可,不需要任何配置。但是在生产环境中,为了提高性能,往往需要调整这些参数,以求系统达到最佳新能。另外这些参数的配置也是影响系统稳定性的一个重要因素,相信大多数Java开发人员都见过“O utOfMem ory”类型的错误。呵呵,这其中很可能就是JVM参数配置不当或者就没有配置没意识到配置引起的。 为了说明这些参数,还需要说说JDK中的命令行工具一些知识做铺垫。 首先看如何获取这些命令配置信息说明: 假设你是windows平台,你安装了J2SDK,那么现在你从cmd控制台窗口进入J2SDK安装目录下的bin目录,然后运行java命令,出现如下结果,这些就是包括java.exe工具的和J VM的所有命令都在里面。 ----------------------------------------------------------------------- D:\j2sdk15\bin>java Usage: java [-options] class [args...] (to execute a class) or java [-options] -jar jarfile [args...] (to execute a jar file) where options include: -client to select the "client" VM -server to select the "server" VM -hotspot is a synonym for the "client" VM [deprecated] The default VM is client.

Java虚拟机工作原理(JVM)

As the Java V irtual Machine is a stack-based machine, almost all of its instructions involve the operand stack in some way. Most instructions push values, pop values, or both as they perform their functions. Java虚拟机是基于栈的(stack-based machine)。几乎所有的java虚拟机的指令,都与操作数栈(operand stack)有关.绝大多数指令都会在执行自己功能的时候进行入栈、出栈操作。 1Java体系结构介绍 Javaís architecture arises out of four distinct but interrelated technologies, each of which is defined by a separate specification from Sun Microsystems: 1.1 Java体系结构包括哪几部分? Java体系结构包括4个独立但相关的技术 the Java programming language →程序设计语言 the Java class file format →字节码文件格式 the Java Application Programming Interface→应用编程接口 the Java V irtual Machine →虚拟机 1.2 什么是JVM java虚拟机和java API组成了java运行时。 1.3 JVM的主要任务。 Java虚拟机的主要任务是装载class文件并执行其中的字节码。 Java虚拟机包含了一个类装载器。 类装载器的体系结构 二种类装载器 启动类装载器 用户定义的类装载器 启动类装载器是JVM实现的一部分 当被装载的类引用另外一个类时,JVM就是使用装载第一个类的类装载器装载被引用的类。 1.4 为什么java容易被反编译? ●因为java程序是动态连接的。从一个类到另一个类的引用是符号化的。在静态连接的 可执行程序中。类之间的引用只是直接的指针或者偏移量。相反在java的class文件中,指向另一个类的引用通过字符串清楚的标明了所指向的这个类的名字。

java虚拟机详解 免费

深入理解JVM 1 Java技术与Java虚拟机 说起Java,人们首先想到的是Java编程语言,然而事实上,Java是一种技术,它由四方面组成: Java编程语言、Java类文件格式、Java虚拟机和Java应用程序接口(Java API)。它们的关系如下图所示: 图1 Java四个方面的关系 运行期环境代表着Java平台,开发人员编写Java代码(.java文件),然后将之编译成字节码(.class文件)。最后字节码被装入内存,一旦字节码进入虚拟机,它就会被解释器解释执行,或者是被即时代码发生器有选择的转换成机器码执行。从上图也可以看出Java平台由Java虚拟机和Java应用程序接口搭建,Java 语言则是进入这个平台的通道,用Java语言编写并编译的程序可以运行在这个平台上。这个平台的结构如下图所示:

在Java平台的结构中, 可以看出,Java虚拟机(JVM) 处在核心的位置,是程序与底层操作系统和硬件无关的关键。它的下方是移植接口,移植接口由两部分组成:适配器和Java操作系统, 其中依赖于平台的部分称为适配器;JVM 通过移植接口在具体的平台和操作系统上实现;在JVM 的上方是Java的基本类库和扩展类库以及它们的API,利用Java API编写的应用程序(application) 和小程序(Java applet) 可以在任何Java平台上运行而无需考虑底层平台, 就是因为有Java虚拟机(JVM)实现了程序与操作系统的分离,从而实现了Java 的平台无关性。 那么到底什么是Java虚拟机(JVM)呢?通常我们谈论JVM时,我们的意思可能是: 1. 对JVM规范的的比较抽象的说明; 2. 对JVM的具体实现; 3. 在程序运行期间所生成的一个JVM实例。 对JVM规范的的抽象说明是一些概念的集合,它们已经在书《The Java Virtual Machine Specification》(《Java虚拟机规范》)中被详细地描述了;对JVM的具体实现要么是软件,要么是软件和硬件的组合,它已经被许多生产厂商所实现,并存在于多种平台之上;运行Java程序的任务由JVM的运行期实例单个承担。在本文中我们所讨论的Java虚拟机(JVM)主要针对第三种情况而言。它可以被看成一个想象中的机器,在实际的计算机上通过软件模拟来实现,有自己想象中的硬件,如处理器、堆栈、寄存器等,还有自己相应的指令系统。 JVM在它的生存周期中有一个明确的任务,那就是运行Java程序,因此当Java程序启动的时候,就产生JVM的一个实例;当程序运行结束的时候,该实例也跟着消失了。下面我们从JVM的体系结构和它的运行过程这两个方面来对它进行比较深入的研究。 2 Java虚拟机的体系结构 刚才已经提到,JVM可以由不同的厂商来实现。由于厂商的不同必然导致JVM在实现上的一些不同,然而JVM还是可以实现跨平台的特性,这就要归功于设计JVM时的体系结构了。 我们知道,一个JVM实例的行为不光是它自己的事,还涉及到它的子系统、存储区域、数据类型和指令这些部分,它们描述了JVM的一个抽象的内部体系结构,其目的不光规定实现JVM时它内部的体系结构,更重要的是提供了一种方式,用于严格定义实现时的外部行为。每个JVM都有两种机制,一个是装载具有合适名称的类(类或是接口),叫做类装载子系统;另外的一个负责执行包含在已装载的类或接口中的指令,叫做运行引擎。每个JVM又包括方法区、堆、Java栈、程序计数器和本地方法栈这五个部分,这几个部分和类装载机制与运行引擎机制一起组成的体系结构图为:

Elasticsearch Java虚拟机配置详解

JVM参数Elasticsearch默认值Environment变量 -Xms 256m ES_MIN_MEM -Xmx 1g ES_MAX_MEM -Xms and -Xmx ES_HEAP_SIZE -Xmn ES_HEAP_NEWSIZE -XX:MaxDirectMemorySize ES_DIRECT_SIZE -Xss 256k -XX:UseParNewGC + -XX:UseConcMarkSweepGC + -XX:CMSInitiatingOccupancyFraction 75 -XX:UseCMSInitiatingOccupancyOnly + -XX:UseCondCardMark (commented out) 首先你注意到的是,Elasticsearch预留了256M到1GB的堆内存。 这个设置适用于开发和演示环境。开发人员只需要简单的解压发行包,再执 行./bin/elasticsearch -f就完成了Elasticsearch的安装。当然这点对于开发来说非常棒,并且在很多场景下都能工作,但是当你需要更多内存来降低Elasticsearch负载的时候就不行了,你需要比2GB RAM更多的可用内存。

ES_MIN_MEM/ES_MAX_MEM是控制堆大小的配置。新的ES_HEAP_SIZE变量是一个更为便利的选择,因为将堆的初始大小和最大值设为相同。也推荐在分配堆内存时尽可能不要用内存的碎片。内存碎片对于性能优化来说非常不利。 ES_HEAP_NEWSIZE是可选参数,它控制堆的子集大小,也就是新生代的大小。 ES_DIRECT_SIZE控制本机直接内存大小,即JVM管理NIO框架中使用的数据区域大小。本机直接内存可以被映射到虚拟地址空间上,这样在64位的机器上更高效,因为可以规避文件系统缓冲。Elasticsearch对本机直接内存没有限制(可能导致OOM)。 由于历史原因Java虚拟机有多个垃圾收集器。可以通过以下的JVM参数组合启用: JVM parameter Garbage collector -XX:+UseSerialGC serial collector -XX:+UseParallelGC parallel collector -XX:+UseParallelOldGC Parallel compacting collector -XX:+UseConcMarkSweepGC Concurrent-Mark-Sweep (CMS) collector -XX:+UseG1GC Garbage-First collector (G1) UseParNewGC和UseConcMarkSweepGC组合启用垃圾收集器的并发多线程模式。UseConcMarkSweepGC自动选择UseParNewGC模式并禁用串行收集器(Serial collector)。在Java6中这是默认行为。 CMSInitiatingOccupancyFraction提炼了一种CMS(Concurrent-Mark-Sweep)垃圾收集设置;它将旧生代触发垃圾收集的阀值设为75.旧生代的大小是堆大小减去新生代大小。这告诉JVM当堆内容达到75%时启用垃圾收集。这是个估计的值,因为越小的堆可能需要越早启动GC。 UseCondCardMark将在垃圾收集器的card table使用时,在marking之前进行额外的判断,避免冗余的store操作。UseCondCardMark不影响Garbage-First收集器。强烈推荐在高并发场景下配置这个参数(规避card table marking技术在高并发场景下的降低吞吐量的负面作用)。在ElasticSearch中,这个参数是被注释掉的。 有些配置可以参考诸如Apache Cassandra项目,他们在JVM上有类似的需求。 总而言之,ElastciSearch配置上推荐: 1. 不采用自动的堆内存配置,将堆大小默认最大值设为1GB 2.调整触发垃圾收集的阀值,比如将gc设为75%堆大小的时候触发,这样不会影响性能。 3.禁用Java7默认的G1收集器,前提是你的ElasticSearch跑在Java7u4以上的版本上。JVM进程的内存结果 JVM内存由几部分组成: Java代码本身:包括内部代码、数据、接口,调试和监控代理或者字节码指令 非堆内存:用于加载类 栈内存:用于为每个线程存储本地变量和操作数

深入理解Java虚拟机笔记(带目录)

目录 1.虚拟机内存结构 (1) 2.对象在内存中的布局 (3) 3.判断对象是否死亡 (4) 4.引用的4中情况 (4) 5.垃圾收集算法 (5) 6.HotSpot虚拟机算法实现 (6) 7.如何在GC发生时让所有线程都要附近的安全点停下 (6) 8.垃圾收集器 (7) 9.GC日志 (9) 10.内存分配 (10) 11.Class类文件的结构 (10) 12.类的生命周期 (13) 13.类加载器 (15) 14.运行时栈帧的结构 (16) 15. 方法调用 (18) 16. 分派 (19) 17.虚方法表 (19) 18.Java内存模型(JMM) (19) 19.内存间的交互 (20) 20.volatile变量 (20) 21.原子性 (21) 22.可见性 (22) 23.有序性 (22) 24.先行发生原则 (22) 25.Java线程调度 (23) 26.线程的状态 (24) 27.线程安全 (25) 28.线程安全的实现方法 (26) 29.锁优化 (27) 30.编译优化技术 (29) 1.虚拟机内存结构 线程私有:虚拟机栈,本地方法栈,程序计数器 线程共享:堆,方法区(包括运行时常量池)

1.1程序计数器 当前程序锁执行的字节码行号指示器,记录下一条需要执行的 指令。 1.2虚拟机栈 生命周期与线程相同,每个方法在执行时都会创建一个栈帧。 方法执行的过程,就是栈帧入栈到出栈的过程。 栈帧用于存放局部变量表,操作数栈,动态链接,方法出口等 信息。 局部变量表存放了编译期可知的基本数据类型和对象引用。1.3 本地方法栈 为虚拟机使用到的Native方法服务。 目前HotSpot虚拟机将本地方法栈和虚拟机栈合二为一。 1.4堆 存放对象实例,所有线程共享。 1.5 方法区(永久代) 存放被虚拟机加载的类信息,常量,静态变量,即时编译器编 译后的代码等。

深入理解java虚拟机

深入理解java虚拟机 (一)虚拟机内存划分 Java虚拟机在执行Java程序时,会把它管理的内存划分为若干个不同的数据区。这些区域有不同的特性,起不同的作用。它们有各自的创建时间,销毁时间。有的区域随着进程的启动而创建,随着进程结束而销毁,有的则始终贯穿虚拟机整个生命周期。 Java虚拟机运行时内存区域主要分为七部分,分别是:程序计数器,Java虚拟机栈,本地方法栈,方法区,Java堆,运行时常量池,直接内存。 如上图所示(图片来源于网络): 蓝色区域包裹的部分为运行时几个数据区域: 白色的部分为线程私有的,既随着线程的启动而创建。每个线程都拥有各自的一份内存区域。它们是:JAVA栈(JAVA STACK),本地方法栈(NATIVE METHOD STACK),和程序计数器(PROGRAM COUNTER REGISTER)。 黄色部分是线程共享的,所有的线程共享该区域的内容。他们是: 方法区(METHOD AREA),堆(HEAP)。 我们分别来介绍这些区域。 (1)程序计数器(program counter register)

学过计算机组成原理的都知道计算机处理器中的程序计数器。当处理器执行一条指令时,首先需要根据PC中存放的指令地址,将指令由内存取到指令寄存器中,此过程称为“取指令”。与此同时,PC中的地址或自动加1或由转移指针给出下一条指令的地址。此后经过分析指令,执行指令。完成第一条指令的执行,而后根据PC取出第二条指令的地址,如此循环,执行每一条指令。 处理器的程序计数器是指寄存器,而java程序计数器是指一小块内存空间。java代码编译字节码之后,虚拟机会一行一行的解释字节码,并翻印成本地代码。这个程序计数器盛放的就是当前线程所执行字节码的行号的指示器。在虚拟机概念模型中,字节码解释器工作室就是通过改变这个计数器的值来选取下一条需要执行的字节码指令,分支,循环,跳转,异常处理等都依赖于它。 Java虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式实现的,因此为了线程切换后还能恢复执行位置,每条线程都需要一个独立的程序计数器。 如果线程正在执行的是一个Java方法,这个计数器记录的是正在执行的虚拟机字节码指令的地址;如果执行的是Java Native方法,这个计数器值为空。 而且程序计数器是Java虚拟机中没有规定任何OutOfMemoryError的区域。 (2)虚拟机栈 Java虚拟机栈(VM Stack)也是线程私有的,因此它的生命周期也和线程相同。它存放的是Java方法执行时的数据,既描述的是Java方法执行的内存模型:每个方法开始执行的时候,都会创建一个栈帧(Stack Frame)用于储存局部变量表、栈操作数、动态链接、方法出口等信息。每个方法从调用到执行完成就对应一个栈帧在虚拟机栈中入栈到出栈的过程。经常有人把Java内存分为堆内存和栈内存,这种是比较粗糙的分法,很大原因是大多数程序‘猿’最关注的,与对象内存分配最密切的区域就是堆和栈。局部变量表存放的是编译器可知的各种基本数据类型(boolean 、byte、int、long、char、short、float、double)、对象引用(reference类型)和returnAddress类型(它指向了一条字节码指令的地址)。其中64bit长度的long和double会占用两个局部变量空间(Slot),其余的数据类型只占用一个。局部变量表所需的内存空间是在编译时期确定的,在方法运行期间不会改变局部变量表的大小。在Java虚拟机规范中,对这部分区域规定了两种异常:1、当一个线程的栈深度大于虚拟机所允许的深度的时候,将会抛出StackOverflowError异常; 2、如果当创建一个新的线程时无法申请到足够的内存,则会抛出OutOfMemeryError异常。 (3)本地方法栈 本地方法栈(Native Method Stack)与虚拟机栈所发挥的作用是十分相似的,他们之间的区别不过是虚拟机栈为Java方法字节码服务,而本地方法栈则为Native方法服务。在虚拟机规范中对本地方法使用的语言和使用方法与数据结构没有强制规定,因此具体的虚拟机可

Java虚拟机的内存结构

我们都知道虚拟机的内存划分了多个区域,并不是一张大饼。那么为什么要划分为多块区域呢,直接搞一块区域,所有用到内存的地方都往这块区域里扔不就行了,岂不痛快。是的,如果不进行区域划分,扔的时候确实痛快,可用的时候再去找怎么办呢,这就引入了第一个问题,分类管理,类似于衣柜,系统磁盘等等,为了方便查找,我们会进行分区分类。另外如果不进行分区,内存用尽了怎么办呢?这里就引入了内存划分的第二个原因,就是为了方便内存的回收。如果不分,回收内存需要全部内存扫描,那就慢死了,内存根据不同的使用功能分成不同的区域,那么内存回收也就可以根据每个区域的特定进行回收,比如像栈内存中的栈帧,随着方法的执行栈帧进栈,方法执行完毕就出栈了,而对于像堆内存的回收就需要使用经典的回收算法来进行回收了,所以看起来分类这么麻烦,其实是大有好处的。 提到虚拟机的内存结构,可能首先想起来的就是堆栈。对象分配到堆上,栈上用来分配对象的引用以及一些基本数据类型相关的值。但是·虚拟机的内存结构远比此要复杂的多。除了我们所认识的(还没有认识完全)的堆栈以外,还有程序计数器,本地方法栈和方法区。我们平时所说的栈内存,一般是指的栈内存中的局部变量表。下面是官方所给的虚拟机的内存结构图

从图中可以看到有5大内存区域,按照是否被线程所共享可分为两部分,一部分是线程独占区域,包括Java栈,本地方法栈和程序计数器。还有一部分是被线程所共享的,包括方法区和堆。什么是线程共享和线程独占呢,非常好理解,我们知道每一个Java进行都会有多个线程同时运行,那么线程共享区的这片区域就是被所有线程一起使用的,不管有多少个线程,这片空间始终就这一个。而线程的独占区,是每个线程都有这么一份内存空间,每个线程的这片空间都是独有的,有多少个线程就有多少个这么个空间。上图的区域的大小并不代表实际内存区域的大小,实际运行过程中,内存区域的大小也是可以动态调整的。下面来具体说说每一个区域的主要功能。

JVM详解

JVM详解 本文详细讲解了JVM(Java Virtual Machine)的方方面面,首先由java的特性来描绘JVM 的大致应用,再细细阐述了JVM的原理及内存管理机制和调优.最后讲述了与JVM密切相关的Java GC机制. 本文内容大多来自网络,但内容十分丰富,是学习JVM的好资料. 后面会再针对JVM的两大职责class loader和execution engine进行讲解 若有疑问 目录 Java相关 (2) 1.1Java定义 (2) 1.2Java的开发流程 (2) 1.3Java运行的原理 (3) 1.4半编译半解释 (4) 1.5平台无关性 (5) JVM内存模型 (5) 2.1JVM规范 (6) 2.2 Sun JVM (9) 2.3 SUN JVM内存管理(优化) (10) 2.4 SUN JVM调优 (13) 2.5.JVM简单理解 (16) 2.5.1Java栈 (16) 2.5.2堆 (16) 2.5.3堆栈分离的好处 (19) 2.5.4 堆(heap)和栈(stack) (19) JAVA垃圾收集器 (20) 3.1垃圾收集简史 (20) 3.2常见的垃圾收集策略 (20) 3.2.1Reference Counting(引用计数) (20) 3.2.2跟踪收集器 (21) 3.3JVM的垃圾收集策略 (25) 3.3.1Serial Collector (25) 3.3.2 Parallel Collector (25) 3.3.3 Concurrent Collector (26) Java虚拟机(JVM)参数配置说明 (26)

java实验报告实验1答案

实验一熟悉NetBeans IDE 平台,开发环境及Java编程 实验目的: 1、我们使用的开发平台是NetBeans IDE,希望通过本次实验同学们能对NetBeans IDE 的开发环境有一个清楚的了解并能熟练运用,对Java语法进行初步运用,对面向对象的编程有一个直观的认识和深入理解,对于Java的基础知识进行理解运用和巩固。为以后的实验中能够进行开发程序打下基础。 2、通过编程和上机实验理解Java语言是如何体现面向对象编程基本思想,了解类的封装方法,以及如何创建类和对象,了解成员变量和成员方法的特性,掌握OOP方式进行程序设计的方法,了解类的继承性和多态性的作用。 实验内容: ● 1. 编写一个体现面向对象思想的程序。 ● 2. 编写一个创建对象和使用对象的方法的程序。 ● 3. 编写一个显示当前日期和时间的程序。 ● 4. 编写不同成员变量修饰方法的程序。 ● 5. 编写不同成员方法修饰方法的程序。 ● 6. 编写体现类的继承性(成员变量、成员方法、成员变量隐藏)的程序。 ●7. 编写体现类的多态性(成员方法重载、构造方法重载)的程序。 实验步骤: ●双击桌面上的NetBeans IDE 6.5.1快捷方式或在文件菜单中打开它。 图1-1 点击文件,创建新项目,创建一个项目名:experiment1。

点击按钮下一步: 在项目名称处输入:experiment1 然后点击完成:

在experiment1 下实现 程序 项目experiment1

样例1:编写应用程序输出如下三角形。 * *** ***** ******* 【参考程序】 public class Star { public static void main(String a[]) { System.out.println(" *"); System.out.println(" ***"); System.out.println(" *****"); System.out.println("*******"); } } 程序运行结果如图1-2所示。 【编程技巧】 (1) main方法是应用程序执行入口; (2) 如何在命令控制台输出字符串。 (3) 输出杨辉三角的前10行;进一步用参数传递的方式输出,例如,shuchu(n)表示 输出杨辉三角的前n行。 样例2:编写Applet程序绘制一个红色三角形,三角形中央绘制兰色文字“三角形”。 【参考程序】

深入理解Java反射机制汇总

深入理解Java反射机制 本文较为详细的分析了Java反射机制。分享给大家供大家参考,具体如下: 一、预先需要掌握的知识(java虚拟机) java虚拟机的方法区: java虚拟机有一个运行时数据区,这个数据区又被分为方法区,堆区和栈区,我们这里需要了解的主要是方法区。方法区的主要作用是存储被装载的类的类型信息,当java虚拟机装载某个类型的时候,需要类装载器定位相应的class文件,然后将其读入到java虚拟机中,紧接着虚拟机提取class 中的类型信息,将这些信息存储到方法区中。这些信息主要包括: 1、这个类型的全限定名 2、这个类型的直接超类的全限定名 3、这个类型是类类型还是接口类型 4、这个类型的访问修饰符 5、任何直接超接口的全限定名的有序列表 6、该类型的常量池 7、字段信息 8、方法信息 9、除了常量以外的所有类变量 10、一个到class类的引用 等等(读者可以参考《深入java虚拟机》这本书的叙述) Class类: Class类是一个非常重要的java基础类,每当装载一个新的类型的时候,java虚拟机都会在java堆中创建一个对应于新类型的Class实例,该实例就代表此类型,通过该Class实例我们就可以访问该类型的基本信息。上面说到在方法区中会存储某个被装载类的类型信息,我们就可以通过Class实例来访问这些信息。比如,对于上面说到的信息Class中都有对应的方法,如下:

1、getName();这个类型的全限定名 2、getSuperClass();这个类型的直接超类的全限定名 3、isInterface();这个类型是类类型还是接口类型 4、getTypeParamters();这个类型的访问修饰符 5、getInterfaces();任何直接超接口的全限定名的有序列表 6、getFields();字段信息 7、getMethods();方法信息 等等(读者可以自己参看jdk帮助文档,得到更多的信息) 二、java反射详解 反射的概念:所谓的反射就是java语言在运行时拥有一项自观的能力,反射使您的程序代码能够得到装载到JVM中的类的内部信息,允许您执行程序时才得到需要类的内部信息,而不是在编写代码的时候就必须要知道所需类的内部信息,这使反射成为构建灵活的应用的主要工具。 反射的常用类和函数:Java反射机制的实现要借助于4个类:Class,Constructor,Field,Method;其中class代表的是类对象,Constructor-类的构造器对象,Field-类的属性对象,Method -类的方法对象,通过这四个对象我们可以粗略的看到一个类的各个组成部分。其中最核心的就是Class类,它是实现反射的基础,它包含的方法我们在第一部分已经进行了基本的阐述。应用反射时我们最关心的一般是一个类的构造器、属性和方法,下面我们主要介绍Class 类中针对这三个元素的方法: 1、得到构造器的方法 Constructor getConstructor(Class[] params) -- 获得使用特殊的参数类型的公共构造函数,Constructor[] getConstructors() -- 获得类的所有公共构造函数 Constructor getDeclaredConstructor(Class[] params) -- 获得使用特定参数类型的构造函数(与接入级别无关) Constructor[] getDeclaredConstructors() -- 获得类的所有构造函数(与接入级别无关) 2、获得字段信息的方法

Java的类一些常识

Java的类一些常识 “1、请解释Java语言的跨平台特性。 解析:虽然不知道什么是跨平台也可以使用Java语言进行编程,但是对于一个Java编程员来说,理解跨平台特性能够更深入掌握Java语言,所以企业中往往要求应聘者至少理解这个特性。 参考答案:Java的跨平台特性也被称为可移植性、平台无关性,或者一次编写处处运行。他的意思就是如果用Java语言编写一个应用,那么就可以在不同平台上运行,而不需要为不同平台单独运行开发。之所以能实现跨平台的特性。主要得益于Java虚拟机(JVM),JVM解释器在运行Java应用时根据当前平台进行解释,解释成符合当前平台规范的机器码,所以可以实现同样的应用在不同平台上都能运行。 “2、请列举JAVA语言的主要特点 解析:了解一门语言,往往从熟悉该语言的主要特点开始入手,所以企业也常常通过应聘者对JAVA语言特点的掌握程度而判断其语言基础是否扎实。 参考答案:JAVA语言有很多特点,主要包括:①跨平台性:一个应用可以不经过修改直接运行到不同的平台上。②面向对象:JAVA语言是一门面向对面的语言,可以使用对象的属性和行为,可以使用面向对象的思想进行分析设计,并实现整个应用。③解释执行JAVA应用时,JVM中的解释器将解释类文件,生成符合当前平台的字节码。④自动回收:JAVA 应用中的垃圾回收是自动进行的,JVM中的后台线程将监视内存中数据的使用,当内存中的数据不再被引用时,将被作为垃圾回收,而不需要程序员动手回收。 “3、请说明一个JAVA类中主要包含哪几个元素?并说明每种元素的作用。 解析:无论简单还是复杂的JAVA应用,都是由若干个类组成,所以类是JAVA应用的组成单位。了解一个类中包含的主要元素能够对类有一个清晰的认识。一个类中往往会有五种元素,即属性、方法、构造方法、块以及内部类、其实块和内部类比较少见。 参考答案:JAVA类中主要包含属性、方法、构造方法、块以及内部类。

java虚拟机的原理和作用

Java虚拟机 一、什么是Java虚拟机 Java虚拟机是一个想象中的机器,在实际的计算机上通过软件模拟来实现。Java虚拟机有自己想象中的硬件,如处理器、堆栈、寄存器等,还具有相应的指令系统。 1.为什么要使用Java虚拟机 Java语言的一个非常重要的特点就是与平台的无关性。而使用Java虚拟机是实现这一特点的关键。一般的高级语言如果要在不同的平台上运行,至少需要编译成不同的目标代码。而引入Java语言虚拟机后,Java语言在不同平台上运行时不需要重新编译。Java语言使用模式Java虚拟机屏蔽了与具体平台相关的信息,使得Java语言编译程序只需生成在Java虚拟机上运行的目标代码(字节码),就可以在多种平台上不加修改地运行。Java虚拟机在执行字节码时,把字节码解释成具体平台上的机器指令执行。 2.谁需要了解Java虚拟机 Java虚拟机是Java语言底层实现的基础,对Java语言感兴趣的人都应对Java虚拟机有个大概的了解。这有助于理解Java语言的一些性质,也有助于使用Java语言。对于要在特定平台上实现Java虚拟机的软件人员,Java语言的编译器作者以及要用硬件芯片实现Java虚拟机的人来说,则必须深刻理解Java 虚拟机的规范。另外,如果你想扩展Java语言,或是把其它语言编译成Java语言的字节码,你也需要深入地了解Java虚拟机。 3.Java虚拟机支持的数据类型 Java虚拟机支持Java语言的基本数据类型如下: byte://1字节有符号整数的补码 short://2字节有符号整数的补码 int://4字节有符号整数的补码 long://8字节有符号整数的补码 float://4字节IEEE754单精度浮点数 double://8字节IEEE754双精度浮点数 char://2字节无符号Unicode字符 几乎所有的Java类型检查都是在编译时完成的。上面列出的原始数据类型的数据在Java执行时不需要用硬件标记。*作这些原始数据类型数据的字节码(指令)本身就已经指出了*作数的数据类型,例如iadd、ladd、fadd和dadd指令都是把两个数相加,其*作数类型别是int、long、 float和double。虚拟机没有给boolean(布尔)类型设置单独的指令。boolean型的数据是由integer指令,包括integer 返回来处理的。boolean型的数组则是用byte数组来处理的。虚拟机使用IEEE754格式的浮点数。不支持IEEE格式的较旧的计算机,在运行 Java数值计算程序时,可能会非常慢。 虚拟机支持的其它数据类型包括: object//对一个Javaobject(对象)的4字节引用 returnAddress//4字节,用于jsr/ret/jsr-w/ret-w指令 注:Java数组被当作object处理。 虚拟机的规范对于object内部的结构没有任何特殊的要求。在Sun公司的实现中,对object的引用是一个句柄,其中包含一对指针:一个指针指向该object的方法表,另一个指向该object的数据。用Java

java中的四个核心概念

Java已经成为一个庞大而复杂的技术平台,对于开发人员而言,要想更好的掌握Java技术,深入理解底层的技术处理细节必不可少。现在介绍下java的四个核心概念: 1.Java虚拟机 Java虚拟机的主要任务是装在class文件并且执行其中的字节码。Java 虚拟机包含一个类装载器,它可以从程序和 API中装载class文件。Java API中只有程序执行时需要的那些类才会被装载。字节码由执行引擎来执行。不同的Java虚拟机中,执行引擎可能实现得非常不同。在由软件实现的虚拟机中,最简单的执行引擎就是一次性解释字节码。 另一种执行引擎更快,但是也更消耗内存,叫做"即时编译器(just-in-time compiler)"。在这种情况下,第一次被执行的字节码会被编译成本地机器代码。编译出的本地机器代码会被缓存,当方法以后被调用的时候可以重用。 第三种执行引擎是自适应优化器。在这种方法里,虚拟机开始的时候解释字节码,但是会监视运行中程序的活动,并且记录下使用最频繁的代码段。程序运行的时候,虚拟机只把那些活动最频繁的代码编译成本地代码,其他的代码由于使用得不是很频繁,继续保留为字节码-由虚拟机继续解释它们。 一个自适应的优化器可以使得Java虚拟机在80%~90%的时间里执行被优化过的本地代码,而只需要编译10%~20%的对性能有影响的代码。 2.类装载器的体系结构 一个Java应用程序可以使用两种类装载器:"启动(bootstrap)"类装载器和用户定义的类装载器。启动类装载器(这是系统中唯一的)是 Java虚拟机实现的一部分。启动类装载器通常使用某种默认方式从本地磁盘中装载类,包括Java API类(启动类装载器也被称为原始类装载器、系统类装载器或者默认类装载器)。Java培训:https://www.wendangku.net/doc/f314141093.html, Java应用程序能够在运行时安装用户定义的类装载器,这种类装载器能够使用自定义的方式来装载类。例如,从网络下载class文件。尽管启动类装载器是虚拟机实现的本质部分,而用户定义的类装载器不是,但用户定义的类装载器能够用Java来编写,能够被编译成class文件,能够被虚拟机装载,还能够像其它对象一样实例化。 3.Java class文件 Java class文件主要在平台无关性和网络移动性方面使Java更适合网络。它在平台无关性方面的任务是:为Java程序提供独立于底层主机平台的二进制形式的服务。这种途径途径打破了C或者C++等语言所遵循的传统,使用这些传统语言写的程序通常首先被编译,然后被连接成单

java虚拟机中的堆,栈和方法区

基础数据类型直接在栈空间分配,方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收。引用数据类型,需要用new来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量。方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完成后从栈空间回收。局部变量new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,堆空间区域等待GC回收。方法调用时传入的literal 参数,先在栈空间分配,在方法调用完成后从栈空间分配。字符串常量在方法区域分配,this 在堆空间分配。数组既在栈空间分配数组名称,又在堆空间分配数组实际的大小! 哦对了,补充一下static在方法区域分配。 从Java的这种分配机制来看,堆栈又可以这样理解:堆栈(Stack)是操作系统在建立某个进程时或者线程(在支持多线程的操作系统中是线程)为这个线程建立的存储区域,该区域具有先进后出的特性。 每一个Java应用都唯一对应一个JVM实例,每一个实例唯一对应一个堆。应用程序在运行中所创建的所有类实例或数组都放在这个堆中,并由应用所有的线程共享.跟C/C++不同,Java中分配堆内存是自动初始化的。Java中所有对象的存储空间都是在堆中分配的,但是这个对象的引用却是在堆栈中分配,也就是说在建立一个对象时从两个地方都分配内存,在堆中分配的内存实际建立这个对象,而在堆栈中分配的内存只是一个指向这个堆对象的指针(引用)而已。 <二> 这两天看了一下深入浅出JVM这本书,推荐给高级的java程序员去看,对你了解JAVA的底层和运行机制有 比较大的帮助。 废话不想讲了.入主题: 先了解具体的概念: JAVA的JVM的内存可分为3个区:堆(heap)、栈(stack)和方法区(method) 堆区: 1.存储的全部是对象,每个对象都包含一个与之对应的class的信息。(class的目的是得到操作指令) 2.jvm只有一个堆区(heap)被所有线程共享,堆中不存放基本类型和对象引用,只存放对象本身 栈区: 1.每个线程包含一个栈区,栈中只保存基础数据类型的对象和自定义对象的引用(不是对象),对象都存放在堆区中 2.每个栈中的数据(原始类型和对象引用)都是私有的,其他栈不能访问。 3.栈分为3个部分:基本类型变量区、执行环境上下文、操作指令区(存放操作指令)。 方法区: 1.又叫静态区,跟堆一样,被所有的线程共享。方法区包含所有的class和static变量。 2.方法区中包含的都是在整个程序中永远唯一的元素,如class,static变量。 为了更清楚地搞明白发生在运行时数据区里的黑幕,我们来准备2个小道具(2个非常简单的小程序)。 AppMain.java

深入理解Java虚拟机(JVM)

深入理解Java虚拟机(JVM) 一、什么是Java虚拟机 当你谈到Java虚拟机时,你可能是指: 1、抽象的Java虚拟机规范 2、一个具体的Java虚拟机实现 3、一个运行的Java虚拟机实例 二、Java虚拟机的生命周期 一个运行中的Java虚拟机有着一个清晰的任务:执行Java程序。程序开始执行时他才运行,程序结束时他就停止。你在同一台机器上运行三个程序,就会有三个运行中的Java 虚拟机。 Java虚拟机总是开始于一个main()方法,这个方法必须是公有、返回void、直接受一个字符串数组。在程序执行时,你必须给Java虚拟机指明这个包换main()方法的类名。 Main()方法是程序的起点,他被执行的线程初始化为程序的初始线程。程序中其他的线程都由他来启动。Java中的线程分为两种:守护线程(daemon)和普通线程(non-daemon)。守护线程是Java虚拟机自己使用的线程,比如负责垃圾收集的线程就是一个守护线程。当然,你也可以把自己的程序设置为守护线程。包含Main()方法的初始线程不是守护线程。 只要Java虚拟机中还有普通的线程在执行,Java虚拟机就不会停止。如果有足够的权限,你可以调用exit()方法终止程序。 三、Java虚拟机的体系结构 在Java虚拟机的规范中定义了一系列的子系统、内存区域、数据类型和使用指南。这些组件构成了Java虚拟机的内部结构,他们不仅仅为Java虚拟机的实现提供了清晰的内部结构,更是严格规定了Java虚拟机实现的外部行为。 每一个Java虚拟机都由一个类加载器子系统(class loader subsystem),负责加载程序中的类型(类和接口),并赋予唯一的名字。每一个Java虚拟机都有一个执行引擎(execution engine)负责执行被加载类中包含的指令。 程序的执行需要一定的内存空间,如字节码、被加载类的其他额外信息、程序中的对象、方法的参数、返回值、本地变量、处理的中间变量等等。Java虚拟机将这些信息统统保存在数据区(data areas)中。虽然每个Java虚拟机的实现中都包含数据区,但是Java虚拟机规范对数据区的规定却非常的抽象。许多结构上的细节部分都留给了Java虚拟机实现者自己发挥。不同Java虚拟机实现上的内存结构千差万别。一部分实现可能占用很多内存,而其他以下可能只占用很少的内存;一些实现可能会使用虚拟内存,而其他的则不使用。这种比较精炼的Java虚拟机内存规约,可以使得Java虚拟机可以在广泛的平台上被实现。 数据区中的一部分是整个程序共有,其他部分被单独的线程控制。每一个Java虚拟机

相关文档
相关文档 最新文档