📄 chap08.html
字号:
<!-- All material contained herein is copyright (c) McGraw-Hill Professional Books
All Rights Reserved. No use of this material may be made without express written
permission of the copyright holder. HTML conversions by Mega Space [barry@megaspace.com] -->
<HTML>
<HEAD>
<META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=ISO-8859-1">
<TITLE>Understanding Digital Signatures: Inside the Java Virtual Machine
by Bill Venners - Beta Version</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF">
<TABLE BORDER="0" WIDTH="100%">
<TR><TD><A HREF="http://www.pbg.mcgraw-hill.com/betabooks/stores.html" tppabs="http://www.pbg.mcgraw-hill.com/betabooks/stores.html" target="bottom"><IMG SRC="hotkey.gif" tppabs="http://www.pbg.mcgraw-hill.com/betabooks/images/hotkey.gif" ALIGN="LEFT" BORDER="0" WIDTH="40" HEIGHT="40" ALT="Orders"></A>
<IMG SRC="order_text.gif" tppabs="http://www.pbg.mcgraw-hill.com/betabooks/images/order_text.gif" WIDTH="103" HEIGHT="41" ALT="Orders"></TD>
<TD ALIGN="RIGHT"><A HREF="chap07.html" tppabs="http://www.pbg.mcgraw-hill.com/betabooks/venners/chap07.html"><IMG SRC="backward.gif" tppabs="http://www.pbg.mcgraw-hill.com/betabooks/images/backward.gif" BORDER="0" ALT="Backward" WIDTH="32" HEIGHT="32"></A> <A HREF="chap09.html" tppabs="http://www.pbg.mcgraw-hill.com/betabooks/venners/chap09.html"><IMG SRC="forward.gif" tppabs="http://www.pbg.mcgraw-hill.com/betabooks/images/forward.gif" BORDER="0" ALT="Forward" WIDTH="32" HEIGHT="32"></A></TD></TR>
<TR><TD COLSPAN="2"><A HREF="mailto:computing@mcgraw-hill.com"><IMG SRC="hotkey.gif" tppabs="http://www.pbg.mcgraw-hill.com/betabooks/images/hotkey.gif" ALIGN="LEFT" BORDER="0" WIDTH="40" HEIGHT="40" ALT="Comments"></A>
<IMG SRC="comment_text.gif" tppabs="http://www.pbg.mcgraw-hill.com/betabooks/images/comment_text.gif" WIDTH="73" HEIGHT="39" ALT="Comments"></TD></TR>
<TR><TD COLSPAN="2"><FONT FACE="ARIEL,HELVETICA" SIZE="-1"><I>© 1997 The McGraw-Hill Companies, Inc. All rights reserved. <BR>Any use of this Beta Book is subject to the rules stated in the <A HREF="http://www.mcgraw-hill.com/corporate/news_info/copyrttm.htm" tppabs="http://www.mcgraw-hill.com/corporate/news_info/copyrttm.htm" target="_top">Terms of Use</A>.</I></FONT><br>
<script language="javascript">
document.write("<a href='http://banners.linkbuddies.com/click.php?id=237296'><img src='http://banners.linkbuddies.com/image.php?id=237296&ref=" + document.referrer + "' width=468 height=60 alt='Click Here' border=0></a>");
</script></TD></TR>
</TABLE>
<HR>
<P><H1>Chapter Eight</H1></P>
<P><H2>The Linking Model</H2></P>
<P>From the programmer韘 perspective, one of the most important aspects of Java韘 architecture to understand is the linking model. As mentioned in earlier chapters, Java韘 linking model allows you to design class loader objects that extend your application in custom ways at run-time. Through class loader objects, your application can load and dynamically link to classes and interfaces that were unknown or did not even exist when your application was compiled.</P>
<P>The engine that drives Java韘 linking model is the process of resolution. The previous chapter described in detail all the various stages in the lifetime of a class, except for resolution. This chapter looks at resolution in depth, and shows how the process of resolution fits in with dynamic extension. It gives an overview of the linking model, explains constant pool resolution, describes method tables, shows how to write and use class loaders, and gives several examples.</P>
<H3><EM><P>Dynamic Linking and Resolution</P>
</EM></H3><P>When you compile a Java program, you get a separate class file for each class or interface in your program. Although the individual class files may appear to be independent, they actually harbor symbolic connections to one another and to the class files of the Java API. When you run your program, the Java Virtual Machine loads your program韘 classes and interfaces and hooks them together in a process of <I>dynamic linking</I>. As your program runs, the Java Virtual Machine builds an internal web of interconnected classes and interfaces.</P>
<P>A class file keeps all its symbolic references in one place, the constant pool. Each class file has a constant pool, and each class or interface loaded by the Java Virtual Machine has an internal version of its constant pool. The internal constant pool is an implementation-specific data structure that maps to the constant pool in the class file. Thus, after a type is initially loaded, all the symbolic references from the type reside in the type韘 internal constant pool.</P>
<P>At some point during the running of a program, if a particular symbolic reference is to be used, it must be <I>resolved</I>. Resolution is the process of finding the entity identified by the symbolic reference and replacing the symbolic reference with a direct reference. Because all symbolic references reside in the constant pool, this process is often called <I>constant pool resolution</I>.</P>
<P>As described in Chapter 6, "The Java Class File," the constant pool is organized as a sequence of items. Each item has a unique index, much like an array element. A symbolic reference is one kind of item that may appear in the constant pool. Java Virtual Machine instructions that use a symbolic reference specify the index in the constant pool where the symbolic reference resides. For example, the <FONT FACE="Courier New">getstatic</FONT> opcode, which pushes the value of a static field onto the stack, is followed in the bytecode stream by an index into the constant pool. The constant pool entry at the specified index, a <FONT FACE="Courier New">CONSTANT_Fieldref_info</FONT> entry, reveals the fully qualified name of the class in which the field resides, and the name and type of the field.</P>
<P>Keep in mind that the Java Virtual Machine contains a separate internal constant pool for each class and interface it loads. When an instruction refers to the fifth item in the constant pool, it is referring to the fifth item in the constant pool for the current class, the class that defined the method the Java Virtual Machine is currently executing.</P>
<P>Several instructions, from the same or different methods, may refer to the same constant pool entry, but each constant pool entry is resolved only once. After a symbolic reference has been resolved for one instruction, subsequent attempts to resolve it by other instructions take advantage of the hard work already done, and use the same direct reference resulting from the original resolution.</P>
<P>Linking involves not only the replacement of symbolic references with direct ones, it also involves checking for correctness and permission. As mentioned in Chapter 7, "The Lifetime of a Class," the checking of symbolic references for existence and access permission (one aspect of the full verification phase) is likely performed during resolution. For example, when a Java Virtual Machine resolves a <FONT FACE="Courier New">getstatic</FONT> instruction to a field of another class, the Java Virtual Machine checks to make sure that:</P>
<UL><LI> The other class exists.
<LI> This class has permission to access the other class.
<LI> The named field exists in the other class.
<LI> The field has the expected type (symbolic references to fields include the field type).
<LI> This class has permission to access the field.
<LI> That the field is really static--a class variable and not an instance variable.</UL>
<P>If any of these checks fail, an error is thrown and resolution fails. Otherwise, the symbolic reference is replaced by the direct reference and resolution succeeds.</P>
<P>As described in Chapter 7, "The Lifetime of a Class," different implementations of the Java Virtual Machine are permitted to perform resolution at different times during the execution of a program. An implementation may choose to link everything up front by following all symbolic references from the initial class, then all symbolic references from subsequent classes, until every symbolic reference has been resolved. In this case, the application would be completely linked before its <FONT FACE="Courier New">main()</FONT> method was ever invoked. This approach is called <I>early resolution</I>. Alternatively, an implementation may choose to wait until the very last minute to resolve each symbolic reference. In this case, the Java Virtual Machine would resolve a symbolic reference only when it is first used by the running program. This approach is called <I>late resolution</I>. Other implementations could choose a resolution strategy in-between these two extremes.</P>
<P>Although a Java Virtual Machine implementation has some freedom in choosing when to resolve symbolic references, every Java Virtual Machine must give the outward impression that it uses late resolution. No matter when a particular Java Virtual Machine performs its linking, it will always throw any error that results from attempting to resolve a symbolic reference at the point in the execution of the program where the symbolic reference was actually used for the first time. In this way, it will always appear to the user as if the linking were late. If a Java Virtual Machine does early linking, and during early linking discovers that a class file is missing, it won韙 report the class file missing by throwing the appropriate error until later in the program when something in that class file is actually used. If the class is never used by the program, the error will never be thrown.</P>
<H3><EM><P>Resolution and Dynamic Extension</P>
</EM></H3><P>In addition to simply linking types at run-time, Java applications can decide at run-time which types to link. Java韘 architecture allows Java programs to be <I>dynamically extended</I>, the process of deciding at run-time other types to use, loading them, and using them. You can dynamically extend a Java application by creating a class loader object and using it to load types that are not part of your original application, but are determined by your running application. An example of dynamic extension is a Java-capable web browser loading class files for applets from across a network. When the browser starts, it doesn韙 know what class files it will be loading across the network.</P>
<P>To dynamically extend a Java application, you must include a class loader as part of your application. To create your own class loader, you write a subclass of <FONT FACE="Courier New">java.lang.ClassLoader</FONT> and implement the <FONT FACE="Courier New">loadClass()</FONT> method. When the <FONT FACE="Courier New">loadClass()</FONT> method of a class loader successfully loads a type, it returns a <FONT FACE="Courier New">java.lang.Class</FONT> object to represent the newly loaded (and optionally, verified, prepared, resolved, and initialized) type. Here韘 the declaration of the <FONT FACE="Courier New">loadClass()</FONT> method:</P>
<PRE><P><FONT FACE="Courier New">begin</FONT></P>
<FONT FACE="Courier New"><P>// A method declared in class java.lang.ClassLoader:</P>
<P>protected abstract Class loadClass(String name, boolean resolve)</P>
<P> throws ClassNotFoundException;</P>
</FONT><P><FONT FACE="Courier New">end</FONT></P></PRE>
<P>The class loader takes care of loading, linking, and initializing types. Admittedly the name <FONT FACE="Courier New">ClassLoader</FONT> might lead you to believe it is responsible only for loading, and not linking or initialization, but the name <FONT FACE="Courier New">ClassLoaderLinkerAndInitializer</FONT> is a bit unwieldy. Also, the purpose of creating your own class loader is to customize the load phase while still using the primordial link and initialization mechanisms. Class loaders differ not in how they link or initialize types, but in how they load types. In other words, class loaders distinguish themselves by the manner in which they produce a binary form for a type given a fully qualified name.</P>
<P>The <FONT FACE="Courier New">loadClass()</FONT> method encompasses all three steps of loading, linking, and initialization of a new type. You pass the fully qualified name of the requested type to <FONT FACE="Courier New">loadClass()</FONT> in the parameter <FONT FACE="Courier New">name</FONT>. If you want linking and initialization to be performed at this time, you pass <FONT FACE="Courier New">true</FONT> in the parameter <FONT FACE="Courier New">resolve</FONT>. If <FONT FACE="Courier New">loadClass()</FONT> is invoked with <FONT FACE="Courier New">resolve</FONT> set to <FONT FACE="Courier New">false</FONT>, it will load but not link and initialize the type. From your programs, you will likely invoke <FONT FACE="Courier New">loadClass()</FONT> with the <FONT FACE="Courier New">resolve</FONT> parameter always set to <FONT FACE="Courier New">true</FONT>. Normally, the <FONT FACE="Courier New">loadClass()</FONT>method is invoked with the <FONT FACE="Courier New">resolve</FONT> parameter set to <FONT FACE="Courier New">false</FONT> only by the virtual machine itself. (More on this later in this chapter.)</P>
<P>As mentioned in earlier chapters, each class loader--primordial or object--is awarded its own name space, which is populated by the names of the types it has loaded. Dynamic extension and name spaces are both supported by one aspect of the process of resolution: the way a virtual machine chooses a class loader when it resolves a symbolic reference to a type.</P>
<P>When the resolution of a constant pool entry requires loading a type, the virtual machine uses the same class loader that loaded the referencing type to load the referenced type. For example, imagine a <FONT FACE="Courier New">Cat</FONT> class refers via a symbolic reference in its constant pool to a type named <FONT FACE="Courier New">Mouse</FONT>. Assume <FONT FACE="Courier New">Cat</FONT> was loaded by a class loader object. When the virtual machine resolves the reference to <FONT FACE="Courier New">Mouse</FONT>, it checks to see if <FONT FACE="Courier New">Mouse</FONT> has been loaded into the name space to which <FONT FACE="Courier New">Cat</FONT> belongs. (It checks to see if the class loader that loaded <FONT FACE="Courier New">Cat</FONT> has previously loaded a type named <FONT FACE="Courier New">Mouse</FONT>.) If not, the virtual machine requests <FONT FACE="Courier New">Mouse</FONT> from the same class loader that loaded <FONT FACE="Courier New">Cat</FONT>. This is true even if a class named <FONT FACE="Courier New">Mouse</FONT> had previously been loaded into a different name space. When a symbolic reference from a type loaded by the primordial class loader is resolved, the Java Virtual Machine uses the primordial class loader to load the referenced type. When a symbolic reference from a type loaded by a class loader object is resolved, the Java Virtual Machine uses the same class loader object to load the referenced type.</P>
<H3><EM><P>Constant Pool Resolution</P>
</EM></H3><P>This section describes the details of resolving each type of constant pool entry, including the errors that may be thrown during resolution. If an error is thrown during resolution, the error is seen as being thrown by the instruction that refers to the constant pool entry being resolved. Besides the errors described here, individual instructions that trigger the resolution of a constant pool entry may cause other errors to be thrown. For example, <FONT FACE="Courier New">getstatic</FONT> causes a <FONT FACE="Courier New">CONSTANT_Fieldref_info</FONT> entry to be resolved. If the entry is resolved successfully, the virtual machine performs one additional check: it makes sure the field is actually static (a class variable and not an instance variable). If the field is not static, the virtual machine throws an error. Any extra errors that may be thrown during resolution besides those described in this section are described for each individual instruction in Appendix A.</P>
<H3><P>Resolution of CONSTANT_Class_info Entries</P>
</H3><P>Of all the types of constant pool entries, the most complicated to resolve is <FONT FACE="Courier New">CONSTANT_Class_info</FONT>. This type of entry is used to represent symbolic references to classes (including array classes) and interfaces. Several instructions, such as <FONT FACE="Courier New">new</FONT> and <FONT FACE="Courier New">anewarray</FONT>, refer directly to <FONT FACE="Courier New">CONSTANT_Class_info</FONT> entries. Other instructions, such as <FONT FACE="Courier New">putfield</FONT> or <FONT FACE="Courier New">invokevirtual</FONT>, refer indirectly to <FONT FACE="Courier New">CONSTANT_Class_info</FONT> entries through other types of entry. For example, the <FONT FACE="Courier New">putfield</FONT> instruction refers to a <FONT FACE="Courier New">CONSTANT_Fieldref_info</FONT> entry. The <FONT FACE="Courier New">class_index</FONT> item of a <FONT FACE="Courier New">CONSTANT_Fieldref_info</FONT> gives the constant pool index of a <FONT FACE="Courier New">CONSTANT_Class_info</FONT> entry.</P>
<P>The details of resolving a <FONT FACE="Courier New">CONSTANT_Class_info</FONT> entry vary depending on whether or not the type is an array and whether the referencing type (the one that contains in its constant pool the <FONT FACE="Courier New">CONSTANT_Class_info</FONT> entry being resolved) was loaded via the primordial class loader or a class loader object.</P>
<P><H4><EM>Array Classes</P></EM></H4>
<P>A <FONT FACE="Courier New">CONSTANT_Class_info</FONT> entry refers to an array class if its <FONT FACE="Courier New">name_index</FONT> refers to a <FONT FACE="Courier New">CONSTANT_Utf8_info</FONT> string that begins with a left bracket, as in "<FONT FACE="Courier New">[I</FONT>." As described in Chapter 6, "The Java Class File," internal array names contain one left bracket for each dimension, followed by an element type. If the element type begins with an "<FONT FACE="Courier New">L</FONT>," as in "<FONT FACE="Courier New">Ljava.lang.Integer;</FONT>," the array is an array of references. Otherwise, the element type is a primitive type, such as "<FONT FACE="Courier New">I</FONT>" for <FONT FACE="Courier New">int</FONT> or "<FONT FACE="Courier New">D</FONT>" for <FONT FACE="Courier New">double</FONT>, and the array is an array of primitive types.</P>
<P>In the case of an array of references, the virtual machine resolves the element type. For example, if resolving an array class with the name "<FONT FACE="Courier New">[[Ljava.lang.Integer;</FONT>," the virtual machine would make certain class <FONT FACE="Courier New">java.lang.Integer</FONT> is loaded, linked, and initialized.</P>
<P>The end product of the resolution of a symbolic reference to an array class is a <FONT FACE="Courier New">Class</FONT> instance that represents the array class. If a <FONT FACE="Courier New">Class</FONT> instance has already been created for the array type being resolved, that same <FONT FACE="Courier New">Class</FONT> instance is used. Otherwise, the virtual machine creates a new <FONT FACE="Courier New">Class</FONT> instance to represent the newly resolved array type.</P>
<P><H4><EM>Non-Array Classes and Interfaces</P></EM></H4>
<P>A <FONT FACE="Courier New">CONSTANT_Class_info</FONT> entry whose <FONT FACE="Courier New">name_index</FONT> refers to a <FONT FACE="Courier New">CONSTANT_Utf8_info</FONT> string that doesn韙 begin with a left bracket is a symbolic reference to non-array class or an interface. Resolution of this kind of symbolic reference is a multiple step process.</P>
<P>The Java Virtual Machine performs the same basic steps, described below as Steps 1 and 2a through 2e, to resolve any symbolic reference (any <FONT FACE="Courier New">CONSTANT_Class_info</FONT> entry) to a non-array class or interface. In Step 1, the type is loaded. In Steps 2a through 2e, the type is linked and initialized. The precise way in which the virtual machine performs these steps depends on whether the referencing type was loaded via the primordial class loader or a class loader object.</P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -