﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-上善若水-随笔分类-深入JVM</title><link>http://www.blogjava.net/DLevin/category/48888.html</link><description>In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation.
To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra</description><language>zh-cn</language><lastBuildDate>Sat, 05 Sep 2015 03:18:43 GMT</lastBuildDate><pubDate>Sat, 05 Sep 2015 03:18:43 GMT</pubDate><ttl>60</ttl><item><title>Java字节码（.class文件）的代码解析</title><link>http://www.blogjava.net/DLevin/archive/2011/09/13/358498.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Mon, 12 Sep 2011 16:05:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2011/09/13/358498.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/358498.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2011/09/13/358498.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/358498.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/358498.html</trackback:ping><description><![CDATA[<div>
<p>Java<span style="font-family: 宋体">二进制指令代码以以下格式紧凑排列（</span>opcode<span style="font-family: 宋体">占一个字节）：</span></p>
<p>opcode operand*</p>
<p><span style="font-family: 宋体">除了</span>tableswitch<span style="font-family: 宋体">和</span>lookupswitch<span style="font-family: 宋体">两条指令中间存在填充字节以外，其他指令都没有填充字节，即使在两条指令之间也没有。因而在读取指令的时候，要根据指令的定义读取。</span>&nbsp;</p>
<p><span style="font-family: 宋体">通过对上面</span>Java<span style="font-family: 宋体">指令集的分析可以知道，</span>Java<span style="font-family: 宋体">指令集中很大一部分没有操作数，因而对这部分指令，只需要读取一个字节的操作码，将操作码映射成助记符即可。</span>&nbsp;</p>
<p><span style="font-family: 宋体">而对其他带操作数的指令，则需要根据不同类型分析（由于</span>apache<span style="font-family: 宋体">中的</span>bcel<span style="font-family: 宋体">（</span>Binary Code Engineering Library<span style="font-family: 宋体">）对字节码的支持，操作码和助记符的映射可以用</span>com.sun.org.apache.bcel.internal.Constats<span style="font-family: 宋体">中提供的映射表数组来完成）。</span></p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>1.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span style="font-family: 宋体">处理两条特殊的指令</span>tableswitch</strong><strong><span style="font-family: 宋体">和</span>lookupswitch</strong><strong><span style="font-family: 宋体">指令。</span></strong></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-family: 宋体">对这两条指令，首先都要去掉填充字符以使</span>defaultbyte1<span style="font-family: 宋体">索引号是字对齐的。</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">private</span></strong> <strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">static</span></strong> <strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">void</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> make4ByteAlignment(ByteSequence codes) {</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> usedBytes = codes.getIndex() % 4;</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> paddingBytes = (usedBytes == 0) ? 0 : 4 - usedBytes;</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">for</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'">(</span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> i = 0;i &lt; paddingBytes;i++) {</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; codes.readByte();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">}</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-family: 宋体">对</span>tableswitch<span style="font-family: 宋体">指令，读取</span>defaultoffset<span style="font-family: 宋体">值，最小项的值，最大项的值以及在最小项和最大项之间每一项的</span>offset<span style="font-family: 宋体">值。并且将读取到的</span>offset<span style="font-family: 宋体">值和当前指令的基地址相加：</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> defaultOffset1 = baseOffset + codes.readInt();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.append(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\tdefault = #"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> + defaultOffset1);</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> low = codes.readInt();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> high = codes.readInt();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> npair1 = high - low + 1;</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.append(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">", npairs = "</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> + npair1 + </span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">);</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">for</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'">(</span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> i = low;i &lt;= high;i++) {</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> match = i;</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; offset = baseOffset + codes.readInt();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.append(String.<em>format</em>(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\tcase %d : #%d\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">, match, offset));</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-family: 宋体">对</span>lookupswitch<span style="font-family: 宋体">指令，读取</span>defaultoffset<span style="font-family: 宋体">值，键值对数值（</span>npairs<span style="font-family: 宋体">），以及</span>npairs<span style="font-family: 宋体">对的键值对，将得到的</span>offset<span style="font-family: 宋体">值和当前指令的基地址相加：</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> defaultOffset2 = baseOffset + codes.readInt();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.append(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\tdefault = #"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> + defaultOffset2);</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> npairs2 = codes.readInt();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.append(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">", npairs = "</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> + npairs2 + </span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">);</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">for</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'">(</span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> i = 0;i &lt; npairs2;i++) {</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> match = codes.readInt();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; offset = baseOffset + codes.readInt();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; builder.append(String.<em>format</em>(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\tcase %d : #%d\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">, match, offset));</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>2.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span style="font-family: 宋体">所有条件跳转指令都有两个字节的偏移量操作数（</span><span>if&lt;cond&gt;, if_icmp&lt;cond&gt;, ifnull, ifnonnull, if_acmp&lt;cond&gt;</span></strong><strong><span style="font-family: 宋体">）。无条件跳转指令</span>goto</strong><strong><span style="font-family: 宋体">和子例程跳转指令</span>jsr</strong><strong><span style="font-family: 宋体">也都是两个字节的偏移量作为操作数。</span></strong></p>
<p style="margin-left: 18pt; text-indent: 0cm; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">offset = baseOffset + codes.readShort();</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.append(String.<em>format</em>(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\t\t#%d\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">, offset));</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>3.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span style="font-family: 宋体">对宽偏移量的跳转指令</span>goto_w</strong><strong><span style="font-family: 宋体">和子例程跳转指令</span>jsr_w</strong><strong><span style="font-family: 宋体">的操作数是四个字节的偏移量。</span></strong></p>
<p style="margin-left: 18pt; text-indent: 0cm; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">offset = baseOffset + codes.readInt();</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.append(String.<em>format</em>(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\t\t#%d\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">, offset));</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>4.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong>wide</strong><strong><span style="font-family: 宋体">指令，则继续读取下一条指令，并将</span>wide</strong><strong><span style="font-family: 宋体">参数设置为</span>true</strong><strong><span style="font-family: 宋体">。</span></strong></p>
<p style="margin-left: 18pt; text-indent: 0cm; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">byteCodeToString(codes, pool, verbose, true);</span></p>
<p style="margin-left: 18pt; text-indent: 0cm; text-align: left" align="left">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>5.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span style="font-family: 宋体">还有一些指令值以一个字节的局部变量索引号作为操作数的，如果有</span>wide</strong><strong><span style="font-family: 宋体">修饰，则用两个字节作为操作数，代表局部变量索引号。这样的指令有：</span><span>aload, iload, fload, lload, dload, astore, istore, fstore, lstore, dstore, ret</span></strong><strong><span style="font-family: 宋体">。</span></strong></p>
<p style="margin-left: 18pt; text-indent: 0cm; text-align: left" align="left"><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">if</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'">(wide) {</span></p>
<p style="margin-left: 18pt; text-indent: 0cm; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp; index = codes.readUnsignedShort();</span></p>
<p style="margin-left: 18pt; text-indent: 0cm; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">} </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">else</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> {</span></p>
<p style="margin-left: 18pt; text-indent: 0cm; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp; index = codes.readUnsignedByte();</span></p>
<p style="margin-left: 18pt; text-indent: 0cm; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">}</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.append(String.<em>format</em>(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\t\t%%%d\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">, index));</span></p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>6.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong>iinc</strong><strong><span style="font-family: 宋体">指令，以一个字节的局部变量索引号和一个自己的常量作为参数；如果以</span>wide</strong><strong><span style="font-family: 宋体">修饰，则该指令的局部变量索引号和常量都占两个字节。</span></strong></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp; </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">if</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'">(wide) {</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; index = codes.readUnsignedShort();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constValue = codes.readShort();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp; } </span><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">else</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> {</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; index = codes.readUnsignedByte();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; constValue = codes.readByte();</span></p>
<p style="text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">&nbsp;&nbsp;&nbsp; }</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.append(String.<em>format</em>(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\t\t%d %d\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">, index, constValue));</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>7.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span style="font-family: 宋体">对象操作指令，它们的操作数都是常量池中的索引，长度为两个字节。指向</span>CONSTANT_Class_info</strong><strong><span style="font-family: 宋体">类型的结构，这些指令有</span><span>new, checkcast, instanceof, anewarray</span></strong><strong><span style="font-family: 宋体">。</span></strong></p>
<p style="text-indent: 18pt; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">index = codes.readUnsignedShort();</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.append(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\t\t"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> + pool.getClassInfo(index).toInstructionString(verbose) + </span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">);</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>8.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span style="font-family: 宋体">所有字段操作指令，它们的操作数都是常量池中的索引，长度为两个字节。指向</span>CONSTANT_Fieldref_info</strong><strong><span style="font-family: 宋体">类型结构，这些指令有</span>getfield, putfield, getstatic, putstatic</strong><strong><span style="font-family: 宋体">。</span></strong></p>
<p style="text-indent: 18pt; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">index = codes.readUnsignedShort();</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.append(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\t\t"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> + pool.getFieldRefInfo(index).toInstructionString(verbose) + </span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">);</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>9.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span style="font-family: 宋体">非接口方法调用指令，也都是以两个字节的索引号作为操作数，指向常量池中的</span>CONSTANT_Methodref_info</strong><strong><span style="font-family: 宋体">类型结构，这些指令有</span>invokespecial, invokevirtual, invokestatic</strong><strong><span style="font-family: 宋体">。</span></strong></p>
<p style="text-indent: 18pt; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">index = codes.readUnsignedShort();</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.append(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\t\t"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> + pool.getMethodRefInfo(index).toInstructionString(verbose) + </span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">);</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><strong>&nbsp;</strong></p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>10.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span></strong><strong><span style="font-family: 宋体">接口方法调用指令</span>invokeinterface</strong><strong><span style="font-family: 宋体">，它有四个字节的操作数，前两个字节为常量池的索引号，指向</span>CONSTANT_InterfaceMethodref_info</strong><strong><span style="font-family: 宋体">类型，第三个字节为</span>count</strong><strong><span style="font-family: 宋体">，表示参数的字节数，最后一个字节为</span>0</strong><strong><span style="font-family: 宋体">值。</span></strong></p>
<p style="text-indent: 18pt; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">index = codes.readUnsignedShort();</span></p>
<p style="text-indent: 18pt; text-align: left" align="left"><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> nargs = codes.<span style="background: silver">readUnsignedByte</span>(); </span><span style="font-size: 10pt; color: #3f7f5f; font-family: 'Courier New'">//Historical, redundant</span></p>
<p style="text-indent: 18pt; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.append(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\t\t"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> + pool.getInterfaceMethodRefInfo(index).toInstructionString(verbose));</span></p>
<p style="text-indent: 18pt; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.append(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">" : "</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> + nargs + </span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">);</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">codes.<span style="background: silver">readUnsignedByte</span>(); </span><span style="font-size: 10pt; color: #3f7f5f; font-family: 'Courier New'">//reserved should be zero</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>11.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span></strong><strong><span style="font-family: 宋体">基本类型的数组创建指令</span>newarray</strong><strong><span style="font-family: 宋体">，它的操作数为一个字节的类型标识。</span></strong></p>
<p style="text-indent: 18pt; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">String type = Constants.</span><em><span style="font-size: 10pt; color: #0000c0; font-family: 'Courier New'">TYPE_NAMES</span></em><span style="font-size: 10pt; color: black; font-family: 'Courier New'">[codes.readByte()];</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.append(String.<em>format</em>(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\t\t(%s)\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">, type));</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>12.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span></strong><strong><span style="font-family: 宋体">多维数组的创建指令</span>multianewarray</strong><strong><span style="font-family: 宋体">，它有三个字节的操作数，前两个字节为索引号，指向</span>CONSTANT_Class_info</strong><strong><span style="font-family: 宋体">类型，表示数组的类型，最后一个字节指定数组的维度。</span></strong></p>
<p style="text-indent: 18pt; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">index = codes.readUnsignedShort();</span></p>
<p style="text-indent: 18pt; text-align: left" align="left"><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">int</span></strong><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> dimensions = codes.readUnsignedByte();</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.append(String.<em>format</em>(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\t\t%s (%d)\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">, pool.<span style="background: silver">getClassInfo</span>(index).getName(), dimensions));</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>13.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span></strong><strong><span style="font-family: 宋体">常量入栈指令</span>ldc</strong><strong><span style="font-family: 宋体">，以一个字节的索引号作为参数，指向</span>CONSTANT_Integer_info</strong><strong><span style="font-family: 宋体">、</span>CONSTANT_Float_info</strong><strong><span style="font-family: 宋体">、</span>CONSTANT_String_info</strong><strong><span style="font-family: 宋体">、</span>CONSTANT_Class_info</strong><strong><span style="font-family: 宋体">类型，表示要入栈的常量值（</span>int</strong><strong><span style="font-family: 宋体">类型值、</span>float</strong><strong><span style="font-family: 宋体">类型值、</span>String</strong><strong><span style="font-family: 宋体">引用类型值或对象引用类型值）。</span></strong></p>
<p style="text-indent: 18pt; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">index = codes.readUnsignedByte();</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.append(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\t\t"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> + pool.getPoolItem(index).toInstructionString(verbose) + </span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">);</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>14.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span></strong><strong><span style="font-family: 宋体">宽索引的常量入栈指令</span>ldc_w</strong><strong><span style="font-family: 宋体">，以两个字节的索引号作为参数，指向</span>CONSTANT_Integer_info</strong><strong><span style="font-family: 宋体">、</span>CONSTANT_Float_info</strong><strong><span style="font-family: 宋体">、</span>CONSTANT_String_info</strong><strong><span style="font-family: 宋体">、</span>CONSTANT_Class_info</strong><strong><span style="font-family: 宋体">类型，表示要入栈的常量值（</span>int</strong><strong><span style="font-family: 宋体">类型值、</span>float</strong><strong><span style="font-family: 宋体">类型值、</span>String</strong><strong><span style="font-family: 宋体">引用类型值或对象引用类型值）。</span></strong></p>
<p style="text-indent: 18pt; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">index = codes.readUnsignedShort();</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.<span style="background: silver">append</span>(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\t\t"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> + pool.getPoolItem(index).toInstructionString(verbose) + </span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">);</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>15.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span></strong><strong><span style="font-family: 宋体">宽索引的常量入栈指令</span>ldc2_w</strong><strong><span style="font-family: 宋体">，以两个字节的索引号作为参数，指向</span>CONSTANT_Long_info</strong><strong><span style="font-family: 宋体">、</span>CONSTANT_Double_info</strong><strong><span style="font-family: 宋体">类型，表示要入栈的常量值（</span>long</strong><strong><span style="font-family: 宋体">类型值、</span>double</strong><strong><span style="font-family: 宋体">类型值）。</span></strong></p>
<p style="text-indent: 18pt; text-align: left" align="left"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">index = codes.readUnsignedShort();</span></p>
<p style="margin-left: 18pt; text-indent: 0cm"><span style="font-size: 10pt; color: black; font-family: 'Courier New'">builder.append(</span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\t\t"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'"> + pool.getPoolItem(index).toInstructionString(verbose) + </span><span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">"\n"</span><span style="font-size: 10pt; color: black; font-family: 'Courier New'">);</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>16.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span></strong><strong>bipush</strong><strong><span style="font-family: 宋体">指令，以一个字节的常量作为操作数。</span></strong></p>
<p style="margin-left: 18pt; text-indent: 0cm"><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">byte </span></strong><span>constByte = codes.readByte();</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">builder.append(<span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">&#8220;\t&#8221;</span> + constByte);</p>
<p style="margin-left: 18pt; text-indent: 0cm">&nbsp;</p>
<p style="margin-left: 18pt; text-indent: -18pt"><strong><span>17.<span style="font: 7pt 'Times New Roman'">&nbsp;&nbsp; </span></span></strong><strong>sipush</strong><strong><span style="font-family: 宋体">指令，以两个字节的常量作为操作数。</span></strong></p>
<p style="margin-left: 18pt; text-indent: 0cm"><strong><span style="font-size: 10pt; color: #7f0055; font-family: 'Courier New'">short </span></strong><span>constShort = codes.readShort();</span></p>
<p style="margin-left: 18pt; text-indent: 0cm">builder.append(<span style="font-size: 10pt; color: #2a00ff; font-family: 'Courier New'">&#8220;\t&#8221;</span> + constShort);</p>
<p style="margin-left: 18pt">&nbsp;</p>
<p><span style="font-family: 宋体">以上还有一些没有完成的代码，包括字段（方法）的签名和描述符没有解析，有一些解析的格式还需要调整等。不管怎么样，总体的结构就是这样了，其它的都是细节问题，这里不讨论了。</span>&nbsp;</p>
<p><span style="font-family: 宋体">参见</span>bcel<span style="font-family: 宋体">项目的</span>org.apache.bcel.classfile.Utility<span style="font-family: 宋体">类.<br /><br />2010年10月6日</span></p></div><img src ="http://www.blogjava.net/DLevin/aggbug/358498.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2011-09-13 00:05 <a href="http://www.blogjava.net/DLevin/archive/2011/09/13/358498.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java二进制指令代码解析</title><link>http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Mon, 12 Sep 2011 16:03:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/358497.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/358497.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/358497.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 小注：去年在看《深入解析JVM》书的时候做的一些记录，同时参考了《Java虚拟机规范》。只是对指令的一些列举，加入了一些自己的理解。可以用来查询。Java二进制指令代码解析Java源码在运行之前都要编译成为字节码格式（如.class文件），然后由ClassLoader将字节码载入运行。在字节码文件中，指令代码只是其中的一部分，里面还记录了字节码文件的编译版本、常量池、访问权限、所有成员...&nbsp;&nbsp;<a href='http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html'>阅读全文</a><img src ="http://www.blogjava.net/DLevin/aggbug/358497.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2011-09-13 00:03 <a href="http://www.blogjava.net/DLevin/archive/2011/09/13/358497.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java字节码（.class文件）格式详解（三）</title><link>http://www.blogjava.net/DLevin/archive/2011/09/05/358035.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Mon, 05 Sep 2011 15:38:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2011/09/05/358035.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/358035.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2011/09/05/358035.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/358035.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/358035.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 2.11&nbsp;在ClassFile、method_info、field_info中同时存在的Attribute  2.11.1&nbsp;&nbsp;&nbsp; &nbsp;Synthetic Attribute  Synthetic Attribute用于指示当前类、接口、方法或字段由编译器生成，而不在源代码中存在（不包含类初始函数和实例初始函数）。相同的功能还有一种方式就是在类、接口、...&nbsp;&nbsp;<a href='http://www.blogjava.net/DLevin/archive/2011/09/05/358035.html'>阅读全文</a><img src ="http://www.blogjava.net/DLevin/aggbug/358035.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2011-09-05 23:38 <a href="http://www.blogjava.net/DLevin/archive/2011/09/05/358035.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java字节码（.class文件）格式详解（二）</title><link>http://www.blogjava.net/DLevin/archive/2011/09/05/358034.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Mon, 05 Sep 2011 15:33:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2011/09/05/358034.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/358034.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2011/09/05/358034.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/358034.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/358034.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 2.9&nbsp;&nbsp;&nbsp;methods  methods数组记录了类或接口中的所有方法，包括实例方法、静态方法、实例初始化方法和类初始化方法，但不包括父类或父接口中定义的方法。methods数组中每项都是method_info类型值，它描述了方法的详细信息，如名称、描述符、方法中的attribute（如Code Attribute记录了方法的字节码）等。          met...&nbsp;&nbsp;<a href='http://www.blogjava.net/DLevin/archive/2011/09/05/358034.html'>阅读全文</a><img src ="http://www.blogjava.net/DLevin/aggbug/358034.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2011-09-05 23:33 <a href="http://www.blogjava.net/DLevin/archive/2011/09/05/358034.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java字节码（.class文件）格式详解（一）</title><link>http://www.blogjava.net/DLevin/archive/2011/09/05/358033.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Mon, 05 Sep 2011 15:24:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2011/09/05/358033.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/358033.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2011/09/05/358033.html#Feedback</comments><slash:comments>3</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/358033.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/358033.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 小介：去年在读《深入解析JVM》的时候写的，记得当时还想着用自己的代码解析字节码的，最后只完成了一部分。现在都不知道还有没有保留着，貌似Apache有现成的BCEL工程可以做这件事。当时也只是为了学习。这份资料主要参考《深入解析JVM》和《Java虚拟机规范》貌似是1.2版本的，整理出来的。里面包含了一些自己的理解和用实际代码的测试。有兴趣的童鞋可以研究研究。嘿嘿。要有错误也希望能为小弟指点出来，...&nbsp;&nbsp;<a href='http://www.blogjava.net/DLevin/archive/2011/09/05/358033.html'>阅读全文</a><img src ="http://www.blogjava.net/DLevin/aggbug/358033.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2011-09-05 23:24 <a href="http://www.blogjava.net/DLevin/archive/2011/09/05/358033.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java虚拟机内部体系结构（摘记，待完善）:)</title><link>http://www.blogjava.net/DLevin/archive/2011/09/05/358032.html</link><dc:creator>DLevin</dc:creator><author>DLevin</author><pubDate>Mon, 05 Sep 2011 15:20:00 GMT</pubDate><guid>http://www.blogjava.net/DLevin/archive/2011/09/05/358032.html</guid><wfw:comment>http://www.blogjava.net/DLevin/comments/358032.html</wfw:comment><comments>http://www.blogjava.net/DLevin/archive/2011/09/05/358032.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/DLevin/comments/commentRss/358032.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/DLevin/services/trackbacks/358032.html</trackback:ping><description><![CDATA[<div><div><p>Java<span style="font-family:宋体;">虚拟机体内部系结构包括</span>class<span style="font-family:宋体;">文件、类装载子系统、运行时数据区、之行引擎、本地方法调用结构，其中运行时数据区包括方法区、堆、</span>Java<span style="font-family:宋体;">栈、程序计数器、本地方法栈等。具体结构如下图所示（摘自</span>Inside Java Virtual Machine<span style="font-family:宋体;">）：</span></p>  <p><img src="http://www.blogjava.net/images/blogjava_net/dlevin/jvm_structure.png" width="501" height="428" alt="" /><br /></p>  <p>&nbsp;</p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;"><strong><span>1.<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong>class</strong><strong><span style="font-family:宋体;">文件</span></strong></p>  <p><span style="font-family:宋体;">在</span>Java<span style="font-family:宋体;">中，所有源文件都编译成二进制的字节码，然后由虚拟机装载运行。一般这样的字节码是以</span>class<span style="font-family:宋体;">文件的形式存在。在运行时，由</span>ClassLoader<span style="font-family:宋体;">类（</span>System ClassLoader or User-defined ClassLoader<span style="font-family:宋体;">）找到对应的</span>class<span style="font-family:宋体;">文件，读取其中的字节码，然后交由虚拟机解析运行。</span></p>  <p><span style="font-family:宋体;">在</span>class<span style="font-family:宋体;">文件中，包含了定义一个类或接口的所有信息，包括类名、访问权限、父类名、继承的所有接口、所有字段、所有方法、方法中的代码、属性等信息，并且每个</span>class<span style="font-family:宋体;">文件的开头还包含了魔术值和版本信息，魔术值用以标识当前的字节码是合法的字节码，版本表示生成当前字节码的编译器版本，从而虚拟机获知其版本而做特定处理，如果对于虚拟机不支持的字节码版本号拒绝加载。</span></p>  <p><span style="font-family:宋体;">在</span>class<span style="font-family:宋体;">文件中，很多信息都是以字符串的形式存放，比如对外部类成员或方法的引用，这些字符串信息在链接的时候由虚拟机解析。每个</span>Java<span style="font-family:宋体;">类，不管是包成员类还是内部类都会生成一个单独的</span>class<span style="font-family:宋体;">文件，因而</span>class<span style="font-family:宋体;">文件是相对独立的。详细信息参考</span>class<span style="font-family:宋体;">文件格式。</span></p>  <p>&nbsp;</p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;"><strong><span>2.<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span style="font-family:宋体;">类装载子系统</span></strong></p>  <p><span style="font-family:宋体;">类装载子系统负责查找</span>class<span style="font-family:宋体;">文件，读取字节码，做部分简单的检验，如魔数是否正确，版本是否受支持，各种数据格式是否正确等。部分解析后的字节码数据存放到方法区中，最后创建字节码代表的类或接口的</span>Class<span style="font-family:宋体;">实例。</span></p>  <p><span style="font-family:宋体;">在</span>Java<span style="font-family:宋体;">中，类装载系统是通过</span>ClassLoader<span style="font-family:宋体;">来完成的。虚拟机规范中，定义了启动类装载器和用于定义类装载器。在</span>sun<span style="font-family:宋体;">提供的虚拟机中，包括了启动类装载器、扩展类装载器、系统类装载器、用户定义类装载器。他们以父子链的方式组织在一起。除了启动类装载器，其他的装载器都是</span>ClassLoader<span style="font-family:宋体;">的子类。</span>ClassLoader<span style="font-family:宋体;">定义了一些方法可以帮助用户定义自己的类装载器，如</span>defineClass<span style="font-family:宋体;">等。详情参考</span>Java<span style="font-family:宋体;">中的</span>ClassLoader<span style="font-family:宋体;">。</span></p>  <p>&nbsp;</p>  <p><strong><span style="font-family:宋体;">如何卸载类数据？（第七章）</span></strong></p>  <p>&nbsp;</p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;"><strong><span>3.<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span style="font-family:宋体;">运行时数据区</span></strong></p>  <p><span style="font-family:宋体;">运行时数据区保存了所有在运行时的信息。包括方法区、</span>Java<span style="font-family:宋体;">栈、堆、程序寄存器、本地方法栈等。其中方法区和堆只在虚拟机中保存一份实例，因而需要处理多线程的同步问题；</span>Java<span style="font-family:宋体;">栈、程序寄存器是每个线程中有单独的实例，因而对不同的线程，他们的数据是私有的。</span></p>  <p>&nbsp;</p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;"><strong><span>3.1<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp; </span></span></strong><strong><span style="font-family:宋体;">方法区</span></strong></p>  <p style="margin-left:18.0pt;text-indent:0cm;"><span style="font-family:宋体;">方法区中保存了读取的字节码信息（包括常量池，静态方法和静态成员信息）、字节码代表的</span>Class<span style="font-family:宋体;">类实例、一个指向加载它的</span>ClassLoader<span style="font-family:宋体;">实例。</span></p>  <p style="margin-left:18.0pt;text-indent:0cm;">Java<span style="font-family:宋体;">程序可以有两种方式来获取某个类的</span>Class<span style="font-family:宋体;">实例：</span></p>  <p style="margin-left:36.0pt;text-indent:-18.0pt;"><span><span>1.<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span>Class.forName()<span style="font-family:宋体;">方法</span></p>  <p style="margin-left:36.0pt;text-indent:-18.0pt;"><span><span>2.<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span>Object.getClass()<span style="font-family:宋体;">方法</span></p>  <p style="margin-left:18.0pt"><span style="font-family:宋体;">通过</span>Class<span style="font-family:宋体;">实例获取和该类或接口相关的任何信息。参考</span>Class<span style="font-family:宋体;">类的定义。</span></p>  <p style="margin-left:18.0pt"><span style="font-family:宋体;">（注：对有启动</span>ClassLoader<span style="font-family:宋体;">加载的类，</span>Class<span style="font-family:宋体;">方法中的</span>getClassLoader<span style="font-family:宋体;">方法返回</span>null<span style="font-family:宋体;">）</span></p>  <p style="margin-left:18.0pt;text-indent:0cm;">&nbsp;</p>  <p style="margin-left:18.0pt;text-indent:0cm;"><span style="font-family:宋体;">为加快执行速度，可以在方法区中引入方法表机制，记录能被外界调用的该类的实例方法，包括父类中继承下来的方法。<strong>（第八章详细介绍？）</strong></span><strong></strong></p>  <p style="margin-left:18.0pt;text-indent:0cm;"><strong>&nbsp;</strong></p>  <p style="margin-left:18.0pt;text-indent:0cm;"><span style="font-family:宋体;">方法区中根据类名搜索类信息，算法：散列、搜索树等。</span></p>  <p style="margin-left:18.0pt;text-indent:0cm;">&nbsp;</p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;"><strong><span>3.2<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp; </span></span></strong><strong>Java</strong><strong><span style="font-family:宋体;">栈</span></strong></p>  <p style="margin-left:18.0pt;text-indent:0cm;"><span style="font-family:宋体;">虚拟机为每个线程生成一个</span>Java<span style="font-family:宋体;">栈，因而对不同的线程，栈内的数据都是私有的。</span>Java<span style="font-family:宋体;">栈由栈帧组成，</span>Java<span style="font-family:宋体;">栈的操作只有两种，压入栈帧和弹出栈帧。线程中每个方法的调用都会在</span>Java<span style="font-family:宋体;">栈压入一个栈帧；每次方法返回（正常方法或抛异常返回），该方法对应的栈帧都会从栈中弹出。</span></p>  <p style="margin-left:18.0pt;text-indent:0cm;">&nbsp;</p>  <p style="margin-left:36.0pt;text-indent:-36.0pt;"><strong><span><span>3.2.1<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span></strong><strong><span style="font-family:宋体;">栈帧</span></strong></p>  <p style="margin-left:36.0pt;text-indent:0cm;"><span style="font-family:宋体;">栈帧由操作数栈、局部变量区和栈帧数据组成。由于</span>Java<span style="font-family:宋体;">中的指令是基于栈而设计的，因而很多指令的默认操作数就是操作数栈中的数据。操作数栈用于保存指令的操作数和指令操作后的结果。</span></p>  <p style="margin-left:36.0pt;text-indent:0cm;"><span style="font-family:宋体;">局部变量区用于保存当前方法的局部变量。</span></p>  <p style="margin-left:36.0pt;text-indent:0cm;"><span style="font-family:宋体;">栈帧数据区则保存当前栈帧的信息，如指向当前类常量池的指针，用于操作数为常量池索引的指令；还有一些和特定虚拟机实现相关的信息和调试信息。</span></p>  <p style="margin-left:18.0pt;text-indent:0cm;">&nbsp;</p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;"><strong><span>3.3<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp; </span></span></strong><strong><span style="font-family:宋体;">程序寄存器</span></strong></p>  <p><span style="font-family:宋体;">每个线程在执行时都会保存当前指令的下一条指令的地址，以控制程序的之行流程。</span></p>  <p>&nbsp;</p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;"><strong><span>3.4<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp; </span></span></strong><strong><span style="font-family:宋体;">堆</span></strong></p>  <p><span style="font-family:宋体;">堆保存了程序在运行时的所有对象。在</span>Java<span style="font-family:宋体;">中，所有的对象都是保存在堆中的，而外部通过对象的引用来访问对象。由于</span>Java<span style="font-family:宋体;">存在垃圾回收器，因而</span>Java<span style="font-family:宋体;">对象可能被移动，以减少内存碎片。其中一种实现可以很好的解决移动对象而需要改变所有该对象的引用变量的技术，即将堆分为句柄池和对象池。对象池中的对象保存了对象的真正内容，而句柄池中的项包含两个指针，一个指向对象，一个指向类数据。一个对象引用就是指向句柄的之战。这样当需要移动对象时，只要改变句柄池中指向对象的指针值即可。然而这种设计是以牺牲速度为代价的，因为这样每次访问对象就要多经历一次指针定位。</span></p>  <p>&nbsp;</p>  <p><span style="font-family:宋体;">在某些垃圾回收器实现中，对象需要额外的信息，如果引用计数的垃圾收集器，需要为每个对象记录引用计数信息；而对另外有些机制，则可能需要暂时保存某些数据。这些额外的数据可以保存在类中，也可以在记录在其他地方。类似的还有同步机制中的数据和记录是否已经调用过</span>finalize<span style="font-family:宋体;">方法的信息。</span></p>  <p>&nbsp;</p>  <p><span style="font-family:宋体;">在</span>Java<span style="font-family:宋体;">中有指令用于在内存中分配对象，却没有显式的指令来释放内存中的对象。</span></p>  <p>&nbsp;</p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;"><strong><span>3.5<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp; </span></span></strong><strong><span style="font-family:宋体;">本地方法栈</span></strong></p>  <p><span style="font-family:宋体;">当</span>Java<span style="font-family: 宋体; ">方法调用本地方法的时候，当前线程的程序寄存器是不确定的值。程序的执行也转向本地方法。本地方法可以正常返回，也可以抛出异常。抛出的异常会在调用该本地方法的指令中重新抛出。</span></p>  <p>&nbsp;</p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;"><strong><span>4.<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span style="font-family:宋体;">执行引擎</span></strong></p>  <p><span style="font-family:宋体;">每个用户线程（即不包括垃圾回收线程等）都有一个执行引擎实例，用以执行字节码指令。</span></p>  <p>&nbsp;</p>  <p style="margin-left:18.0pt;text-indent:-18.0pt;"><strong><span>5.<span style="font:7.0pt &quot;Times New Roman&quot;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></strong><strong><span style="font-family:宋体;">本地方法接口</span></strong></p>  <p>Java<span style="font-family:宋体;">程序可以通过本地方法接口来调用本地方法。<br /><br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; 于2010-10-06<br /></span></p></div></div><img src ="http://www.blogjava.net/DLevin/aggbug/358032.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/DLevin/" target="_blank">DLevin</a> 2011-09-05 23:20 <a href="http://www.blogjava.net/DLevin/archive/2011/09/05/358032.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>