??xml version="1.0" encoding="utf-8" standalone="yes"?>public static String isNull(Object obj){
if(null == obj || ((String)obj).equals("")||obj.equals("null")||obj.equals("NULL")){
return "" ;
}
return (String)obj;
}
]]>
1、抽象的Java虚拟?br />
2、一个具体的Java虚拟机实?br />
3、一个运行的Java虚拟机实?br />
二、Java虚拟机的生命周期
一个运行中的Java虚拟机有着一个清晰的dQ执行JavaE序。程序开始执行时他才q行Q程序结束时他就停止。你在同一台机器上q行三个E序Q就会有三个q行中的Java虚拟机?br />
Java虚拟机L开始于一个main()ҎQ这个方法必L公有、返回void、直接受一个字W串数组。在E序执行Ӟ你必ȝJava虚拟机指明这个包换main()Ҏ的类名?br />
Main()Ҏ是程序的LQ他被执行的U程初始化ؓE序的初始线E。程序中其他的线E都׃来启动?br />
Java中的U程分ؓ两种Q?span style="color: red">守护U程 QdaemonQ和普通线E(non-daemonQ?/span>
守护U程是Java虚拟׃用的U程Q比如负责垃圾收集的U程是一个守护线E。当Ӟ你也可以把自qE序讄为守护线E。包含Main()Ҏ的初始线E不是守护线E?br />
只要Java虚拟Zq有普通的U程在执行,Java虚拟机就不会停止。如果有_的权限,你可以调用exit()Ҏl止E序?/span>
三、Java虚拟机的体系l构
在Java虚拟机的规范中定义了一pd的子pȝ、内存区域、数据类型和使用指南。这些组件构成了Java虚拟机的内部l构Q他们不仅仅为Java虚拟机的实现提供了清晰的内部l构Q更是严D定了Java虚拟机实现的外部行ؓ?
每一个Java虚拟机都׃个类加蝲器子pȝQclass loader subsystemQ,负责加蝲E序中的cdQ类和接口)Qƈ赋予唯一的名字。每一个Java虚拟机都有一个执行引擎(execution engineQ负责执行被加蝲cM包含的指令?/span>
E序的执行需要一定的内存I间Q如字节码、被加蝲cȝ其他额外信息、程序中的对象、方法的参数、返回倹{本地变量、处理的中间变量{等。Java虚拟机将 q些信息l统保存在数据区Qdata areasQ中。虽然每个Java虚拟机的实现中都包含数据区,但是Java虚拟范对数据区的规定却非常的抽象。许多结构上的细节部分都留给?Java虚拟机实现者自己发挥。不同Java虚拟机实C的内存结构千差万别。一部分实现可能占用很多内存Q而其他以下可能只占用很少的内存;一些实现可 能会使用虚拟内存Q而其他的则不使用。这U比较精炼的Java虚拟机内存规U,可以使得Java虚拟机可以在q泛的^C被实现?br />
数据Z的一部分是整个程序共有,其他部分被单独的U程控制。每一个Java虚拟机都包含Ҏ区(method areaQ和堆(heapQ,他们都被整个E序׃n。Java虚拟机加载ƈ解析一个类以后Q将从类文g中解析出来的信息保存与方法区中。程序执行时创徏?对象都保存在堆中?
当一个线E被创徏Ӟ会被分配只属于他自己的PC寄存?#8220;pc register”Q程序计数器Q和Java堆栈QJava stackQ。当U程不掉用本地方法时QPC寄存器中保存U程执行的下一条指令。Java堆栈保存了一个线E调用方法时的状态,包括本地变量、调用方法的 参数、返回倹{处理的中间变量。调用本地方法时的状态保存在本地Ҏ堆栈中(native method stacksQ,可能再寄存器或者其他非q_独立的内存中?br />
Java堆栈有堆栈块Qstack frames (or frames)Q组成。堆栈块包含JavaҎ调用的状态。当一个线E调用一个方法时QJava虚拟Z一个新的块压到Java堆栈中,当这个方法运行结束时QJava虚拟Z对应的块弹出ƈ抛弃?br />
Java虚拟Z使用寄存器保存计的中间l果Q而是用Java堆栈在存放中间结果。这是的Java虚拟机的指o更紧凑,也更Ҏ在一个没有寄存器的设备上实现Java虚拟机?
Java堆栈中向下增长的QPC寄存器中U程三ؓ灰色Q是因ؓ它正在执行本地方法,他的下一条执行指令不保存在PC寄存器中?br />
四、数据类型(Data TypesQ?br />
所有Java虚拟Z使用的数据都有确定的数据cdQ数据类型和操作都在Java虚拟范中严格定义。Java中的数据cd分ؓ原始数据cd Qprimitive typesQ和引用数据cdQreference typeQ。引用类型依赖于实际的对象,但不是对象本w。原始数据类型不依赖于Q何东西,他们是本n表示的数据?br />
所有JavaE序语言中的原始 数据cdQ都是Java虚拟机的原始数据cdQ除了布型QbooleanQ。当~译器将Java源代码编译ؓ自己码时Q用整型(intQ或者字节型 QbyteQ去表示布尔型。在Java虚拟Z使用整数0表示布尔型的falseQ用非零整数表C布型的trueQ布数l被表示为字节数l,虽然?们可能会以字节数l或者字节块Qbit fieldsQ保存在堆中?br />
除了布尔型,其他Java语言中的原始cd都是Java虚拟Z的数据类型。在Java中数据类型被分ؓQ整形的byteQshortQintQlongQchar和Q点型的floatQdouble。Java语言中的数据cd在Q何主Z都有同样的范围?
在Java虚拟Zq存在一个Java语言中不能用的原始数据cdq回值类型(returnValueQ。这U类型被用来实现JavaE序中的“finally clauses”Q具体的参见18章的“Finally Clauses”?br />
引用cd可能被创ZؓQ类cdQclass typeQ,接口cdQinterface typeQ,数组cdQarray typeQ。他们都引用被动态创建的对象。当引用cd引用nullӞ说明没有引用M对象?br />
Java虚拟范只定义了每一U数据类型表C的范围Q没有定义在存储时每U类型占用的I间。他们如何存储由Java虚拟机的实现者自己决定。关于Q点型更多信息参见14?#8220;Floating Point Arithmetic”?br />
TypeRange
byte8-bit signed two's complement integer (-27 to 27 - 1, inclusive)
short16-bit signed two's complement integer (-215 to 215 - 1, inclusive)
int32-bit signed two's complement integer (-231 to 231 - 1, inclusive)
long64-bit signed two's complement integer (-263 to 263 - 1, inclusive)
char16-bit unsigned Unicode character (0 to 216 - 1, inclusive)
float32-bit IEEE 754 single-precision float
double64-bit IEEE 754 double-precision float
returnValueaddress of an opcode within the same method
referencereference to an object on the heap, or null
五、字节长?br />
Java虚拟Z最的数据单元式字QwordQ,其大由Java虚拟机的实现者定义。但是一个字的大必够容UbyteQshortQintQ?charQfloatQreturnValueQreferenceQ两个字必须_容纳longQdouble。所以虚拟机的实现者至提供的字不能小 ?1bits的字Q但是最好选择特定q_上最有效率的字长?br />
在运行时QJavaE序不能军_所q行机器的字ѝ字长也不会影响E序的行为,他只是在Java虚拟Z的一U表现方式?br />
六、类加蝲器子pȝ
Java虚拟Z的类加蝲器分ZU:原始cd载器Qprimordial class loaderQ和cd载器对象Qclass loader objectsQ。原始类加蝲器是Java虚拟机实现的一部分Q类加蝲器对象是q行中的E序的一部分。不同类加蝲器加载的c被不同的命名空间所分割?br />
cd载器调用了许多Java虚拟Z其他的部分和java.lang包中的很多类。比如,cd载对象就是java.lang.ClassLoader子类 的实例,ClassLoadercM的方法可以访问虚拟机中的cd载机Ӟ每一个被Java虚拟机加载的c都会被表示Z?java.lang.Classcȝ实例。像其他对象一Pcd载器对象和Class对象都保存在堆中Q被加蝲的信息被保存在方法区中?br />
1、加载、连接、初始化QLoading, Linking and InitializationQ?br />
cd载子pȝ不仅仅负责定位ƈ加蝲cLӞ他按照以下严格的步骤作了很多其他的事情:Q具体的信息参见W七章的“cȝ生命周期”Q?br />
1Q、加载:Lq导入指定类型(cd接口Q的二进制信?br />
2Q、连接:q行验证、准备和解析
①验证:保导入cd的正?br />
②准备:为类型分配内存ƈ初始化ؓ默认?br />
③解析:字W引用解析ؓ直接饮用
3Q、初始化Q调用Java代码Q初始化cd量ؓ合适的?br />
2、原始类加蝲器(The Primordial Class LoaderQ?br />
每个Java虚拟机都必须实现一个原始类加蝲器,他能够加载那些遵守类文g格式q且被信ȝcR但是,Java虚拟机的规范q没有定义如何加载类Q这?Java虚拟机实现者自己决定。对于给定类型名的类型,原始莱加载器必须扑ֈ那个cd名加“.class”的文件ƈ加蝲入虚拟机中?br />
3、类加蝲器对?br />
虽然cd载器对象是JavaE序的一部分Q但是ClassLoadercM的三个方法可以访问Java虚拟Z的类加蝲子系l?br />
1Q、protected final Class defineClass(…)Q用这个方法可以出入一个字节数l,定义一个新的类型?br />
2Q、protected Class findSystemClass(String name)Q加载指定的c,如果已经加蝲Q就直接q回?br />
3Q、protected final void resolveClass(Class c)QdefineClass()Ҏ只是加蝲一个类Q这个方法负责后l的动态连接和初始化?br />
具体的信息,参见W八?#8220;q接模型”Q?The Linking ModelQ?br />
4、命名空?br />
当多个类加蝲器加载了同一个类ӞZ保证他们名字的唯一性,需要在cd前加上加载该cȝcd载器的标识。具体的信息Q参见第八章“q接模型”Q?The Linking ModelQ?br />
七、方法区QThe Method AreaQ?br />
在Java虚拟ZQ被加蝲cd的信息都保存在方法区中。这写信息在内存中的l织形式p拟机的实现者定义,比如Q虚拟机工作在一?#8220;little- endian”的处理器上,他就可以信息保存ؓ“little-endian”格式的,虽然在JavacL件中他们是以“big-endian”格式?存的。设计者可以用最适合q地机器的表C格式来存储数据Q以保证E序能够以最快的速度执行。但是,在一个只有很内存的讑֤上,虚拟机的实现者就不会占用 很大的内存?br />
E序中的所有线E共享一个方法区Q所以访问方法区信息的方法必LU程安全的。如果你有两个线E都d载一个叫Lava的类Q那只能׃个线E被容许d载这个类Q另一个必ȝ待?br />
在程序运行时Q方法区的大是可变的,E序在运行时可以扩展。有些Java虚拟机的实现也可以通过参数也订制方法区的初始大,最值和最大倹{?br />
ҎZ可以被垃圾收集。因为程序中的内q加蝲器动态加载,所有类可能变成没有被引用(unreferencedQ的状态。当cd成这U状态时Q他可 能被垃圾攉掉。没有加载的cd括两U状态,一U是真正的没有加载,另一个种?#8220;unreferenced”的状态。详l信息参见第七章的类的生命周?QThe Lifetime of a ClassQ?br />
1、类型信息(Type InformationQ?br />
每一个被加蝲的类型,在Java虚拟Z都会在方法区中保存如下信息:
1Q、类型的全名QThe fully qualified name of the typeQ?br />
2Q、类型的父类型的全名Q除非没有父cdQ或者弗雷Ş式java.lang.ObjectQ(The fully qualified name of the typeís direct superclassQ?br />
3Q、给cd是一个类q是接口Qclass or an interfaceQ(Whether or not the type is a class Q?br />
4Q、类型的修饰W(publicQprivateQprotectedQstaticQfinalQvolatileQtransient{)QThe typeís modifiersQ?br />
5Q、所有父接口全名的列表(An ordered list of the fully qualified names of any direct superinterfacesQ?br />
cd全名保存的数据结构由虚拟机实现者定义。除此之外,Java虚拟要ؓ每个cd保存如下信息Q?br />
1Q、类型的帔R池(The constant pool for the typeQ?br />
2Q、类型字D늚信息QField informationQ?br />
3Q、类型方法的信息QMethod informationQ?br />
4Q、所有的静态类变量Q非帔RQ信息(All class (static) variables declared in the type, except constantsQ?br />
5Q、一个指向类加蝲器的引用QA reference to class ClassLoaderQ?br />
6Q、一个指向Classcȝ引用QA reference to class ClassQ?br />
1Q、类型的帔R池(The constant pool for the typeQ?br />
帔R池中保存中所有类型是用的有序的常量集合,包含直接帔RQliteralsQ如字符丌Ӏ整数、QҎ的常量,和对cd、字Dc方法的W号引用。常量池 中每一个保存的帔R都有一个烦引,像数组中的字段一栗因为常量池中保存中所有类型用到的类型、字Dc方法的字符引用Q所以它也是动态连接的主要?象。详l信息参见第六章“The Java Class File”?br />
2Q、类型字D늚信息QField informationQ?br />
字段名、字D늱型、字D늚修饰W(publicQprivateQprotectedQstaticQfinalQvolatileQtransient{)、字D在cM定义的顺序?br />
3Q、类型方法的信息QMethod informationQ?br />
Ҏ名、方法的q回值类型(或者是voidQ、方法参数的个数、类型和他们的顺序、字D늚修饰W(publicQprivateQprotectedQstaticQfinalQvolatileQtransient{)、方法在cM定义的顺?br />
如果不是抽象和本地本法还需要保?br />
Ҏ的字节码、方法的操作数堆栈的大小和本地变量区的大(E候有详细信息Q、异常列表(详细信息参见W十七章“Exceptions”。)
4Q、类Q静态)变量QClass VariablesQ?br />
cd量被所有类的实例共享,即不通过cȝ实例也可以访问。这些变量绑定在cMQ而不是类的实例上Q,所以他们是cȝ逻辑数据的一部分。在Java虚拟Z用这个类之前需要ؓcd量(non-finalQ分配内?br />
帔RQfinalQ的处理方式于这U类变量Qnon-finalQ不一栗每一个类型在用到一个常量的时候,都会复制一份到自己的常量池中。常量也像类?量一样保存在ҎZQ只不过他保存在帔R池中。(可能是,cd量被所有实例共享,而常量池是每个实例独有的Q。Non-finalcd量保存ؓ定义他的 cd数据Qdata for the type that declares themQ的一部分Q而final帔R保存Z用他的类型数据(data for any type that uses themQ的一部分。详情参见第六章“The Java Class FileThe Java Class File”
5Q、指向类加蝲器的引用QA reference to class ClassLoaderQ?br />
每一个被Java虚拟机加载的cdQ虚拟机必须保存q个cd是否由原始类加蝲器或者类加蝲器加载。那些被cd载器加蝲的类型必M存一个指向类加蝲器的?用。当cd载器动态连接时Q会使用q条信息。当一个类引用另一个类Ӟ虚拟机必M存那个被引用的类型是被同一个类加蝲器加载的Q这也是虚拟机维护不同命 名空间的q程。详情参见第八章“The Linking Model”
6Q、指向Classcȝ引用QA reference to class ClassQ?br />
Java虚拟Zؓ每一个加载的cd创徏一个java.lang.Classcȝ实例。你也可以通过ClasscȝҎQ?br />
public static Class forName(String className)来查找或者加载一个类Qƈ取得相应的Classcȝ实例。通过q个Classcȝ实例Q我们可以访问Java虚拟机方法区中的信息。具体参照ClasscȝJavaDoc?br />
2、方法列表(Method TablesQ?br />
Z更有效的讉K所有保存在ҎZ的数据,q些数据的存储结构必ȝq仔l的设计。所有方法区中,除了保存了上边的那些原始信息外,q有一个ؓ了加快存 取速度而设计的数据l构Q比如方法列表。每一个被加蝲的非抽象c,Java虚拟机都会ؓ他们产生一个方法列表,q个列表中保存了q个cd能调用的所有实?Ҏ的引用,报错那些父类中调用的Ҏ。详情参见第八章“The Linking Model”
八、堆
当JavaE序创徏一个类的实例或者数l时Q都在堆中ؓ新的对象分配内存。虚拟机中只有一个堆Q所有的U程都共享他?br />
1、垃圾收集(Garbage CollectionQ?br />
垃圾攉是释放没有被引用的对象的主要Ҏ。它也可能会Z减少堆的片Q而移动对象。在Java虚拟机的规范中没有严格定义垃圾收集,只是定义一个Java虚拟机的实现必须通过某种方式理自己的堆。详情参见第九章“Garbage Collection”?br />
2、对象存储结构(Object RepresentationQ?br />
Java虚拟机的规范中没有定义对象怎样在堆中存储。每一个对象主要存储的是他的类和父cM定义的对象变量。对于给定的对象的引用,虚拟机必d耨很快的 定位到这个对象的数据。另为,必须提供一U通过对象的引用方法对象数据的ҎQ比如方法区中的对象的引用,所以一个对象保存的数据中往往含有一个某UŞ?指向Ҏ区的指针?br />
一个可能的堆的设计是将堆分Z个部分:引用池和对象池。一个对象的引用是指向引用池的本地指针。每一个引用池中的条目都包含两个部分:指向对象池中?象数据的指针和方法区中对象类数据的指针。这U设计能够方便Java虚拟机堆片的整理。当虚拟机在对象池中Ud一个对象的时候,只需要修改对应引用池?的指针地址。但是每ơ访问对象的数据都需要处理两ơ指针。下图演CZq种堆的设计。在W九章的“垃圾攉”中的HeapOfFish Applet演示了这U设计?
另一U堆的设计是Q一个对象的引用是一个指向一堆数据和指向相应对象的偏UL针。这U设计方便了对象的访问,可是对象的移动要变的异常复杂。下图演CZq种设计
当程序试囑ְ一个对象{换ؓ另一U类型时Q虚拟机需要判断这U{换是否是q个对象的类型,或者是他的父类型。当E序适用instanceof语句的时候也 会做cM的事情。当E序调用一个对象的ҎӞ虚拟机需要进行动态绑定,他必d断调用哪一个类型的Ҏ。这也需要做上面的判断?br />
无论虚拟机实现者用哪一U设计,他都可能为每一个对象保存一个类似方法列表的信息。因Z可以提升对象Ҏ调用的速度Q对提升虚拟机的性能非常重要Q但 是虚拟机的规范中比没有要求必d现类似的数据l构。下图描qCq种l构。图中显CZ一个对象引用相兌的所有的数据l构Q包括:
1Q、一个指向类型数据的指针
2Q、一个对象的Ҏ列表。方法列表是一个指向所有可能被调用对象Ҏ的指针数l。方法数据包括三个部分:操作码堆栈的大小和方法堆栈的本地变量区;Ҏ的字节码Q异常列表?br />
每一个Java虚拟Z的对象必d联一个用于同步多U程的lock(mutex)。同一时刻Q只能有一个对象拥有这个对象的锁。当一个拥有这个这个对?的锁Q他可以多ơ申误个锁Q但是也必须释放相应ơ数的锁才能真正释放q个对象锁。很多对象在整个生命周期中都不会被锁Q所以这个信息只有在需要时才需 要添加。很多Java虚拟机的实现都没有在对象的数据中包含“锁定数据”Q只是在需要时才生成相应的数据。除了实现对象的锁定Q每一个对象还逻辑兌C ?#8220;wait set”的实现。锁定帮l线E独立处理共享的数据Q不需要妨其他的U程?#8220;wait set”帮组U程协作完成同一个目标?#8220;wait set”往往通过Objectcȝwait()和notify()Ҏ来实现?
垃圾攉也需要堆中的对象是否被关联的信息。Java虚拟范中指出垃圾攉一个运行一个对象的finalizerҎ一ơ,但是容许 finalizerҎ重新引用q个对象Q当q个对象再次不被引用Ӟ׃需要再ơ调用finalizeҎ。所以虚拟机也需要保存finalizeҎ 是否q行q的信息。更多信息参见第九章?#8220;垃圾攉”
3、数l的保存QArray RepresentationQ?br />
在Java 中,数组是一U完全意义上的对象,他和对象一样保存在堆中、有一个指向Classcd例的引用。所有同一l度和类型的数组拥有同样的ClassQ数l的?度不做考虑。对应Class的名字表CZؓl度和类型。比如一个整型数据的Class?#8220;[I”Q字节型三维数组Class名ؓ“[[[B”Q两l对象数?Class名ؓ“[[Ljava.lang.Object”?br />
多维数组被表CZؓ数组的数l,如下图:
数组必须在堆中保存数l的长度Q数l的数据和一些对象数l类型数据的引用。通过一个数l引用的Q虚拟机应该能够取得一个数l的长度Q通过索引能够讉K特定 的数据,能够调用Object定义的方法。Object是所有数据类的直接父cR更多信息参见第六章“cL?#8221;?br />
九、PC寄存器(E序计数器)QThe Program CounterQ?br />
每一个线E开始执行时都会被创Z个程序计数器。程序计数器只有一个字长(wordQ,所以它能够保存一个本地指针和returnValue。当U程执行 ӞE序计数器中存放了正在执行指令的地址Q这个地址可以使一个本地指针,也可以一个从Ҏ字节码开始的偏移指针。如果执行本地方法,E序计数器的值没 有被定义?br />
十、Java堆栈QThe Java StackQ?br />
当一个线E启动时QJava虚拟ZZ创徏一个Java堆栈。Java堆栈用一些离散的framecȝ录线E的状态。Java虚拟机堆Java堆栈的操作只有两U:压入和弹出frames?br />
U程中正在执行的Ҏ被称为当前方法(current methodQ,当前Ҏ所对应的frame被称为当前Qcurrent frameQ。定义当前方法的c被UCؓ当前c(current classQ,当前cȝ帔R池被UCؓ当前帔R池(current constant pool.Q。当U程执行ӞJava虚拟Z跟踪当前cd当前帔R池。但U程操作保存在中的数据Ӟ他只操作当前帧的数据?br />
当线E调用一个方法时Q虚拟机会生成一个新的Qƈ压入U程的Java堆栈。这个新的变成当前帧。当Ҏ执行Ӟ他用当前保存Ҏ的参数、本地变 量、中间结构和其他数据。方法有两种退出方式:正常退出和异常推出。无论方法以哪一U方式推出,Java虚拟机都会弹出ƈ丢弃Ҏ的Q上一个方法的帧变 为当前?br />
所有保存在帧中的数据都只能被拥有它的线E访问,U程不能讉K其他U程的堆栈中的数据。所以,讉KҎ的本地变量时Q不需要考虑多线E同步?br />
和方法区、堆一PJava堆栈不需要连l的内存I间Q它可以被保存在一个分散的内存I间或者堆上。堆栈具体的数据和长度都有Java虚拟机的实现者自己定义。一些实现可能提供了执行堆栈最大值和最值的Ҏ?br />
十一、堆栈QThe Stack FrameQ?br />
堆栈帧包含三部分Q本地变量、操作数堆栈和数据。本地变量和操作数堆栈的大小都是一字(wordQؓ单位的,他们在编译就已经定。数据的大取决于 不同的实现。当E序调用一个方法时Q虚拟机从类数据中取得本地变量和操作数堆栈的大小Q创Z个合适大和帧,然后压入Java堆栈中?br />
1、本地变量(Local VariablesQ?br />
本地变量在Java堆栈帧中被组lؓ一个从0计数的数l,指o通过提供他们的烦引从本地变量Z取得相应的倹{Int,float,reference, returnValue占一个字Qbyte,short,char被{换成int然后存储Qlong和doubel占两个字?br />
指o通过提供两个字烦引中的前一个来取得long,doubel的倹{比如一个long的值存储在索引3Q?上,指o可以通过3来取得这个longcd的倹{?br />
本地变量Z包含了方法的参数和本地变量。编译器方法的参数以他们申明的序攑֜数组的前面。但是编译器却可以将本地变量L排列在本地变量数l中Q甚至两个本地变量可以公用一个地址Q比如,当两个本地变量在两个不交叠的区域内,像循环变量i,j?br />
虚拟机的实现者可以用Q何结构来描述本地变量Z的数据,虚拟范中没有定义如何存储long和doubel?br />
2、操作数堆栈QOperand StackQ?br />
向本地变量一P操作数堆栈也被组lؓ一个以字ؓ单位的数l。但是不像本地变量那样通过索引讉KQ而是通过push和pop值来实现讉K的。如果一个指令push一个值到堆栈中,那么下一个指令就可以popq且使用q个倹{?br />
操作数堆栈不像程序计数器那样不可以被指o直接讉KQ指令可以直接访问操作数堆栈。Java虚拟机是一个以堆栈为基Q而不是以寄存器ؓ基础的,因ؓ它的 指o从堆栈中取得操作敎ͼ而不是同寄存器中。当Ӟ指o也可以从其他地方ȝ操作敎ͼ比如指o后面的操作码Q或者常量池。但是Java虚拟机指令主要是?操作数堆栈中取得他们需要的操作数?br />
Java虚拟机将操作数堆栈视为工作区Q很多指令通过先从操作数堆栈中pop|在处理完以后再将l果push回操作数堆栈。一个add的指令执行过E如 下图所C:先执行iload_0和iload_1两条指o需要相加的两个敎ͼ从本地方法区中取出,qpush到操作数堆栈中;然后执行iadd指oQ现 popZ个|相加Qƈ结果puspq操作数堆栈中;最后执行istore_2指oQpop出结果,赋值到本地ҎZ?
3、数据QFrame DataQ?br />
处理本地变量和操作数堆栈以外Qjava堆栈帧还包括了ؓ了支持常量池Q方法返回值和异常分发需要的数据Q他们被保存在数据中?br />
当虚拟机遇到使用指向帔R池引用的指oӞ׃通过帧数据中指向帔R区的指针来访问所需要的信息。前面提到过Q常量区中的引用在最开始时都是W号引用。即使当虚拟机检查这些引用时Q他们也是字W引用。所以虚拟机需要在q时转换q个引用?br />
当一个方法正常返回时Q虚拟机需要重建那个调用这个方法的Ҏ的堆栈。如果执行完的方法有q回|虚拟机就需要将q个值pushq调用方法的哪个操作数堆栈中?br />
帧数据中也包含虚拟机用来处理异常的异常表的引用。异常表定义了一个被catch语句保护的一D字节码。每一个异常表中的个体又包含了需要保护的字节玛的 范围Q和异常被捕捉到旉要执行的字节码的位置。当一个方法抛Z个异常时QJava虚拟机就是用异常表去判断如何处理q个异常。如果虚拟机扑ֈ了一个匹 配的catchQ他׃控制权交给catch语句。如果没有找到匹配的catchQ方法就会异常返回,然后再调用的Ҏ中l这个过E?br />
除了以上的三个用途外Q数据q可能包含一些依赖于实现的数据,比如调试的信息?br />
十二、本地方法堆?br />
本地ҎZ赖于虚拟机的不同实现。虚拟机的实现者可以自己决定用哪一U机制去执行本地Ҏ?br />
M本地Ҏ接口QNative Method InterfaceQ都使用某种形式的本地方法堆栈?
十三、执行引?br />
一个java虚拟机实现的核心是执行引擎。在Java虚拟范,执行引擎被描qCؓ一pd的指令。对于每一个指令,规范都描qC他们应该做什么,但是没有说要如何d?br />
1、指令集
在Java虚拟Z一个方法的字节码流是一个指令的序列。每一个指令由一个字节的操作码(OpcodeQ和可能存在的操作数QOperandsQ。操?码指C去做什么,操作数提供一些执行这个操作码可能需要的额外的信息。一个抽象的执行引擎每次执行一个指令。这个过E发生在每一个执行的U程中?br />
有时Q执行引擎可能会遇到一个需要调用本地方法的指oQ在q种情况下,执行引擎会去试图调用本地ҎQ但本地Ҏq回Ӟ执行引擎会l执行字节码中的下一个指令。本地方法也可以看成对Java虚拟Z的指令集的一U扩充?br />
军_下一步执行那一条指令也是执行引擎工作的一部分。执行引擎有三种Ҏd得下一条指令。多数指令会执行跟在他会面的指oQ一些像gotoQ?return的指令,会在他们执行的时候决定他们的下一条指令;当一个指令抛出异常时Q执行引擎通过匚wcatch语句来决定下一条应该执行的指o?br />
q_独立性、网l移动性、安全性左右了Java虚拟机指令集的设计。^台独立性是指o集设计的主要影响因素之一。基于堆栈的l构使得Java虚拟机可以在 更多的^C实现。更的操作码,紧凑的结构得字节码可以更有效的利用|络带宽。一ơ性的字节码验证,使得字节码更安全Q而不影响太多的性能?br />
2、执行技?br />
许多U执行技术可以用在Java虚拟机的实现中:解释执行Q及时编译(just-in-time compilingQ,hot-spot compiling,native execution in silicon?br />
3、线E?br />
Java虚拟范定义了一Uؓ了在更多q_上实现的U程模型。JavaU程模型的一个目标时可以利用本地U程。利用本地线E可以让JavaE序中的U程能过在多处理器机器上真正的同时执行?br />
JavaU程模型的一个代价就是线E优先Q一个JavaU程可以?-10的优先上运行?最低,10最高。如果设计者用了本地U程Q他们可能将q?10个优先映射到本C先上。Java虚拟范只定义了,高一点优先的线E可以却一些cpu旉Q低优先U的U程在所有高优先U线E都堵塞Ӟ?可以获取一些cpu旉Q但是这没有保证Q低优先U的U程在高优先U线E没有堵塞时不可以获得一定的cpu旉。因此,如果需要在不同的线E间协作Q你?M用的“同步QsynchronizatoinQ?#8221;?br />
同步意味着两个部分Q对象锁Qobject lockingQ和U程{待、激z?thread wait and notify)。对象锁帮助U程可以不受其他U程的干扰。线E等待、激zd以让不同的线E进行协作?br />
在Java虚拟机的规范中,JavaU程被描qCؓ变量、主内存、工作内存。每一个Java虚拟机的实例都有一个主内存Q他包含了所有程序的变量Q对象、数l合cd量。每一个线E都有自q工作内存Q他保存了哪些他可能用到的变量的拯。规则:
1Q、从d存拷贝变量的值到工作内存?br />
2Q、将工作内存中的值写会主内存?br />
如果一个变量没有被同步化,U程可能以Q何顺序更C内存中的变量。ؓ了保证多U程E序的正的执行Q必M用同步机制?br />
十四、本地方法接口(Native Method InterfaceQ?br />
Java虚拟机的实现q不是必d现本地方法接口。一些实现可能根本不支持本地Ҏ接口。Sun的本地方法接口是JNI(Java Native Interface)?br />
十五、现实中的机器(The Real MachineQ?br />
十六、数学方法:仿真(Eternal Math : A Simulation)
]]>
理解ThreadLocal中提到的变量副本
事实上,我们向ThreadLocal中set的变量不是由ThreadLocal来存储的Q而是ThreadU程对象自n保存。当用户调用ThreadLocal对象的set(Object o)Ӟ该方法则通过Thread.currentThread()获取当前U程Q将变量存入Thread中的一个Map内,而Map的Key是当前的ThreadLocal实例。请看源码,q是最主要的两个函敎ͼ能看出ThreadLocal与Thread的调用关p: