<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Biaobiaoqi的博客]]></title>
  <link href="http://biaobiaoqi.github.io/atom.xml" rel="self"/>
  <link href="http://biaobiaoqi.github.io/"/>
  <updated>2017-03-19T22:49:15+08:00</updated>
  <id>http://biaobiaoqi.github.io/</id>
  <author>
    <name><![CDATA[Biaobiaoqi]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[[译] 设计一个现代的缓存]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2017/03/19/design-of-a-modern-cache/"/>
    <updated>2017-03-19T22:43:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2017/03/19/design-of-a-modern-cache</id>
    <content type="html"><![CDATA[<blockquote><p>原文：http://highscalability.com/blog/2016/1/25/design-of-a-modern-cache.html</p></blockquote>

<p>缓存是提升性能的通用方法，现在大多数的缓存实现都使用了经典的技术。这篇文章中，我们会发掘 <a href="https://github.com/ben-manes/caffeine">Caffeine</a> 中的现代的实现方法。Caffeine 是一个开源的 Java 缓存库，它能提供高命中率和出色的并发能力。期望读者们能被这些想法激发，进而将它们应用到任何你喜欢的编程语言中。</p>

<h2>驱逐策略</h2>

<p>缓存的<strong>驱逐策略是为了预测哪些数据在短期内最可能被再次用到</strong>，从而提升缓存的命中率。由于简洁的实现、高效的运行时表现以及在常规的使用场景下有不错的命中率，LRU（Least Recently Used）策略或许是最流行的驱逐策略。但 LRU 通过历史数据来预测未来是局限的，它会认为最后到来的数据是最可能被再次访问的，从而给与它最高的优先级。</p>

<p>现代缓存扩展了对历史数据的使用，结合就近程度（recency）和访问频次（frequency）来更好的预测数据。其中一种保留历史信息的方式是使用 popularity sketch（一种压缩、概率性的数据结构）来从一大堆访问事件中定位频繁的访问者。可以参考 <a href="http://dimacs.rutgers.edu/~graham/pubs/papers/cmsoft.pdf">CountMin Sketch</a> 算法，它由计数矩阵和多个哈希方法实现。发生一次读取时，矩阵中每行对应的计数器增加计数，估算频率时，取数据对应是所有行中计数的最小值。这个方法让我们从空间、效率、以及适配矩阵的长宽引起的哈希碰撞的错误率上做权衡。</p>

<p><img src="http://biaobiaoqi.u.qiniudn.com/CountMinSketch%E7%AE%97%E6%B3%95.jpg" alt="CountMinSketch 算法.jpg" /></p>

<p><a href="https://arxiv.org/pdf/1512.00727.pdf">Window TinyLFU</a>（W-TinyLFU）算法将 sketch 作为过滤器，当新来的数据比要驱逐的数据高频时，这个数据才会被缓存接纳。这个许可窗口给予每个数据项积累热度的机会，而不是立即过滤掉。这避免了持续的未命中，特别是在突然流量暴涨的的场景中，一些短暂的重复流量就不会被长期保留。为了刷新历史数据，一个时间衰减进程被周期性或增量的执行，给所有计数器减半。
<img src="http://biaobiaoqi.u.qiniudn.com/Window%20TinyLFU.jpg" alt="Window TinyLFU.jpg" /></p>

<p>对于长期保留的数据，W-TinyLFU 使用了分段 LRU（Segmented LRU，缩写 SLRU）策略。起初，一个数据项存储被存储在试用段（probationary segment）中，在后续被访问到时，它会被提升到保护段（protected segment）中（保护段占总容量的 80%）。保护段满后，有的数据会被淘汰回试用段，这也可能级联的触发试用段的淘汰。这套机制确保了访问间隔小的热数据被保存下来，而被重复访问少的冷数据则被回收。</p>

<p><img src="http://biaobiaoqi.u.qiniudn.com/%E7%BC%93%E5%AD%98%E6%B5%8B%E8%AF%95%E7%BB%93%E6%9E%9C.jpg" alt="缓存测试结果.jpg" /></p>

<p>如图中数据库和搜索场景的结果展示，通过考虑就近程度和频率能大大提升 LRU 的表现。一些高级的策略，像 <a href="https://www.usenix.org/legacy/event/fast03/tech/full_papers/megiddo/megiddo.pdf">ARC</a>，<a href="http://web.cse.ohio-state.edu/hpcs/WWW/HTML/publications/papers/TR-02-6.pdf">LIRS</a> 和 <a href="https://arxiv.org/pdf/1512.00727.pdf">W-TinyLFU</a> 都提供了接近最理想的命中率。想看更多的场景测试，请查看相应的论文，也可以在使用 <a href="https://github.com/ben-manes/caffeine/wiki/Simulator">simulator</a> 来测试自己的场景。</p>

<h2>过期策略</h2>

<p>过期的实现里，往往每个数据项拥有不同的过期时间。因为容量的限制，过期后数据需要被懒淘汰，否则这些已过期的脏数据会污染到整个缓存。一般缓存中会启用专有的清扫线程周期性的遍历清理缓存。这个策略相比在每次读写操作时按照过期时间排序的优先队列来清理过期缓存要好，因为后台线程隐藏了的过期数据清除的时间开销。</p>

<p>鉴于大多数场景里不同数据项使用的都是固定的过期时长，Caffien 采用了统一过期时间的方式。这个限制让用 O（1）的有序队列组织数据成为可能。针对数据的写后过期，维护了一个写入顺序队列，针对读后过期，维护了一个读取顺序队列。缓存能复用驱逐策略下的队列以及下面将要介绍的并发机制，让过期的数据项在缓存的维护阶段被抛弃掉。</p>

<h2>并发</h2>

<p>由于在大多数的缓存策略中，数据的读取都会伴随对缓存状态的写操作，并发的缓存读取被视为一个难点问题。传统的解决方式是用同步锁。这可以通过将缓存的数据划成多个分区来进行锁拆分优化。不幸的是热点数据所持有的锁会比其他数据更常的被占有，在这种场景下锁拆分的性能提升也就没那么好了。当单个锁的竞争成为瓶颈后，接下来的经典的优化方式是只更新单个数据的元数据信息，以及使用<a href="http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.110.8469&amp;rep=rep1&amp;type=pdf">随机采样</a>、<a href="https://en.wikipedia.org/wiki/Page_replacement_algorithm#Second-chance">基于 FIFO</a> 的驱逐策略来减少数据操作。这些策略会带来高性能的读和低性能的写，同时在选择驱逐对象时也比较困难。</p>

<p>另一种<a href="http://web.cse.ohio-state.edu/hpcs/WWW/HTML/publications/papers/TR-09-1.pdf">可行方案</a>来自于数据库理论，通过提交日志的方式来扩展写的性能。写入操作先记入日志中，随后异步的批量执行，而不是立即写入到数据结构中。这种思想可以应用到缓存中，执行哈希表的操作，将操作记录到缓冲区，然后在合适的时机执行缓冲区中的内容。这个策略依然需要同步锁或者 tryLock，不同的是把对锁的竞争转移到对缓冲区的追加写上。</p>

<p>在 Caffeine 中，有一组缓冲区被用来记录读写。一次访问首先会被因线程而异的哈希到 stripped ring buffer 上，当检测到竞争时，缓冲区会自动扩容。一个 ring buffer 容量满载后，会触发异步的执行操作，而后续的对该 ring buffer 的写入会被丢弃，直到这个 ring buffer 可被使用。虽然因为 ring buffer 容量满而无法被记录该访问，但缓存值依然会返回给调用方。这种策略信息的丢失不会带来大的影响，因为 W-TinyLFU 能识别出我们希望保存的热点数据。通过使用因线程而异的哈希算法替代在数据项的键上做哈希，缓存避免了瞬时的热点 key 的竞争问题。</p>

<p><img src="http://biaobiaoqi.u.qiniudn.com/StippedReadBuffer.jpg" alt="StippedReadBuffer.jpg" /></p>

<p>写数据时，采用更传统的并发队列，每次变更会引起一次立即的执行。虽然数据的损失是不可接受的，但我们仍然有很多方法可以来优化写缓冲区。所有类型的缓冲区都被多个的线程写入，但却通过单个线程来执行。这种多生产者/单个消费者的模式允许了更简单、高效的算法来实现。</p>

<p>缓冲区和细粒度的写带来了单个数据项的操作乱序的竞态条件。插入、读取、更新、删除都可能被各种顺序的重放，如果这个策略控制的不合适，则可能引起悬垂索引。解决方案是通过状态机来定义单个数据项的生命周期。</p>

<p><img src="http://biaobiaoqi.u.qiniudn.com/%E6%95%B0%E6%8D%AE%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F.jpg" alt="StippedReadBuffer.jpg" /></p>

<p>在<a href="https://github.com/ben-manes/caffeine/wiki/Benchmarks#read-100-1">基准测试</a>中，缓冲区随着哈希表的增长而增长，它的的使用相对更节省资源。读的性能随着 CPU 的核数线性增长，是哈希表吞吐量的 33%。写入有 10%的性能损耗，这是因为更新哈希表时的竞争是最主要的开销。</p>

<p><img src="http://biaobiaoqi.u.qiniudn.com/%E5%B9%B6%E5%8F%91%E5%9F%BA%E5%87%86%E6%B5%8B%E8%AF%95.jpg" alt="并发基准测试.jpg" /></p>

<h2>结论</h2>

<p>还有许多实用的话题没有被覆盖到。包括最小化内存的技巧，当复杂度上升时保证质量的测试技术以及确定优化是否值得的性能分析方法。这些都是缓存的实践者需要关注的点，因为一旦这些被忽视，就很难重拾掌控缓存带来的复杂度的信心。</p>

<p>Caffeine 的设计实现来自于大量的洞见和许多贡献者的共同努力。它这些年的演化离不开一些人的帮助：Charles Fry, Adam Zell, Gil Einziger, Roy Friedman, Kevin Bourrillion, Bob Lee, Doug Lea, Josh Bloch, Bob Lane, Nitsan Wakart, Thomas Müeller, Dominic Tootell, Louis Wasserman, and Vladimir Blagojevic. Thanks to Nitsan Wakart, Adam Zell, Roy Friedman, and Will Chu for their feedback on drafts of this article.<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2017/03/19/design-of-a-modern-cache/'>http://biaobiaoqi.github.io/blog/2017/03/19/design-of-a-modern-cache/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p></p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[邮件服务（三）：实践服务器搭建]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2014/01/22/email-3/"/>
    <updated>2014-01-22T01:14:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2014/01/22/email-3</id>
    <content type="html"><![CDATA[<h2>背景</h2>

<p><a href="http://biaobiaoqi.github.io/tags/you-jian-fu-wu/">邮件服务系列博文</a>中，前两篇介绍了邮件系统的基本功能和安全体系，本文记录了搭建邮箱服务器的实践。</p>

<p><a href="http://en.wikipedia.org/wiki/Sendmail">Sendmail</a> 是一种多用途、支援多种协定的跨网络电子邮件传送代理软件，于 1983 年随着 BSD 4.1c 首次发行，2001 年时的调查，互联网上的邮件服务器有 42%使用 Sendmail，但之后由于多次被发现重大的安全性漏洞，且其设定档过于复杂造成较高的学习门槛等因素，导致市占率下滑。</p>

<p><a href="http://www.postfix.org/">Postfix</a> 被 <a href="http://www.porcupine.org/wietse/">Wietse Zweitze Venema</a> 创造出来以取代 Sendmail。本次实践即使用 Postfix 为域名 biaobiaoqi.me 搭建邮箱服务器，服务器是 Linode 上的 VPS，Ubuntu 12.04LTS，DNS 服务器使用 <a href="http://dnspod.com/">DNSPod</a>。</p>

<!--more-->


<h2>检查 IP 信用度</h2>

<p>在自建邮件服务器之前，先检查所要使用的 IP 的信用度。如果 IP 已经在垃圾邮件联盟的黑名单里面，意味着这个 IP 在此之前或许被用作发送垃圾邮件，不愿意给这个 IP 擦屁股的话，就换个 IP 好了。</p>

<p>可以点下面的链接查看：</p>

<ul>
<li><a href="http://anti-spam.org.cn/">http://anti-spam.org.cn/</a></li>
<li><a href="http://www.justspam.org/check-an-ip">http://www.justspam.org/check-an-ip</a></li>
<li><a href="http://www.spamhaus.org/query/bl?ip=74.125.129.27">http://www.spamhaus.org/query/bl?ip=74.125.129.27</a> #需要将连接末尾的 IP 替换为邮箱服务器的 IP</li>
</ul>


<h2>域名、IP 相关配置</h2>

<h3>FQDN 配置</h3>

<p>选定的邮箱服务器需要有合适的全名。比如可以设定 biaobiaoqi.me 的邮箱服务器的 FQDN 为 mail.biaobiaoqi.me。修改 FQDN 的命令如下：</p>

<p><code>hostname mail.biaobiaoqi.me</code></p>

<p>查看 FQDN 的命令为：</p>

<p><code>hostname -f</code></p>

<p>FQDN 需要记住，之后配置 DNS 记录时需要。</p>

<h3>MX 记录和 A 记录</h3>

<p>配置 MX 记录指向邮箱服务器 IP。</p>

<p><code>biaobiaoqi.me.     600 IN  MX  10 biaobiaoqi.me.</code></p>

<p>使用如下命令查询：</p>

<p><code>dig mx biaobiaoqi.me</code></p>

<p>在 DNS 服务器里增加针对邮箱服务器的 FQDN 的 A 记录。</p>

<p><code>mail.biaobiaoqi.me.    10  IN  A   106.186.114.43</code></p>

<p>使用如下命令查询：</p>

<p><code>dig a mail.biaobiaoqi.me</code></p>

<h3>PTR 配置</h3>

<p>Linode 的域名反向解析设置教程参见：<a href="https://library.linode.com/dns-manager#sph_setting-reverse-dns">Setting Reverse DNS</a></p>

<p>查看 PTR 的命令如下：</p>

<p><code>dig -x 106.186.114.43</code></p>

<p>得到的结果中有如下项则为正确：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>;; ANSWER SECTION:
</span><span class='line'>43.114.186.106.in-addr.arpa. 8640 IN  PTR mail.biaobiaoqi.me.</span></code></pre></td></tr></table></div></figure>


<h3>SPF 配置</h3>

<p>在 DNSPod 上设置 SPF 记录。由于 DNS 本身不支持 SPF 类型，可以使用 TXT 记录代替：</p>

<p><code>biaobiaoqi.me.     600 IN  TXT "v=spf1 a mx -all"</code></p>

<p>可以通过如下命令查询：</p>

<p><code>dig txt biaobiaoqi.me</code></p>

<p>在线测试 SPF 是否部署成功：<a href="http://www.openspf.org/Why?show-form=1">http://www.openspf.org/Why?show-form=1</a></p>

<h2>Postfix 搭建</h2>

<p>使用 Postfix 做 SMTP 服务器，Dovecot 来做 IMAP/POP3 服务器，并使用 Mysql 存储加盐密码作认证。具体教程如下：</p>

<p><a href="https://library.linode.com/email/postfix/postfix2.9.6-dovecot2.0.19-mysql"><strong>Email with Postfix, Dovecot, and MySQL</strong></a></p>

<p>启动 postfix：</p>

<p><code>sudo service postfix start</code></p>

<p>启动 dovecot：</p>

<p><code>sudo dovecot -c /etc/dovecot/dovecot.conf</code></p>

<p>邮件服务启动后，测试相应的端口是否正常开放：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>netstat -nltp | grep dovecot #查看 POP3 （993）和 IMAP （995）的端口是否运行
</span><span class='line'>
</span><span class='line'>netstat -nltp | grep 25 #查看 SMTP（25）的端口是否运行</span></code></pre></td></tr></table></div></figure>


<p>对于没有使用 TLS 或者 STARTTLS 的邮件服务器，可以使用 <a href="http://exchange.mvps.org/smtp_frames.htm">telnet</a> 测试 SMTP 是否部署成功。而本次实践中增加了用户认证，telnet 这种明文传输的协议无法作为测试工具，只好直接使用邮件客户端做功能测试。</p>

<h2>DKIM 配置安装</h2>

<p>具体部署过程参照：<a href="https://rtcamp.com/tutorials/mail/dkim-postfix-ubuntu/">dkim-postfix-ubuntu</a></p>

<p>其中需要注意，如果使用的 Postfix 版本不低于 2.6，需要将/etc/postfix/main.cf 中的 milter_protocol 赋值为 6。
<code>postconf mail_version</code>可以查看 postfix 的版本。</p>

<p>本地测试公钥密钥是否正确：（<a href="http://www.opendkim.org/opendkim-testkey.8.html">opendkim-testkey 命令的介绍</a>）</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>-&gt;#opendkim-testkey -d biaobiaoqi.me -s mail -k /etc/postfix/dkim.key -vvv
</span><span class='line'>opendkim-testkey: key loaded from /etc/postfix/dkim.key
</span><span class='line'>opendkim-testkey: checking key 'mail._domainkey.biaobiaoqi.me'
</span><span class='line'>opendkim-testkey: key not secure #这里大概是用户、权限的问题。不会影响基本功能
</span><span class='line'>opendkim-testkey: key OK</span></code></pre></td></tr></table></div></figure>


<h2>邮箱服务器健康度测试</h2>

<p>完成上述所有步骤后，一个完整的域名邮箱就可以投入使用了。在此之前，可以做一次全面的体检。</p>

<p>推荐使用在线监测服务：<a href="http://www.mail-tester.com/">mail-tester</a>。这是一个全面、界面友好的邮件测试网站。按照要求发送邮件到某邮件地址，然后就可以查到邮件服务器的体检报告了。每项都会有详细的说明。</p>

<h2>遇到的问题记录</h2>

<p>安装部署的过程中，遇到不少问题。简略记录下其中有代表性的一些：</p>

<ul>
<li><p>能成功发送邮件，但无法接受邮件，在日志中查看，全是 status=deferred。解决：FQDN 的设定错误。</p></li>
<li><p>使用<code>sudo service opendkim start</code>无法启动 OpenDKIM。解决：不要使用 service 命令，而是用如下命令启动：<code>/etc/init.d/opendkim start</code></p></li>
<li><p>在使用 Maildir 格式接收邮件时，无法接受邮件。解决：需要自己在用户目录下构建 Maildir/tmp、Maildir/cur、Maildir/new 目录。</p></li>
<li><p>在使用 Maildir 格式接收邮件时，无法接受邮件。解决：本地 Maildir 的权限会造成 Permission denied。</p></li>
<li><p>给 qq 邮箱发送邮件，提示『此地址未验证，请注意识别』。解决：这是 qq 的 bug：<a href="http://edm.marketing100.com/service/news_detail.php?ID=126">参见解释</a></p></li>
</ul>


<h2>管理队列中的邮件</h2>

<p>在服务器端管理队列中得邮件使用<code>postqueue</code>和<code>postsuper</code>命令。使用详情参见：</p>

<ul>
<li><a href="http://www.faqforge.com/linux/server/manage-the-postfix-mailqueue-with-postsuper-postqueue-und-mailq/">Manage the postfix mailqueue with postsuper, postqueue und mailq</a></li>
<li><a href="http://www.postfix.org/postqueue.1.html">postqueue</a></li>
<li><a href="http://www.postfix.org/postsuper.1.html">postsuper</a></li>
</ul>


<h2>其他参考资料</h2>

<ul>
<li><a href="https://rtcamp.com/tutorials/mail/">Emails Tutorials</a></li>
</ul>


<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2014/01/22/email-3/'>http://biaobiaoqi.github.io/blog/2014/01/22/email-3/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[邮件服务（二）：安全、认证和垃圾邮件]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2014/01/21/email-2/"/>
    <updated>2014-01-21T01:38:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2014/01/21/email-2</id>
    <content type="html"><![CDATA[<h2>背景</h2>

<p><a href="http://biaobiaoqi.github.io/tags/you-jian-fu-wu/">邮件服务系列博文</a>中<a href="http://biaobiaoqi.github.io/blog/2014/01/20/email-1/">第一篇博客</a>介绍了邮件服务的基本知识，了解了邮件是如何从发件人的邮件客户端经过不同的传输协议传送到收件人的邮件客户端的。这只是邮件的基本功能实现，但如果发生如下情况，整个邮件系统的生态环境将被扰乱：</p>

<ul>
<li>被不法分子利用邮箱服务器发送垃圾邮件</li>
<li>被其他人伪造域名邮箱发送邮件</li>
<li>被中间人窃取账号密码、甚至重要邮件信息（中间人攻击）</li>
<li>即使自己拥有邮箱服务器的权限，无限制的滥用这个权利给其他邮箱发送邮件也是不好的</li>
</ul>


<p>为了防止以上的种种情况，电子邮箱体系引入了更多的协议和机制。本文对此做浅显的总结，如有出错，还请指出和补充。</p>

<!--more-->


<h2>PTR</h2>

<p><a href="http://en.wikipedia.org/wiki/List_of_DNS_record_types#PTR">PTR</a>(pointer record)是 DNS 记录中的一种。与 A 记录相反，它记录的是由 IP 到 <a href="http://en.wikipedia.org/wiki/Fully_qualified_domain_name">FQDN</a>（fully qualified domain name）的映射。</p>

<p>PTR 并非由 DNS 服务提供商控制，而是通过 IP 提供商来设定。比如博主使用 linode 的 VPS，独立 IP 由 Linode 提供，于是 PTR 也是由 Linode 给予的权限来做设定。</p>

<p>在 DNS 中设置 PTR 能增加邮件的非垃圾邮件权重（不同垃圾邮件黑名单判定机构有不同的判定规则，其中 PTR 是公认的指标之一）。</p>

<p>更多信息参考：<a href="https://rtcamp.com/tutorials/mail/fqdn-reverse-dns-ptr-mx-record-checks/">Checking FQDN, Reverse-DNS/PTR, MX record</a></p>

<h2>SPF</h2>

<p><a href="http://en.wikipedia.org/wiki/Sender_Policy_Framework">SPF</a>(Sender Policy Framework)也是一种 DNS 记录。通过查询发送邮件者的 IP 地址和发送来的邮件地址做对比，进行简单的邮件验证。域名的管理员通过 SPF 记录或 TXT 记录来规定这个域名下哪些 IP 地址是“允许”发送邮件的，这样如果有人用别的 IP 地址来伪造发送的话，收件方可以根据 SPF 来选择拒收这些邮件。</p>

<p>最初的邮件系统没有考虑到各种安全因素，SPF 是在之后的发展过程中提出的。为了兼容不支持 SPF 记录的 DNS 服务器（本博客所使用的 DNSPod 就不支持 SPF 记录。），允许用 TXT 记录来表达 SPF。<a href="www.openspf.org">OpenSPF</a> 建议在这段过渡时期同时添加 SPF 记录和 TXT 记录。</p>

<p>SPF 记录的语法规则见<a href="http://www.openspf.org/SPF_Record_Syntax">链接</a>。这里给出一个简单的例子：</p>

<p><code>v=spf1 a mx -all</code></p>

<p>其中<code>spf1</code>是版本号；<code>a</code>代表把 A 记录对应的 IP 加入信任列表；<code>mx</code>说明把这个域名上的 MX 记录所对应的服务器加入信任列表；<code>-all</code>代表将不是从信任列表发出的邮件强制拒绝(hard fail)。</p>

<p>值得注意的是，经过测试发现 Gmail 并没有对 hard fail 的邮件执行拒绝，只是在 web 界面做出了一些提醒。这里有一篇相关的讨论：<a href="http://www.gossamer-threads.com/lists/spf/discuss/32914">链接</a>。</p>

<p>更多资料参考：<a href="http://t.tt/31/">如何给域名设置 SPF 记录</a></p>

<h2>DKIM</h2>

<p><a href="http://www.dkim.org/">DKIM</a>(DomainKeys Identified Mail)是一种电子邮件的验证技术，使用<a href="http://zh.wikipedia.org/wiki/%E5%85%AC%E5%BC%80%E5%AF%86%E9%92%A5%E5%8A%A0%E5%AF%86">非对称加密</a>为邮件提供了签名与验证的功能。一般来说，发送方会在电子邮件的标头插入 DKIM-Signature 及电子签名，它们由私钥加密。而接收方则透过 DNS 查询得到公开密钥后进行验证。</p>

<p>DKIM 是由 DomainKeys 所改进的协定，大多数的运作方式与 DomainKeys 相同。在 2007 年 2 月时，DKIM 被列入互联网工程工作小组（IETF）的标准提案，并于同年 5 月成为正式标准。</p>

<h2>传输协议</h2>

<p>通过标准的 TCP/IP 做 SMTP 传输，没有任何安全保障，信息容易被截获。为了保证数据的安全，可以使用 SSL/TLS 提供加密链接。对于 IMAP 和 POP3 使用 SSL 加密尚可接受，而 SMTP 会遇到兼容性问题：POP3 和 IMAP 至于 MRA 和 MUA 有关，而使用 SMTP 协议的 MTA 会与其他的 MTA 沟通，除非全世界所有 MTA 节点同时升级为 SSL/TLS 加密的通信协议，否则会出现部分邮件无法中继的状况。</p>

<p><a href="http://en.wikipedia.org/wiki/STARTTLS">STARTTLS</a> 很好的解决了这一问题。STARTTLS 是明文传输协议的扩展，它允许在明文连接的基础上将连接升级为加密连接，而不是使用另外一个加密接口。</p>

<p>一般的，邮箱服务器各协议端口设定如下：</p>

<ul>
<li>IMAP：明文端口 143，加密端口 993</li>
<li>POP3：明文端口 110，加密端口 995</li>
<li>SMTP：明文端口 25，SSL 加密端口 465，STARTTLS 加密端口 587</li>
</ul>


<p>关于 SSL、TLS 和 STARTTLS 的区别，参见<a href="https://www.fastmail.fm/help/technology_ssl_vs_tls_starttls.html">链接</a>。</p>

<h2>查看邮件原始信息</h2>

<p>随着邮件在网络中的中转，邮件头会被加入许多必要信息。理解邮件头能帮助理解邮件服务的整个系统，在遇到垃圾邮件时，也可以助于定位问题所在。</p>

<p>GMAIL 的 Web 端可以查看邮件的详细信息。在邮件内容框里，点击右上角的更多选项，选中 Show original，如图：</p>

<p><img src="http://biaobiaoqi.u.qiniudn.com/emailori-mail.png?imageView/2/w/800/h/800" alt="img" /></p>

<p>下面展示 linode 发送给 someuser@gmail.com 的一封邮件的原始信息：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>Delivered-To: someuser@gmail.com
</span><span class='line'>Received: by 10.114.246.38 with SMTP id xt6csp139967ldc;
</span><span class='line'>        Mon, 20 Jan 2014 06:53:17 -0800 (PST)
</span><span class='line'>X-Received: by 10.182.81.197 with SMTP id c5mr7069251oby.40.1390229596297;
</span><span class='line'>        Mon, 20 Jan 2014 06:53:16 -0800 (PST)
</span><span class='line'>Return-Path: &lt;support@linode.com&gt;
</span><span class='line'>Received: from mail2.linode.com (mail2.linode.com. [173.255.198.11])
</span><span class='line'>        by mx.google.com with ESMTP id f6si835374obr.111.2014.01.20.06.53.15
</span><span class='line'>        for &lt;someuser@gmail.com&gt;;
</span><span class='line'>        Mon, 20 Jan 2014 06:53:16 -0800 (PST)
</span><span class='line'>Received-SPF: pass (google.com: domain of support@linode.com designates 173.255.198.11 as permitted sender) client-ip=173.255.198.11;
</span><span class='line'>Authentication-Results: mx.google.com;
</span><span class='line'>       spf=pass (google.com: domain of support@linode.com designates 173.255.198.11 as permitted sender) smtp.mail=support@linode.com
</span><span class='line'>Received: from li114-242.members.linode.com (li114-242.members.linode.com [69.164.199.242])
</span><span class='line'>  by mail2.linode.com (Postfix) with ESMTP id 6AF9E26A96;
</span><span class='line'>  Mon, 20 Jan 2014 09:53:15 -0500 (EST)
</span><span class='line'>Date: Mon, 20 Jan 2014 09:53:15 -0500 (EST)
</span><span class='line'>From: support@linode.com
</span><span class='line'>To: someuser@gmail.com
</span><span class='line'>Message-ID: &lt;376750868.26764.1390229595437.JavaMail.cfusion@mail2.linode.com&gt;</span></code></pre></td></tr></table></div></figure>


<p>这封邮件里每项的具体含义如下(<a href="https://support.google.com/mail/answer/29436?hl=en">参考</a>以及 <a href="http://www.ietf.org/rfc/rfc0822.txt">RFC0822</a>)：</p>

<ul>
<li>Delivered-TO： 发送的目的地邮箱地址。</li>
<li>Received： 这个字段可以用来追踪邮件的传送路径。字段的基本格式为 from A by B for C。其中 A 为发送方，B 为接收方，C 为收件人邮箱地址。由于邮件可能经过多个 SMTP 服务器中继，故可以有多个 Received 字段。在邮件头中，以自底向上的顺序阅读它们。对于这封邮件，传送路径为：li114-242.members.linode.com -> mail2.linode.com -> mx.google.com -> 10.182.81.197 -> 10.114.246.38.</li>
<li>Received-SPF： 用于记录 SPF 查询来验证邮件的安全性，参考 <a href="http://www.openspf.org/SPF_Received_Header">SPF Received Header</a></li>
<li>Return-Path： 邮件的回复地址，来自于 SMTP 中得<code>mail from:</code>命令。</li>
<li>Date： 邮件的发送时间。</li>
<li>From： 客户端显示的发件人地址，可以跟 Return-Path 中得地址不同。</li>
<li>To： 客户端显示的收件人地址。</li>
</ul>


<p>实际上，Google 估计到 IP 地址为敏感信息，某些情况下会隐藏发送邮件标头中得发件人 IP 地址（<a href="https://support.google.com/mail/answer/26903?hl=zh-Hans&amp;hlrm=en">资料链接</a>）。</p>

<p>不同的客户端对这一功能做了不同的实现。比如 QQ 邮箱以前是能够查看邮件原始信息的，而现在已经关闭了这一功能。</p>

<h2>第三方邮件服务</h2>

<p>邮件服务器的基本功能实现虽然看起来简单，但为了让系统拥有足够的稳定性、邮件送达率，还是得付出不小的努力来与复杂的网络环境做斗争。许多公司为了集中注意力于自己的核心业务，会剥离出邮件发送的业务，使用第三方的邮件服务。国内国外涌现了不少第三方的邮件服务解决方案。自己没有相应的使用经验，谨贴出两个不错的介绍，供大家参考：</p>

<ul>
<li><a href="http://70.io/develop/aws_ses-mailgun-sendcloud.html">邮件发送服务 AWS SES，Mailgun 以及 SendCloud</a></li>
<li><a href="http://www.zhihu.com/question/20088568">知乎的邮件系统为什么不再用 Amazon SES 了？</a></li>
</ul>


<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2014/01/21/email-2/'>http://biaobiaoqi.github.io/blog/2014/01/21/email-2/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[邮件服务（一）：基本框架]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2014/01/20/email-1/"/>
    <updated>2014-01-20T00:11:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2014/01/20/email-1</id>
    <content type="html"><![CDATA[<h2>背景</h2>

<p>电子邮件出现在 1960s 晚期，比打开浏览器就要使用的 HTTP 协议早了 20 年左右,是二十世纪人类最伟大的发明之一。这个古老、经典的框架在网络中运行了五十多年，现今仍然是网络中主要的流量类型之一。</p>

<p>不得不提的是，<a href="http://zh.wikipedia.org/wiki/%E7%94%B5%E5%AD%90%E9%82%AE%E4%BB%B6">wiki</a> 上关于中国的第一封电子邮件的记载：1987 年 9 月 14 日 [1]中国第一封电子邮件是由“德国互联网之父”维纳·措恩与王运丰在北京的计算机应用技术研究所发往德国卡尔斯鲁厄大学的，其内容为英文，大意如下：</p>

<blockquote><p>Across the <strong>Great Wall</strong> we can reach every corner in the world.</p></blockquote>

<p>真的是很有远见呢-,-</p>

<p>前几天梳理了电子邮箱相关的协议和框架组件，准备整理成文。一共<a href="http://biaobiaoqi.github.io/tags/you-jian-fu-wu/">三篇</a>，本文是基本知识总结，第二篇介绍安全认证和防垃圾邮件的规范，第三篇记录了邮件服务器的搭建实践。</p>

<!--more-->


<h2>邮件传输所需要的组件</h2>

<p>在解释邮件的传送过程前，先简单介绍一些术语（可以结合）：</p>

<h3>术语</h3>

<h4>Mail Agent</h4>

<p>一般我们将邮件传输中的组件称作 Mail Agent，根据功能的不同，它们包括了：</p>

<ul>
<li><strong>MUA</strong>（Mail User Agent）：作为客户端程序，负责从服务器接收邮件，向服务器发送邮件。</li>
<li><strong>MTA</strong>（Mail Transfer Agent）：将邮件发送给目的地址的邮件服务器（目的服务器上也有一个 MTA 来做接收）。一般提到的 Mail Server 就是 MTA。</li>
<li><strong>MDA</strong>（Mail Delivery Agent）：根据 MTA 收取的邮件，决定邮件的去向（如果本机就是邮件的目的地址，则放入本地用户邮箱，如果不是，则继续转发）。还可以配置过滤垃圾邮件和自动回复等。</li>
<li><strong>Mailbox</strong>：存储邮件数据。MDA 会将邮件放到不同用户的邮箱中。一般的，有 mbox、Maildir 和 dbmail 等存储格式。mbox 将所有邮件存放到一个文件中，弊端较多。Maildir 则将邮件存为单独的文件。</li>
<li><strong>MRA</strong>（Mail Retrieval Agent）：负责与 MUA 沟通。</li>
</ul>


<h4>协议</h4>

<p>不同 Agent 之间的数据交通都依赖于协议，这些协议包括：</p>

<ul>
<li><strong>SMTP</strong>（Simple Mail Transfer Protocol）：端口 25，面向连接的传输协议。用于 MUA 向 MTA 发送邮件，以及 MTA 之间互相发送和接收邮件。严格来说，MTA 其实仅是指 SMTP。</li>
<li><strong>POP3</strong>（Post Office Protocol3）：离线协议，MUA 从 MRA 获取邮件使用这一协议。首先验证身份，将邮件传输到 MUA 后，用户 Mailbox 中的邮件会被删除。</li>
<li><strong>IMAP</strong>（Internet Message Access Protocol）：与 POP3 类似，用于从 MRA 收取邮件。不过 MUA 与 MRA 之间双向通信，客户端的改动会反映到服务器上。IMAP 整体上为用户带来更为便捷和可靠的体验，POP3 更易丢失邮件或多次下载相同的邮件，但 IMAP 通过邮件客户端与 webmail 之间的双向同步功能很好地避免了这些问题。</li>
</ul>


<h4>其他</h4>

<ul>
<li><p><strong>MX 记录</strong>：邮箱服务器 DNS 的 MX 记录指向邮箱服务器。MX 记录可以设定优先级，数字越小的优先级越高。比如如下两条 MX 记录：<code>gmail.com.     1656    IN  MX  5 gmail-smtp-in.l.google.com.</code>，<code>gmail.com.        1656    IN  MX  10 alt1.gmail-smtp-in.l.google.com.</code>  <br/>
  MTA 会优先选取优先级为 5 的地址发送，如果不成功，再转向优先级低的服务器发送。而较低优先级的服务器只是用作暂存，将邮件保存在待发送的队列中，等主服务器恢复后中继转发到主服务器，以此容错。</p></li>
<li><p><strong>中继转发</strong>（Relay）：邮件从一台 MTA 转发到下一台 MTA，这个操作就成为邮件中继转发。如果所有人都能使用某台 MTA 做中继转发，则这台 MTA 成为 Open Relay。这是很危险的，容易被互联网的邮件流量拖垮，且可能被记录到邮箱服务器黑名单上，坏处多多。需要仔细配置 Relay 规则。</p></li>
</ul>


<h3>邮件传输实例</h3>

<p>从一个简单的应用场景谈起：Biaobiaoqi（简称 B）使用自己的邮箱 hello@biaobiaoqi.me 登陆了自己的 foxmail 邮件客户端，撰写邮件，发送给 shenyapeng(简称 S)，S 的邮箱地址是 shenyapeng@gmail.com。S 打开自己的邮件客户端，查收了邮件。</p>

<p>从用户的角度来看，只是简单的将邮件从 B 发到了 S，而实际上邮件在网络中经过了多个协议的传输。其大致流程如下：</p>

<ul>
<li>1.首先邮件需要从 B 的邮件客户端传送到对应的 biaobiaoqi.me 邮箱服务器</li>
<li>2.biaobiaoqi.me 的邮箱服务器将邮件发送给 gmail.com 邮箱服务器。</li>
<li>3.S 的邮件客户端从 gmail 邮件服务器获取到信收到的邮件。</li>
</ul>


<p>而更技术的解释如下：</p>

<ul>
<li>1.B 登陆 PC 上得 MUA（比如 Foxmail、Thunderbird、Outlook 等），撰写邮件。MUA 使用 SMTP 将邮件发送给 B 的邮箱服务器 mail.biaobiaoqi.me。</li>
<li>2.mail.biaobiaoqi.me 的 MDA 检测邮件头信息，发现需要将邮件发送给 gmail.com 邮箱服务器，于是交予 MTA 来做传输。</li>
<li>3.MTA 通过 gmail.com 的 DNS 中 MX 记录，查找到邮箱服务器的 IP 地址(<code>dig mx gmail.com</code>)，再向目的地中继转发这封邮件。</li>
<li>4.到达目的 MTA 后，邮件被 MDA 送到 Mailbox 中。MRA 开始工作，供 MUA 收取邮件。</li>
<li>5.S 登陆 MUA，使用 POP3 或 IMAP 协议从 gmail.com 的邮箱服务器 MRA 收取邮件。</li>
</ul>


<p>无论是邮件客户端跟邮箱服务器之间认证身份，还是邮件数据的传输，都会进行加密。直接暴露在互联网络中是很危险的。安全认证方面的问题会在下一篇讨论，这里先介绍基本的功能协议。要知道最早发明邮件协议时，也并没有将安全问题考虑在内。</p>

<h2>主要参考资料</h2>

<ul>
<li><a href="http://vbird.dic.ksu.edu.tw/linux_server/0380mail.php">鸟哥的 Linux 私房菜——服务器架设篇</a></li>
<li><a href="http://wiki2.dovecot.org/MailServerOverview">MailServerOverview</a></li>
</ul>


<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2014/01/20/email-1/'>http://biaobiaoqi.github.io/blog/2014/01/20/email-1/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[JVM参数调优：Eclipse启动实践]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2014/01/17/java-performance-tunning-in-practice/"/>
    <updated>2014-01-17T11:16:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2014/01/17/java-performance-tunning-in-practice</id>
    <content type="html"><![CDATA[<p>本文主要参考自<a href="http://book.douban.com/subject/6522893/">《深入理解 Java 虚拟机》</a>。这本书是国人写的难得的不是照搬代码注释的且不是废话连篇的技术书，内容涵盖了 Java 从源码到字节码到执行的整个过程，包括了 JVM（Java Virtual Machine）的架构，垃圾收集的介绍等。这里摘录出关于配置 JVM 基本参数来调优 Eclipse 启动的过程，比较初级，供初学者参考。</p>

<!--more-->


<h2>基础知识</h2>

<p>针对 JVM 的参数调优主要集中在数据区大小的控制和垃圾回收策略的选择。关于 JVM 运行机制等更多内容可参考<a href="http://biaobiaoqi.github.io/tags/jvm/">其他博文</a></p>

<h3>JVM 的运行时数据区</h3>

<p>运行时 JVM 的数据区主要包括各线程私有的栈和程序计数器，线程共享的方法区，以及管理对象的堆（又称回收堆）等。程序运行时，类信息、常量、静态变量等会被加载到方法区。运行过程中几乎所有对象都在堆里，内存占用的空间最大，这也是最值得优化得部分。</p>

<h3>JVM 的垃圾回收策略</h3>

<p>Java 程序中，除了基本类型（primitive types），其他的数据都是以对象的形式存在。对象生命周期有长有短，如果无区别的保留在内存中，会造成内存超载。内存垃圾回收(Garbage Collection, 缩写 GC)就是解决这一问题的策略。</p>

<p><em>注意：JVM 不仅仅只对对象进行垃圾回收，实际上也会对废弃常量和无用的类做回收。</em></p>

<p>垃圾回收首先得找到需要被回收的对象，一般采用根搜索算法来标记处这些过时的对象（另外有一种简单的实现：<a href="http://zh.wikipedia.org/wiki/%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0">引用计数</a>，但存在明显的弊端，即循环引用）。</p>

<p>回收垃圾的过程会消耗计算资源和时间。根据不同的处理方式，垃圾回收有不同的策略，现在常用的是分代收集算法：根据对象的存活周期将堆划分为几代：新生代（Young Generation 或 New Generation）和老生代（Tenured Generation），<a href="http://en.wikipedia.org/wiki/HotSpot">HotSpot 虚拟机</a>里还分出了永生代（基本等同于方法区）。不同代采用不同的垃圾回收策略。</p>

<h4>HotSpot 虚拟机</h4>

<p>HotSpot 虚拟机中，Perm 代指永生代，Old 代指老年代，而新生代使用复制算法，将区域划分为三块：Eden，S0 和 S1（S 是 Survivor 的缩写）。</p>

<p>IBM 研究表明，新生代中的对象 98%是朝生夕死的，三者的比例划分是 8：1：1。对象先分配到 Eden，如果 Eden 中内存占用量达到一定得比例，触发 Minor GC，JVM 会将 Eden 和 S0(或 S1)中存活的对象复制到 S1（或 S0），并清空 Eden 和 S0(或 S1)。如果同时老年代的内存占用量打达到一定比例，会触发 Major GC（也称 Full GC）。通常 Major GC 比 Minor GC 慢 10 倍以上。</p>

<h3>编译过程</h3>

<p>Java 一直号称“Write once, run anywhere”，这个特性正是由 JVM 这一虚拟层来支撑的。</p>

<p>Java 源代码首先编译为 Java 字节码，字节码再被 JVM 加载运行。运行的过程可以是直接针对字节码的解释执行，也可以是经过了 <a href="http://zh.wikipedia.org/wiki/%E5%8D%B3%E6%99%82%E7%B7%A8%E8%AD%AF">JIT</a>（Just in time）编译为机器码后的执行。另外，还有<a href="(http://en.wikipedia.org/wiki/AOT_compiler">静态提前编译器</a>(Ahead Of Time，也缩写为 AOT)，能将源码直接编译为机器码。</p>

<p><a href="http://en.wikipedia.org/wiki/HotSpot">HotSpot 虚拟机</a>的 JIT 编译器有：Client Complier（简称 C1）、Server Complier（简称 C2）以及在 Java7 中堆出的<a href="http://docs.oracle.com/javase/7/docs/technotes/guides/vm/performance-enhancements-7.html">分层编译器</a>。C1 编译器做一些快速的优化，C2 做一些更耗时的优化但是产生更高效的代码，而分层编译器则结合了两者的优点：快速的启动和逐步的优化（brings client startup speeds to the server VM）。</p>

<h3>性能监控和故障处理工具</h3>

<p>对于系统调优和问题定位，周志明在《深入 Java 虚拟机》中总结到</p>

<blockquote><p>给一个系统定位问题的时候，知识、经验是关键基础，数据是依据，工具是运用知识处理数据的手段。这里说的数据包括：运行日志、异常堆栈、GC 日志、线程快照、堆转储快照等……应当意识到工具永远都是知识技能的一层包装，没有什么工具是“秘密武器”。</p></blockquote>

<p>Java 提供了很多工具给开发者来监控和处理运行中的问题。包括命令行工具以及可视化工具</p>

<h4>命令行工具</h4>

<p>比如 jps, jstat, jinfo 等。举例如下：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>jstat -gcutil xxx #xxx 是 jps 查出的 LVMID，查看 gc 相关数据
</span><span class='line'>jstat -gccause xxx#查看 gc 的原因
</span><span class='line'>jinfo  -flag XXX xxx#XXX 是参数名，xxx 是 VMID，查看虚拟机的参数值</span></code></pre></td></tr></table></div></figure>


<h4>可视化工具</h4>

<ul>
<li>JConsole：比较老得分析软件，Java 自带。Windows 下搜 jconsole.exe，Mac 下启动命令为 jconsole</li>
<li><a href="http://www.eclipse.org/mat/">Eclipse Memory Analyzer Tool</a>：用于分析 dump 下的堆数据</li>
<li><a href="http://visualvm.java.net/">VisualVM</a>:推荐，很全能的分析工具</li>
</ul>


<h3>JVM 参数</h3>

<p>这里零散的罗列了一些我用到的简单的 JVM 配置参数：</p>

<p>内存大小控制：</p>

<ul>
<li>-Xmx20M: 堆的最大值</li>
<li>-Xms10M: 堆的初始分配内存</li>
<li>-Xmn: 新生代堆的分配内存</li>
<li>-Xss128k: 线程的栈空间大小</li>
<li>-XX:PermSize=10M | 方法区初始大小</li>
<li>-XX:MaxPermSize=10M |方法区最大值</li>
</ul>


<p>编译相关：</p>

<ul>
<li>-Xint: 关闭 JIT，完全使用解释执行，实践中没什么作用，解释执行很慢。</li>
<li>-client: 启动 C1 编译器</li>
<li>-server: 启动 C2 编译器</li>
<li>-XX:+TieredCompilation: 启动分层编译器</li>
</ul>


<p>其他：</p>

<ul>
<li>-XX:+DisableExplicitGC: 屏蔽 System.gc()调用</li>
<li>-PrintGCDetails: 让 jvm 在每次发生 gc 的时候打印日志，利于分析 gc 的原因和状况</li>
<li>-XX:+HeapDumpOnOutOfMemoryError: 内存溢出时 dump 下 heap，可以通过 Eclipse Memory Analyzer Tool 打开查看。</li>
<li>-XX:HeapDumpPath=/: dump 下的 heap 文件快照的路径。Eclipse 中默认放到项目的根目录中</li>
<li>-XVerify:none: 禁止字节码验证</li>
</ul>


<h2>调优 Eclipse 启动的实践</h2>

<h3>实践环境</h3>

<ul>
<li>CPU： 2.8 GHz Intel Core i7</li>
<li>内存： 8 GB 1333 MHz DDR3</li>
<li>操作系统： OS X 10.9.1(64bits)</li>
<li>Eclipse：Version: Kepler Service Release 1， Build id: 20130919-0819</li>
<li>Java：java version &#8220;1.7.0_45&#8221;</li>
</ul>


<h3>调优实践</h3>

<p>调优 Eclipse 启动实际上就是调优 Eclipse 在 JVM 中的加载和程序启动阶段的运行。由于默认的 Ecpise 启动配置无法适应所有不同的硬件、软件环境，做针对性的调优是必要的。</p>

<p>Eclipse 的启动配置文件是 eclipse.ini，对 JVM 的参数调优直接在该文件中修改。OS X 下，其文件路径为 $ECLIPSE/Eclipse.app/Contents/MacOS/eclipse.ini（<em>注意不是 Eclipse 文件包根目录下得 eclipse.ini</em>）。</p>

<h4>测 Eclipse 启动时间</h4>

<p>要优化 Eclipse 的启动时间，先要能确定 Eclipse 的启动时间。这里推荐网友实现的一个 Eclipse 插件：<a href="http://empirel.iteye.com/blog/1404226">计算启动时间的 Eclipse 插件</a>。下载后放到 Eclipse 的插件包中，启动 Eclipse 即可看到弹窗显示的启动时间。为了得到一个尽可能公平的测试结果，需要在测试过程中关闭其他程序，避免 CPU 负载带来的误差，并多次测试取平均值。</p>

<h4>调优策略</h4>

<p>使用 VisualVM 查看程序的运行状况来定位瓶颈，尝试调优解决。下图是 VisualVM 的示例图，右边图示展示了 GC 的状态以及编译时间、类加载时间和垃圾回收时间等指标。</p>

<p><img src="http://biaobiaoqi.u.qiniudn.com/visualvmscreen.png?imageView/2/w/800/h/800" alt="img" /></p>

<p>也可以通过命令行工具查看 GC 的状态，比如：<code>jstat -gc XXX #其中 XXX 是 jps 查出的进程的 LVMID</code>.</p>

<p>我的实践总结如下：</p>

<ul>
<li>类加载时间过长：禁止加载类时的字节码验证。我们认为 Eclipse 的字节码是可靠的。<code>-XVerify:none</code></li>
<li>编译时间：<code>-client</code>缩短了编译时间，但长期运行的性能可能受影响，<code>-server</code>编译时间长，而编译优化做得更多，后期使用汇报大，<code>-XX:+TieredCompilation</code>分层编译则集合了前两者的优势。没有特殊需求，可选用这一编译选项。</li>
<li>Minor GC 次数太多：新生代空间太小，加大新生代的内存大小。<code>-Xmn800m</code>，同时，增大回收堆的总大小上限（<code>-Xmx1024m</code>）。</li>
<li>Full GC 次数太多：为了避免回收堆由小到大的动态增长增加时间开销，可将其初始大小跟最大上限设定为同一值<code>-Xms1024m</code>，并增加老年代的大小(<code>-XX:PermSize=256m</code>，<code>-XX:MaxPermSize=256m</code>)。</li>
<li>代码中的 GC 调用：Eclipse 代码中有 System.GC()的调用可能促使 JVM 执行垃圾回收，可以通过<code>-XX:+DisableExplicitGC</code>来防止。</li>
<li>垃圾收集器的选择：垃圾收集器有很多，比如 Serial、ParNew、Parallel Scavenge、CMS、G1 等。推荐使用老生代 CMS 新生代 ParNew 的组合来应对 Eclipse 用户交互频繁的情况(<code>-XX:+UseParNewGC</code>、<code>-XX:+UserConcMarkSweepGC</code>)。同时提升 CMS 的垃圾回收的触发条件：<code>-XX:CMSInitiatingOccupancyFraction=85</code>，进一步降低 Full GC 的出现。</li>
</ul>


<h4>最终 eclipse.ini</h4>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>-startup
</span><span class='line'>../../../plugins/org.eclipse.equinox.launcher_1.3.0.v20130327-1440.jar
</span><span class='line'>--launcher.library
</span><span class='line'>../../../plugins/org.eclipse.equinox.launcher.cocoa.macosx.x86_64_1.1.200.v20130807-1835
</span><span class='line'>-product
</span><span class='line'>org.eclipse.epp.package.standard.product
</span><span class='line'>--launcher.defaultAction
</span><span class='line'>openFile
</span><span class='line'>-showsplash
</span><span class='line'>org.eclipse.platform
</span><span class='line'>--launcher.XXMaxPermSize
</span><span class='line'>256m
</span><span class='line'>--launcher.defaultAction
</span><span class='line'>openFile
</span><span class='line'>--launcher.appendVmargs
</span><span class='line'>-vmargs
</span><span class='line'>-Dosgi.requiredJavaVersion=1.6
</span><span class='line'>-XstartOnFirstThread
</span><span class='line'>-Dorg.eclipse.swt.internal.carbon.smallFonts
</span><span class='line'>-Xms1024m
</span><span class='line'>-Xmx1024m
</span><span class='line'>-Xmn800m
</span><span class='line'>-Xdock:icon=../Resources/Eclipse.icns
</span><span class='line'>-XstartOnFirstThread
</span><span class='line'>-Dorg.eclipse.swt.internal.carbon.smallFonts
</span><span class='line'>-XX:+TieredCompilation
</span><span class='line'>-XX:PermSize=256m
</span><span class='line'>-XX:MaxPermSize=256m
</span><span class='line'>-XX:+DisableExplicitGC
</span><span class='line'>-XVerify:none
</span><span class='line'>-XX:+UseParNewGC
</span><span class='line'>-XX:+UserConcMarkSweepGC
</span><span class='line'>-XX:CMSInitiatingOccupancyFraction=85</span></code></pre></td></tr></table></div></figure>


<h2>其他参考</h2>

<ul>
<li><a href="http://www.searchtb.com/2013/07/jvm-gc-introduction-examples.html">《JVM 的 GC 简介和实例》</a> — 搜索技术博客－淘宝.昆仑</li>
</ul>


<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2014/01/17/java-performance-tunning-in-practice/'>http://biaobiaoqi.github.io/blog/2014/01/17/java-performance-tunning-in-practice/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[用Ruby实现的论坛灌水工具：CC98 Post Machine]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/11/05/cc98-post-machine/"/>
    <updated>2013-11-05T15:20:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/11/05/cc98-post-machine</id>
    <content type="html"><![CDATA[<p>ZJU 的校网论坛 CC98 比较活跃。论坛只对校内网开放，而且账号跟学生绑定，每个学生注册的账号数量有限。『十大』是 CC98 的经典页面：基于关注的人数（回帖的用户数而不是回帖的数量）用算法求出 24 小时内最火爆的十个发帖。很多同学都会浏览十大，关注论坛动态。</p>

<p>故事就是从十大引出的。有的社团在宣传活动时，为了扩大宣传面，会发动成员的小马甲顶贴上十大。这种违背社区自然发展轨迹的手段，强奸了关注十大贴的用户的意愿，阻碍了信息的自由流动。</p>

<p>于是萌生了完成一个批量发帖的机器，以其人之道还治其人之身的想法。在下次十大被宣传贴攻占时，能有反击的工具。</p>

<p>工具的用途很简单：使用不同的用户身份模拟真人论坛回帖，增加帖子关注度，以抵抗宣传贴。流程如下：提前收集各路亲朋好友的用户信息作为『预备水军』，『灌水』时，在评论内容文件中输入自定义的评论内容，在命令行参数中制定目标贴，即可实现随机顺序的用户自动顶贴。鉴于现在的功能是顶贴竞争十大，而十大排名是根据关注人数也就是独立用户评论数量做排序的，这里设计的顶贴策略是一个马甲发一条评论。以后可以考虑增加灵活的配置方案，实现更多功能。</p>

<p>项目 Github 地址：<a href="https://github.com/biaobiaoqi/CC98PostMachine/">https://github.com/biaobiaoqi/CC98PostMachine/</a></p>

<p>介于这个工具本身的罪恶的攻击属性，在此强调，工程仅供学习交流和对抗宣传贴。</p>

<!--more-->


<h2>使用说明</h2>

<h3>1.准备顶贴用户</h3>

<p>发帖的 HTTP 请求为 POST 请求。系统验证信息中需要有发帖用户的 username，userid 和 hash 后的 password，这些都能从 cookie 中获得。</p>

<p>有两种方式设置水军的信息：</p>

<h4>(1)浏览器中获取 cookie</h4>

<p>在浏览器中，找到 cookie 的内容。将对应的三项信息抽取出来后，填入<code>water_army.yml</code>文件，由于该文件涉及用户隐私信息，项目中设置了.gitignore，不会上传到 repo 中。可以参照<code>water_army.yml.example</code>的格式自行修改。</p>

<h4>(2)使用<code>RegWaterArmy</code>工具导入</h4>

<p>如果不想手动的查找 cookie 信息，也可以将账号、密码输入到<code>pre_water_army.txt</code>文件中，执行命令来获取相关信息。</p>

<p>在<code>pre_water_army.txt</code>文件中，一行为单位输入用户名和密码，中间用空格隔开。同样由于隐私原因，repo 中没有上传该文件，可以自行将<code>pre_water_army.txt.example</code>改为<code>pre_water_army.txt</code>，然后填入内容。</p>

<p>填入账号、密码后，在 src 目录下运行如下命令：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$CC98POSTMACHINE/src/ruby RegWaterArmy.rb</span></code></pre></td></tr></table></div></figure>


<p>即可自动获取用户的 cookie 信息，并导入<code>water_army.yml</code>文件中。注意，安全起见，完成这一步后，尽量删除账号、密码等隐私信息。</p>

<h3>2.准备评论</h3>

<p>在<code>comments.txt</code>文件中，设置用户评论的内容，每行一条。现在的设计是保证回帖的顺序与文件中的内容顺序一致。如果用户数量超过了文件中的评论条数，则回复时循环使用<code>comments.txt</code>中的评论。建议自行设计评论，且评论数大于等于水军数量。同样需要用户自行修改<code>comments.txt.example</code>为<code>commnets.txt</code>后使用。</p>

<h3>3.执行</h3>

<p>来到 CC98PostMachine 的 src 目录，执行：</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>$CC98POSTMACHINE/src/ruby  PostMachine.rb  POST_URL [SPEED]</span></code></pre></td></tr></table></div></figure>


<p>其中<code>POST_URL</code>为响应帖子的网址，<code>SPEED</code>是设定的两个回帖之间的间隔时间，实际回帖时间做了如下的模糊：<code>SPEED + random(SPEED)</code>。</p>

<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>
</span><span class='line'>Usage: $CC98POSTMACHINE/src/ruby  PostMachine.rb  POST_URL [SPEED].
</span><span class='line'>
</span><span class='line'>    POST_URL is the url address of target post.
</span><span class='line'>
</span><span class='line'>    SPEED is the time gap unit between two posts, it may be 1(s), 10(s) or any other number
</span></code></pre></td></tr></table></div></figure>


<h2>下一步：</h2>

<ul>
<li><p>修复 bug：第一次执行命令时，都只有一个用户能成功回复。（是服务器端的对 cookie 的记录？）</p></li>
<li><p>整理代码结构，解耦，增加配置灵活性，写的更 ruby 一点</p></li>
</ul>


<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/11/05/cc98-post-machine/'>http://biaobiaoqi.github.io/blog/2013/11/05/cc98-post-machine/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[《大规模Web服务开发技术》]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/10/28/scalable-web-service-development/"/>
    <updated>2013-10-28T01:41:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/10/28/scalable-web-service-development</id>
    <content type="html"><![CDATA[<h2>Web 服务开发的心灵鸡汤</h2>

<p><img src="http://img3.douban.com/mpic/s6818566.jpg" alt="img" /></p>

<p>周末去上海陪妹子的两天在路途上看完了这本<a href="http://book.douban.com/subject/6758780/">《大规模 Web 服务开发技术》</a>。</p>

<p>《大规模 Web 服务开发技术》是日本的 <a href="http://www.hatena.com/">Hetena</a> 团队以夏天举办的实习活动的课程讲义为基础整理的开发、运营大规模服务的入门书。书中更多的偏重了 Hetena 技术团队发展过程中的实践经验总结，将一个系统从无到有的发展过程有条理的展现了出来。读完全书，觉得它更像是一本 Web 服务开发的心灵鸡汤，有许多靠谱的总结，但相对零散，刚接触的人很难掌握。当然，心灵鸡汤并不是贬义，只是有不同的针对性。</p>

<!--more-->


<h2>内容</h2>

<p>经验总结的东西一般很难有板有眼的映射到一个成熟的理论框架下。它的知识点相对零散，互相的耦合性比较强，很难在初步接触时建立起深刻的认识。尽管作者已经尽可能的让书的内容跟着服务端架构从小变大的过程走，具备一定的条理性，但要想通过看这种书来补强不谢知识区域，还是有些牵强。</p>

<p>值得称道的是，作者对 Hetena 的技术发展史的描绘，能让读者接触到一个真实系统的成长过程。这是比较难得的一次体验。或许某个时刻，我们能在网上看到一篇讲解分布式存储系统的文章，另一个时刻，我们又看到了一篇将数据库划分的文章，但我们不知道在什么场景需要使用它们。这些零散的知识点，都需要融入到一个整体的经验体系、理论体系中，来发挥它们的作用。实践经验能帮助知识点的梳理，达到这一点。</p>

<p>另外，全书的文笔很轻松，所以读起来比较快。时不时会跳出几句卖萌的表达。或许跟主体内容来自讲义有关吧=)。</p>

<h2>定位</h2>

<p>如此的定位，让本书不太适合想打造实际的技能栈的童鞋。换句话说，想通过这本书实际学习到某门技术是不太可行的。倒是挺适合对 Web 服务端不怎么熟悉，想走走看看了解下概貌，陶冶下情操的童鞋们，把这本书当做闲暇的读物，找找学习的感觉。我就是如此。另外，本身已经有足够的经验，或许也能读读这本书，梳理自己的思路。</p>

<p>这本书让我想起了<a href="http://biaobiaoqi.github.io/blog/2013/07/19/odps-in-alibaba/">阿里的暑期课堂</a>，不过说实话，阿里的那次开放课堂的内容更多的侧重于宣讲，缺少技术架构的内容。希望更多的国内的企业能开放技术培训，让学生们能更早的接触到实践层的东西，某种角度来看也是一种双赢。</p>

<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/10/28/scalable-web-service-development/'>http://biaobiaoqi.github.io/blog/2013/10/28/scalable-web-service-development/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[《黑客：计算机革命的英雄》]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/10/21/hacker/"/>
    <updated>2013-10-21T22:19:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/10/21/hacker</id>
    <content type="html"><![CDATA[<p><img src="http://img3.douban.com/mpic/s6951030.jpg" alt="img" /></p>

<p>《<a href="http://book.douban.com/subject/6860890/">黑客：计算机革命的英雄</a>》不是一本新书，创作于上世纪 80 年代。</p>

<p>这本书我断断续续花了几个月才看完。大量的传奇黑客人物穿插其中，说实话容量略大，到头来也没记住几个名字，不过这不是重点。全书将对整个计算机发展史有深刻影响的黑客文化分三个阶段的娓娓道来，让我重新审视对黑客的看法认识。下面是我对全书的笔记摘要。</p>

<!--more-->


<h2>第一代黑客：MIT 黑客</h2>

<p>他们是最早的黑客，活跃在 20 世纪 50-60 年代。黑客主义刚刚在 MIT 出现的时候主要是专注于体验计算机的魅力；探索和扩展那些迷人的系统的精髓，并利用这些完美的逻辑系统来创造一种文化和一种生活方式。</p>

<p>MIT 的技术模型火车俱乐部（TMRC）发明了很多英文单词。hack 是其中一个，如果一个正在进行的项目或正在构建的产品不仅仅是为了实现某个建设性目标，而且只要参与进来，就会有莫大的乐趣，那么这个项目就叫一个 Hack。</p>

<p>这些黑客们探索事物的本质，掌握它们。通过将东西拆开，了解它们的工作原理，并根据这种理解创造新奇的甚至更有趣的东西，可以学习到关于系统（关于世界）的重要知识。</p>

<p>那时候的计算机非常古老，古老到我只从文字里见到过，比如在纸带上写程序，比如一间房间大小的操作系统，预约时间才能上机，比如批处理系统等。</p>

<p>那时候计算机管理严格，想给系统增加汇编指令，得经过长时间的审批操作，MIT 的黑客们为了节省时间，晚上偷偷的潜入机房改装计算机。当黑客们需要某些东西来帮助他们创造、探索或修理时，他才不管“知识产权”这种在他们看来荒谬的说辞呢。他们市场潜入办公室，拿到需要的工具，用完后再放回去。当然，他们是不以经济利益为目的的。</p>

<p>在黑客们看来，没有任何一个系统是完成了的，总能让它变得更好。他们不断的改进系统，在系统上开发具有挑战性的应用。黑客通过了解和建立各种几乎被所有其他人漠视的系统来改善整个世界的愿望非常强烈。</p>

<h2>第二代黑客：硬件黑客</h2>

<p>源于大名鼎鼎的家酿计算机俱乐部（<a href="http://en.wikipedia.org/wiki/Homebrew_Computer_Club">homebrew computer club</a>）。理念是：互助，分享，恪守黑客道德，反对狭隘的所有权和自私行为。</p>

<p>黑客们从与众不同和贴近群众的角度去看待计算机怎样改变人民的生活。他们对计算机的传播更感兴趣。将原先的庞然大物带入了寻常百姓家。探索精神和自己动手的行为成为这里主要的价值观。</p>

<p>苹果和微软都在这个时期诞生。也是在这个时期，黑客文化开始接受商业的冲突，集中体现在大家对软件版权问题的理解上。这与原有的朴素的共享原则发生了冲突。在原来的观点中，软件属于全人类，是不需要有版权保护的（即使到后来 Stallman 也不认同版权的存在）。</p>

<h2>第三代黑客：游戏黑客</h2>

<p>第三代黑客生活的时代，黑客到的已经大打折扣。一切都是金钱在作怪。黑客道德的一条要旨是电脑本身不会考虑信息产权，但在那个年代，信息产权已经越发的成熟和深入到各个层面。</p>

<p>20 世纪 80 年代，黑客游戏是唯一具有商业价值的艺术形式。黑客必须自己做导演：独立构思、编剧、指挥、执行并美化、最终完成一部杰作。第三代黑客看重的是艺术性。他们中，很多人成为了新闻人物，他们是文华英雄，突破限制并探索电脑系统。</p>

<p>计算机游戏的发展充斥着商业气息，程序员的角色也在滚滚金钱利益关系中被削弱，比如最早游戏编写者所能获得的版权 30%，后来越来越低。</p>

<p>实际上，越往后，商业和黑客主义的并存局面越明显。现在的黑客是在黑客主义和商业两种价值观并不对立的环境中成长起来的。他们正在影响着黑客运动的未来。</p>

<p>保罗格拉汉姆(<a href="http://paulgraham.com/">Paul Graham</a>)认为，黑客主义的价值观不会受到商业的威胁……他们一定能够战胜商业。依靠直觉解决问题，分散化作出决策，强调工作质量，这些都是黑客的理念，而且他们在工作中一直遵循这些理念。</p>

<p>黑客精神开始向企业家精神转变。</p>

<h2>黑客道德</h2>

<p>作者总结的七条『黑客道德准则』：</p>

<ol>
<li>Access to computers and anything which might teach you something about the way the world works should be unlimited and total.
(接触电脑和任何能教会你世界运转方式的通道都应该完全向任何人开放。)</li>
<li>Always yield to the Hands on Imperative!<br/>
(永远践行亲自动电脑操作的指令。)</li>
<li>All information should be free.
(所有信息都应该是免费的。)</li>
<li>Mistrust authority promote decentralization.
(不信任权威，致力于推动反中央集权式统治。)</li>
<li>Hackers should be judged by their hacking, not bogus criteria such as degrees, age, race or position.
(黑客只受其黑客技术水平评价，而不是受制于他们的学位、种族、年龄或社会地位影响。)</li>
<li>You can create art and beauty on a computer.
(计算机可以创造艺术和美感。)</li>
<li>Computers can change your life for the better.
(计算机能让人们的生活变得更美好。)</li>
</ol>


<p>其中，对第三条容易产生误读。实际上，这一条最早出现在黑客道德的未来会议上。布兰德说</p>

<blockquote><p>一方面，信息应该是昂贵的，因为它非常有价值。你在正确的地方得到正确的信息，就会改变你的人生。另一方面，信息应该是免费的，因为获得信息的成本不断地降低。这就是两者的矛盾所在。</p></blockquote>

<p>在黑客文化的发展过程中，黑客道德是原则性的一环。</p>

<p>最早，黑客们认为，对计算机的访问（以及任何可能帮助你认识我们这个世界的事物）应该是不受限制的、完全的。任何人都有动手尝试的权利。『信息应该是自由的』这种信念最直接的体现在优秀的计算机或计算机程序中。</p>

<p>而后随着计算机的发展，出现了分权的思想。这根政治的分权或许有千丝万缕的联系，但更多的是体现了黑客精神中对计算机民主平等的追求。而黑客的信息自由和分权思想甚至对 Internet 的前身 ARPAnet 有重大的影响。</p>

<p>到如今，商业化席卷全球。硅谷一些科技公司也在追寻黑客道德，比如风头正劲的 Facebook 一直以来所阐述的黑客之道（The Hacker Way）。在面临着新的时代，黑客道德也在不停的适应新的挑战。</p>

<p>计算机先驱费尔森斯坦说</p>

<blockquote><p>技术绝不仅仅是死气沉沉的硬件。它还是人们思想的展现。……人们仅仅是活着，而人性更加珍贵，也更加脆弱。所以我们必须要向那些墨守成规的文化挑战，用我们自身的创造力向它挑战。要并且用我们自己的创造力去取得突破……这才是实质</p></blockquote>

<p>这就是黑客道德的实质。</p>

<p>世界都将会从黑客道德中受益。</p>

<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/10/21/hacker/'>http://biaobiaoqi.github.io/blog/2013/10/21/hacker/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[《改变未来的九大算法》]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/10/03/nine-algorithms-that-changed-the-future/"/>
    <updated>2013-10-03T13:19:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/10/03/nine-algorithms-that-changed-the-future</id>
    <content type="html"><![CDATA[<h2>不要在意那些细节</h2>

<p>这是一本关于计算机世界的科普读物。豆瓣链接：<a href="http://book.douban.com/subject/24529132/">请戳</a>。</p>

<p>非常推荐刚接触计算机的朋友花上三五小时将全书通读一遍，没有技术细节，没有公式证明，它会告诉作者挑选出的九大算法出现的缘由和发展的过程。这些不关乎实现细节的思想概括，更能体现算法在整个领域中的存在的原因。它们不再是冷冰冰的算法过程，这是阅读大多数的书籍、教材所难以获得的。</p>

<p>至于我的推荐原因，如下全文。</p>

<!--more-->


<h3>课程学习之弊</h3>

<p>大二大三的那两年，在学校上过了网络、操作系统、数据库、算法设计等诸多计算机领域的基础必修课程。由于生性愚钝加之令人头大的砖头书的体系化、理论化，对于知识的掌握顶多处于知其然却不知其所以然的程度。在这种情况下，学习就退化为了填鸭式的记忆。比如我记得 RSA 的公钥加密流程，却无法理解为何要套用这么一大堆的数学方法来得到如此神奇的效果，比如，我知道数据库有一套日志机制，却不知道它是如何与其他的组件共同构建起一个系统的……没有来龙去脉，就没有对技术发展的感知，没有对技术本质的理解和认识。</p>

<p>经过了四年多的摸爬滚打，到现在对这些基础知识的认识自然远好于当年上课的理解水平，不是因为我在反复的学习课程内容，而是从实践中获得了新的认识。</p>

<h3>从生活出发</h3>

<p>大多数人的思维习惯，倾向于从已知的事物中衍生对新事物的认识，这是一种类推。而高校的大多数教材和教授方式较少考虑到这一点，一种简单粗暴的直入主题的方式被大量的使用，排斥了知识的前因后果，技术的来龙去脉，剩下的只是新体系本身。这增加了学习曲线的入门坡度。</p>

<blockquote><p>理论是灰色的，生活之树常青</p></blockquote>

<p>《改变未来的九大算法》是对歌德这句话的良好实践。</p>

<!--more-->


<p>比如在阐述公钥加密机制时，作者从生活中的寄信讲起，一步步类比到计算机的通信上，并用染料配色的方式举例了迪菲赫尔曼机制。生动形象的描述了算法的原理和来由。</p>

<h3>抛开细节实现</h3>

<p>过往的教材一旦陷入对某些实现细节的阐释，就失去了对全局上下文的把握。就像镜头对焦到了一个点上，造成了周边景物的模糊化效果。</p>

<p>《改变未来的九大算法》全书很少涉及到技术的实现细节，这正是它的优势所在。比如数据库的一致性章节，从一个银行转账的实例讲起，通过所面临的一系列问题的解决方案的描述，让人对数据库的整体实现有了全局的把握。在理解的前提下，更深入的学习也变得更有动力和方向。</p>

<p>奈何自己已经过了朦朦胧胧的那几年，在此将本书强烈推荐给初涉计算机领域的朋友们。当然，其他人粗读一遍，也是对自己知识体系的一次小梳理啦;)。</p>

<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/10/03/nine-algorithms-that-changed-the-future/'>http://biaobiaoqi.github.io/blog/2013/10/03/nine-algorithms-that-changed-the-future/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Java类、实例的初始化顺序]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/09/22/java-initialization/"/>
    <updated>2013-09-22T21:53:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/09/22/java-initialization</id>
    <content type="html"><![CDATA[<p>今晚是阿里巴巴 2013 校园招聘的杭州站笔试。下午匆忙看了两张历年试卷，去现场打了瓶酱油。</p>

<p>题目总体考察点偏基础，倒数第二题（Java 附加题）比较有趣，考察了 Java 初始化机制的细节，在此摘录出来。</p>

<h2>题目</h2>

<p>求如下 java 代码的输出：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">class</span> <span class="nc">T</span>  <span class="kd">implements</span> <span class="n">Cloneable</span><span class="o">{</span>
</span><span class='line'>  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">int</span> <span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
</span><span class='line'>  <span class="kd">public</span> <span class="kd">static</span> <span class="n">T</span> <span class="n">t1</span> <span class="o">=</span> <span class="k">new</span> <span class="n">T</span><span class="o">(</span><span class="s">&quot;t1&quot;</span><span class="o">);</span>
</span><span class='line'>  <span class="kd">public</span> <span class="kd">static</span> <span class="n">T</span> <span class="n">t2</span> <span class="o">=</span> <span class="k">new</span> <span class="n">T</span><span class="o">(</span><span class="s">&quot;t2&quot;</span><span class="o">);</span>
</span><span class='line'>  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="n">print</span><span class="o">(</span><span class="s">&quot;i&quot;</span><span class="o">);</span>
</span><span class='line'>  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="mi">99</span><span class="o">;</span>
</span><span class='line'>  
</span><span class='line'>  <span class="kd">public</span> <span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="n">print</span><span class="o">(</span><span class="s">&quot;j&quot;</span><span class="o">);</span>
</span><span class='line'>  <span class="o">{</span>
</span><span class='line'>      <span class="n">print</span><span class="o">(</span><span class="s">&quot;构造快&quot;</span><span class="o">);</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>  
</span><span class='line'>  <span class="kd">static</span> <span class="o">{</span>
</span><span class='line'>      <span class="n">print</span><span class="o">(</span><span class="s">&quot;静态块&quot;</span><span class="o">);</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>  
</span><span class='line'>  <span class="kd">public</span> <span class="nf">T</span><span class="o">(</span><span class="n">String</span> <span class="n">str</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>      <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">((++</span><span class="n">k</span><span class="o">)</span> <span class="o">+</span> <span class="s">&quot;:&quot;</span> <span class="o">+</span> <span class="n">str</span> <span class="o">+</span> <span class="s">&quot;    i=&quot;</span> <span class="o">+</span> <span class="n">i</span> <span class="o">+</span> <span class="s">&quot;  n=&quot;</span> <span class="o">+</span> <span class="n">n</span><span class="o">);</span>
</span><span class='line'>      <span class="o">++</span><span class="n">n</span><span class="o">;</span> <span class="o">++</span> <span class="n">i</span><span class="o">;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>  
</span><span class='line'>  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">int</span> <span class="nf">print</span><span class="o">(</span><span class="n">String</span> <span class="n">str</span><span class="o">){</span>
</span><span class='line'>      <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">((++</span><span class="n">k</span><span class="o">)</span> <span class="o">+</span><span class="s">&quot;:&quot;</span> <span class="o">+</span> <span class="n">str</span> <span class="o">+</span> <span class="s">&quot;   i=&quot;</span> <span class="o">+</span> <span class="n">i</span> <span class="o">+</span> <span class="s">&quot;   n=&quot;</span> <span class="o">+</span> <span class="n">n</span><span class="o">);</span>
</span><span class='line'>      <span class="o">++</span><span class="n">n</span><span class="o">;</span>
</span><span class='line'>      <span class="k">return</span> <span class="o">++</span> <span class="n">i</span><span class="o">;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>  
</span><span class='line'>  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">){</span>
</span><span class='line'>      <span class="n">T</span> <span class="n">t</span> <span class="o">=</span> <span class="k">new</span> <span class="n">T</span><span class="o">(</span><span class="s">&quot;init&quot;</span><span class="o">);</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>




<!--more-->


<h2>分析</h2>

<p>代码主要考察的是类、变量初始化的顺序。</p>

<p>一般的，我们很清楚类需要在被实例化之前初始化，而对象的初始化则是运行构造方法中的代码。</p>

<p>本题的代码显然没有这么简单了。本题中涉及到了<code>static {…}</code> 和 <code>{…}</code>这种形式的代码块，以及在类的静态变量中初始化该类的对象这种交错的逻辑，容易让人焦躁（类似于密集恐惧症吧=(）。实际上，按照<a href="http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loading-linking-and-initialising/">类的装载、链接和初始化逻辑</a>，以及<a href="http://biaobiaoqi.github.io/blog/2013/09/08/strange-behavior-using-braces-in-java/">对象初始化的顺序</a>来思考，不难得到答案。</p>

<h3>代码组成</h3>

<ul>
<li><p>成员变量
  2~6 行的变量是 static 的，为类 T 的静态成员变量，需要在类加载的过程中被执行初始化；第 8 行的<code>int j</code>则为实例成员变量，只再类被实例化的过程中初始化。</p></li>
<li><p>代码段
  9~11 行为实例化的代码段，在类被实例化的过程中执行；13~15 行为静态的代码段，在类被加载、初始化的过程中执行。</p></li>
<li><p>方法
  方法<code>public static int print(String str)</code> 为静态方法，其实现中牵涉到 k,i,n 三个静态成员变量，实际上，这个方法是专门用来标记执行顺序的方法；T 的构造方法是个实例化方法，在 T 被实例化时调用。</p></li>
<li><p>main 方法
  main 方法中实例化了一个 T 的实例。</p></li>
</ul>


<h3>执行顺序分析</h3>

<p>在一个对象被使用之前，需要经历的过程有：类的装载 -> 链接（验证 -> 准备 -> 解析） -> 初始化 -> 对象实例化。（详情参见<a href="http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loading-linking-and-initialising/">《Java 类的装载、链接和初始化》</a>），这里需要注意的点主要有：</p>

<ul>
<li>在类链接之后，类初始化完成之前，实际上类已经可以被实例化了。</li>
</ul>


<p>就如此题代码中所述，在众多静态成员变量被初始化完成之前，已经有两个实例的初始化了。实际上，此时对类的实例化，除了无法正常使用类的静态成员变量以外（还没有保证完全被初始化），JVM 中已经加载了类的内存结构布局，只是没有执行初始化的过程。比如第 3 行<code>public static T t1 = new T("t1");</code>，在链接过程中，JVM 中已经存在了一个 t1，它的值为 null，还没有执行<code>new T("t1")</code>。又比如第 5 行的<code>public static int i = print("i");</code>，在没有执行初始化时，i 的值为 0，同理 n 在初始化前值也为 0.</p>

<ul>
<li>类实例化的过程中，先执行父类的构造器，然后执行隐式的构造代码，再执行构造方法中的代码。</li>
</ul>


<p>实际上，在编译 Java 代码到字节码的过程中，编译器已经将源码中实例化相关的代码集中到了构造方法中。</p>

<p>这里隐式的构造代码包括了<code>{}</code>代码块中的代码，以及实例成员变量声明中的初始化代码。它们的执行顺序以源代码中代码出现的顺序为准。为何不是先执行显示的构造方法中的代码，再执行隐式的代码呢？这也很容易解释：构造方法中可能就需要使用到实例成员变量，而这时候，我们是期待实例变量能正常使用的。</p>

<p>有了如上的分析，也就能推到出最终的输出结果了。实际上，这几个原则都不需要死记硬背，完全能通过理解整个 JVM 的执行过程来梳理出思路的。</p>

<h2>答案</h2>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="mi">1</span><span class="o">:</span><span class="n">j</span>   <span class="n">i</span><span class="o">=</span><span class="mi">0</span>   <span class="n">n</span><span class="o">=</span><span class="mi">0</span>
</span><span class='line'><span class="mi">2</span><span class="o">:</span><span class="err">构造快</span>   <span class="n">i</span><span class="o">=</span><span class="mi">1</span>   <span class="n">n</span><span class="o">=</span><span class="mi">1</span>
</span><span class='line'><span class="mi">3</span><span class="o">:</span><span class="n">t1</span>    <span class="n">i</span><span class="o">=</span><span class="mi">2</span>  <span class="n">n</span><span class="o">=</span><span class="mi">2</span>
</span><span class='line'><span class="mi">4</span><span class="o">:</span><span class="n">j</span>   <span class="n">i</span><span class="o">=</span><span class="mi">3</span>   <span class="n">n</span><span class="o">=</span><span class="mi">3</span>
</span><span class='line'><span class="mi">5</span><span class="o">:</span><span class="err">构造快</span>   <span class="n">i</span><span class="o">=</span><span class="mi">4</span>   <span class="n">n</span><span class="o">=</span><span class="mi">4</span>
</span><span class='line'><span class="mi">6</span><span class="o">:</span><span class="n">t2</span>    <span class="n">i</span><span class="o">=</span><span class="mi">5</span>  <span class="n">n</span><span class="o">=</span><span class="mi">5</span>
</span><span class='line'><span class="mi">7</span><span class="o">:</span><span class="n">i</span>   <span class="n">i</span><span class="o">=</span><span class="mi">6</span>   <span class="n">n</span><span class="o">=</span><span class="mi">6</span>
</span><span class='line'><span class="mi">8</span><span class="o">:</span><span class="err">静态块</span>   <span class="n">i</span><span class="o">=</span><span class="mi">7</span>   <span class="n">n</span><span class="o">=</span><span class="mi">99</span>
</span><span class='line'><span class="mi">9</span><span class="o">:</span><span class="n">j</span>   <span class="n">i</span><span class="o">=</span><span class="mi">8</span>   <span class="n">n</span><span class="o">=</span><span class="mi">100</span>
</span><span class='line'><span class="mi">10</span><span class="o">:</span><span class="err">构造快</span>   <span class="n">i</span><span class="o">=</span><span class="mi">9</span>   <span class="n">n</span><span class="o">=</span><span class="mi">101</span>
</span><span class='line'><span class="mi">11</span><span class="o">:</span><span class="n">init</span>    <span class="n">i</span><span class="o">=</span><span class="mi">10</span>  <span class="n">n</span><span class="o">=</span><span class="mi">102</span>
</span></code></pre></td></tr></table></div></figure>


<h3>参考：</h3>

<ul>
<li><a href="http://biaobiaoqi.github.io/blog/2013/09/08/strange-behavior-using-braces-in-java/">《Java 构造方法中的执行顺序》</a></li>
<li><a href="http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loading-linking-and-initialising/">《Java 类的装载、链接和初始化》</a></li>
</ul>


<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/09/22/java-initialization/'>http://biaobiaoqi.github.io/blog/2013/09/22/java-initialization/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[对Java字符串的探究]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/09/08/string-interning/"/>
    <updated>2013-09-08T13:01:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/09/08/string-interning</id>
    <content type="html"><![CDATA[<h2>问题的出发点</h2>

<p>在网上看到一道题：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">String</span> <span class="n">str</span> <span class="o">=</span> <span class="k">new</span> <span class="n">String</span><span class="o">(</span><span class="s">&quot;abc&quot;</span><span class="o">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>以上代码执行过程中生成了多少个 String 对象？</p>

<p>答案写的是两个。&#8221;abc&#8221;本身是一个，而 new 又生成了一个。</p>

<h2>&#8220;abc&#8221;是什么</h2>

<p>查看这句程序的字节码，如下：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">NEW</span> <span class="n">String</span>
</span><span class='line'>    <span class="n">DUP</span>
</span><span class='line'>    <span class="n">LDC</span> <span class="s">&quot;abc&quot;</span>
</span><span class='line'>    <span class="n">INVOKESPECIAL</span> <span class="n">String</span><span class="o">.&lt;</span><span class="n">init</span><span class="o">&gt;(</span><span class="n">String</span><span class="o">)</span> <span class="o">:</span> <span class="kt">void</span>
</span><span class='line'>    <span class="n">ASTORE</span> <span class="mi">1</span>
</span></code></pre></td></tr></table></div></figure>


<p>指令<code>ldc indexbyte</code>的含义：将两字节的值从 indexbyte 索引的常量池中加载到方法栈上。</p>

<p>指令<code>LDC "abc"</code>说明了&#8221;abc&#8221;并不是直接以对象存在的，而是存在于常量池的索引中。String 的构造函数调用命令实际使用的就是 String 类型作为参数，那么，栈上应该有一个 String 类型的索引。</p>

<p>由此我们得出，在字节码中，ldc 命令在常量池中找到了能索引到“abc”那个 String 对象的索引值。</p>

<h2>常量池</h2>

<p>常量池是类文件（.class）文件中的一部分，记录了许多常量信息，索引的字符串信息。</p>

<p>由于 Java 是动态加载的，类文件并没有包含程序运行时的内存布局，方法调用等无法直接记录出方法的物理位置，常量池通过索引的方法解决了这个问题。</p>

<!--more-->


<p>常量池中存着许多表，其中 Constant_Utf8_info 表中，记录着会被初始化为 String 对象的字符串的字面值（iteral）。
而在 String 的 java doc 中，有对 String 字面值的说明：</p>

<blockquote><p>All string literals in Java programs, such as &#8220;abc&#8221;, are implemented as instances of this class.</p></blockquote>

<p>在 Java 编译的过程中，确定下来的 String 字面值都先被优化记录在常量池中（那些双引号字符串，都是以 CONSTANT_utf8_info 的形式存储在常量池中的）。也就是说，Java 源代码文件中出现的那些诸如&#8221;abc&#8221;字符串，都已经被提前放在了常量池中。</p>

<p>可以使用如下代码验证这一点：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Program</span>
</span><span class='line'><span class="o">{</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span>
</span><span class='line'>    <span class="o">{</span>
</span><span class='line'>       <span class="n">String</span> <span class="n">str1</span> <span class="o">=</span> <span class="s">&quot;Hello&quot;</span><span class="o">;</span>
</span><span class='line'>       <span class="n">String</span> <span class="n">str2</span> <span class="o">=</span> <span class="s">&quot;Hello&quot;</span><span class="o">;</span>
</span><span class='line'>       <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">print</span><span class="o">(</span><span class="n">str1</span> <span class="o">==</span> <span class="n">str2</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>输出结果是 true.说明&#8221;Hello&#8221;作为对象是被程序从同一个内存空间读取出来的。</p>

<p>常量池是编译时产生的，存在于类文件中（*.class 文件）。运行时，JVM 中每个对象都拥有自己的运行时常量池（run time constant pool）。</p>

<h2>字符串池</h2>

<p>我在 JDK 6.0 源码的 String 类中，发现了一个有趣的 method：intern() ,我翻译如下：</p>

<blockquote><p>当 intern 方法被调用，如果池中已经拥有一个与该 String 的字符串值相等（即 equals()调用后为 true）的 String 对象时，那么池中的那个 String 对象会被返回。否则，池中会增加这个对象，并返回当前这个 String 对象。</p></blockquote>

<p>其中有介绍一个字符串池的东西：字符串池（String pool），初始是空的，由类私有的控制。</p>

<p>查看 java.lang.String 的源代码，发现 Intern()方法是一个 native 方法，即本地实现的方法，而不是 Java 方法，这让我们不能直观的看到字符串池的实现细节。不过能够理解字符串池其实是类似于线程池的缓冲器，可以起到节约内存的作用。如下代码可以验证</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kn">package</span> <span class="n">biaobiaoqi</span><span class="o">.</span><span class="na">thinkingInJava</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Test</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">){</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">String</span> <span class="n">strA1</span> <span class="o">=</span> <span class="s">&quot;ab&quot;</span><span class="o">;</span>
</span><span class='line'>        <span class="n">String</span> <span class="n">strA2</span> <span class="o">=</span> <span class="s">&quot;c&quot;</span><span class="o">;</span>
</span><span class='line'>        <span class="n">String</span> <span class="n">strB1</span> <span class="o">=</span> <span class="s">&quot;a&quot;</span><span class="o">;</span>
</span><span class='line'>        <span class="n">String</span> <span class="n">strB2</span> <span class="o">=</span> <span class="s">&quot;bc&quot;</span> <span class="o">;</span>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">((</span><span class="n">strA1</span><span class="o">+</span><span class="n">strA2</span><span class="o">).</span><span class="na">intern</span><span class="o">()</span> <span class="o">==</span> <span class="o">(</span><span class="n">strB1</span> <span class="o">+</span> <span class="n">strB2</span><span class="o">).</span><span class="na">intern</span><span class="o">());</span>
</span><span class='line'>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>输出结果为 true。</p>

<p>现代的 JVM 实现里，考虑到垃圾回收（Garbage Collection）的方便，将内存区域 <a href="http://en.wikipedia.org/wiki/Java_Virtual_Machine#Heap">heap</a> 划分为三部分： young generation 、 tenured generation（old generation）和 permanent generation(也就是方法区），方法区存储着类、静态变量、常量等信息。字符串池是为了解决字符串重复的问题，存在于方法区中。</p>

<p>回过头来看看文章刚开始的那个问题。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">String</span> <span class="n">str</span> <span class="o">=</span> <span class="k">new</span> <span class="n">String</span><span class="o">(</span><span class="s">&quot;abc&quot;</span><span class="o">);</span>
</span></code></pre></td></tr></table></div></figure>


<p>这里确实是有两个 String 对象生成了。<code>new String("xxx")</code> 创建的 String 对象会在堆中生成对象。而如果使用<code>String str = "xxx"</code>则先查看字符串池 是否已经存在，存在则直接返回该 String 对象，否则生成新的 String 对象，并将它加入字符串池中。</p>

<h2>intern()的应用</h2>

<p>在 JDK 源码中查找对 String.intern()的调用，发现诸如 java.lang.Class 中就有方法调用了它：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'> <span class="kd">private</span> <span class="n">Field</span> <span class="nf">searchFields</span><span class="o">(</span><span class="n">Field</span><span class="o">[]</span> <span class="n">fields</span><span class="o">,</span> <span class="n">String</span> <span class="n">name</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">String</span> <span class="n">internedName</span> <span class="o">=</span> <span class="n">name</span><span class="o">.</span><span class="na">intern</span><span class="o">();</span>
</span><span class='line'>        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">fields</span><span class="o">.</span><span class="na">length</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
</span><span class='line'>            <span class="k">if</span> <span class="o">(</span><span class="n">fields</span><span class="o">[</span><span class="n">i</span><span class="o">].</span><span class="na">getName</span><span class="o">()</span> <span class="o">==</span> <span class="n">internedName</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>                <span class="k">return</span> <span class="nf">getReflectionFactory</span><span class="o">().</span><span class="na">copyField</span><span class="o">(</span><span class="n">fields</span><span class="o">[</span><span class="n">i</span><span class="o">]);</span>
</span><span class='line'>            <span class="o">}</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>        <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>这里获得的 internedName</p>

<h2>总结</h2>

<ul>
<li><p>编译 Java 源代码时，源文件中出现的双引号内的字符串都被收纳到常量池中，用 CONSTANT_utf8_info 项存储着。</p></li>
<li><p>JVM 中，相应的类被加载运行后，常量池对应的映射到 JVM 的运行时常量池中。其中每项 CONSTANT_utf8_info（也就试记录那些字符串的）都会在常量引用解析时，自动生成相应的 internal String，记录在字符串池中。</p></li>
<li><p>尽量使用<code>String str = "abc";</code>，而不是<code>String str = new String("abc")；</code>。用 new 的方法肯定会开辟新的堆空间，而前者的方法，则会通过 string interning 优化。</p></li>
<li><p>JDK 的实现也一直在优化，</p></li>
</ul>


<h3>参考资料</h3>

<ul>
<li><a href="http://www.codeinstructions.com/2009/01/busting-javalangstringintern-myths.html">Busting java.lang.String.intern() Myths</a></li>
<li><a href="http://www.xyzws.com/Javafaq/what-is-string-literal-pool/3">What is String literal pool? How to create a String</a></li>
<li><a href="http://stackoverflow.com/questions/4918399/what-type-of-memory-heap-or-stack-string-constant-pool-in-java-gets-stored">What type of memory (Heap or Stack) String constant pool in Java gets stored?</a></li>
</ul>


<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/09/08/string-interning/'>http://biaobiaoqi.github.io/blog/2013/09/08/string-interning/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[AOP实践：java.lang.instrument的使用]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/09/08/custom-premain-method/"/>
    <updated>2013-09-08T13:01:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/09/08/custom-premain-method</id>
    <content type="html"><![CDATA[<h2>背景</h2>

<p>在 <a href="https://github.com/biaobiaoqi/rcjp">rcjp</a> 项目中，需要调用 ASM API（用于字节码处理的开源库）对字节码进行处理，目标是实现对 Java 程序运行时各种对象的动态跟踪，并进一步分析各个对象之间的关系。在此之前，需要考虑如何获取程序运行的入口。</p>

<p>首先，我考虑到了自定义类加载器（详情见参考资料），即在程序的 main 入口处，首先加载自定义的类加载器，然后通过反射技术使用这个类加载器加载并调用测试程序。这个方法缺点是：每次都必须先找到测试程序的入口类，而对于有的封装成 jar 的程序集合，这一点相对比较难控制。</p>

<p>于是，有了这里介绍的方法：通过 <a href="http://docs.oracle.com/javase/6/docs/api/java/lang/instrument/package-summary.html">java.lang.instrument</a> 实现的 java agent 对象操作字节码，是一种 <a href="http://en.wikipedia.org/wiki/Aspect-oriented_programming">AOP</a> 的方法。</p>

<p>程序中，除了 ASMAgent 以外的所有类都是调用 ASM API 实现对测试程序中各个对象的构造、方法调用、属性赋值等操作行为的记录（其中对 Collection 子类的处理着实费了一番心血= =，字节码操作很细节，容易出错）。</p>

<!--more-->


<h2>原理</h2>

<p><a href="http://www.ibm.com/developerworks/cn/java/j-lo-jse61/index.html#N100A7">JVMTI（Java Virtual Machine Tool Interface）</a>是一套本地编程接口集合，它提供了一套『代理』机制，可以支持第三方工具程序以代理的方式连接和访问 JVM，并利用 JVMTI 提供的丰富的编程接口，完成很多跟 JVM 相关的功能。</p>

<p>java.lang.instrument 包的实现，也就是基于这种机制的：在 Instrumentation 的实现当中，存在一个 JVMTI 的代理程序，通过调用 JVMTI 当中 Java 类相关的函数来完成 Java 类的动态操作。</p>

<p>Instrumentation 的最大作用就是类定义的动态改变和操作。在 Java SE 5 及其后续版本当中，开发者可以在一个普通 Java 程序（带有 main 函数的 Java 类）运行时，通过 – javaagent 参数指定一个特定的 jar 文件（包含 Instrumentation 代理）来启动 Instrumentation 的代理程序。</p>

<h2>步骤</h2>

<h3>1.编写 java 代理类</h3>

<p>这个类中，premain 方法是关键，对比于一般的入口 main 一样，这里的 premain 是在 main 之前执行的。它会告诉 JVM 如何处理加载上来的 java 字节码。如下例：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">premain</span><span class="o">(</span><span class="n">String</span> <span class="n">agentArgs</span><span class="o">,</span> <span class="n">Instrumentation</span> <span class="n">inst</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>                    <span class="n">Trace</span><span class="o">.</span><span class="na">BeginTrace</span><span class="o">();</span> <span class="c1">// it&#39;s important for trace files  </span>
</span><span class='line'>            <span class="n">inst</span><span class="o">.</span><span class="na">addTransformer</span><span class="o">(</span><span class="k">new</span> <span class="n">ASMAgent</span><span class="o">());</span>
</span><span class='line'>        <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>值得注意的是，addTransformer 实现了对字节码处理的方法的回调。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">inst</span><span class="o">.</span><span class="na">addTransformer</span><span class="o">(</span><span class="k">new</span> <span class="n">ASMAgent</span><span class="o">());</span>
</span></code></pre></td></tr></table></div></figure>


<p>类 ASMAgent 包含着实现对 java 字节码处理的方法：transform()。它来自于 ClassFileTransformer 接口。为了方便，这里将对 ClassFileTransformer 接口的实现跟 ASMAgent 类放在了一起。其中 classfileBuffer 是类文件加载时的原始的字节码，retVal 则是经过处理后的字节码。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kt">byte</span><span class="o">[]</span> <span class="nf">transform</span><span class="o">(</span><span class="n">ClassLoader</span> <span class="n">loader</span><span class="o">,</span> <span class="n">String</span> <span class="n">className</span><span class="o">,</span><span class="n">Class</span><span class="o">&lt;?&gt;</span> <span class="n">classBeingRedefined</span><span class="o">,</span>
</span><span class='line'>                                        <span class="n">ProtectionDomain</span> <span class="n">protectionDomain</span><span class="o">,</span><span class="kt">byte</span><span class="o">[]</span> <span class="n">classfileBuffer</span><span class="o">)</span>
</span><span class='line'>            <span class="kd">throws</span> <span class="n">IllegalClassFormatException</span> <span class="o">{</span>
</span><span class='line'>            <span class="kt">byte</span><span class="o">[]</span> <span class="n">retVal</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'>            <span class="k">if</span><span class="o">(</span><span class="n">isInstrumentable</span><span class="o">(</span><span class="n">className</span><span class="o">)){</span>
</span><span class='line'>                <span class="n">ClassWriter</span> <span class="n">cw</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ClassWriter</span><span class="o">(</span><span class="n">ClassWriter</span><span class="o">.</span><span class="na">COMPUTE_FRAMES</span><span class="o">);</span>
</span><span class='line'>                <span class="n">ASMClassAdapter</span> <span class="n">mca</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ASMClassAdapter</span><span class="o">(</span><span class="n">cw</span><span class="o">);</span>
</span><span class='line'>                <span class="n">ClassReader</span> <span class="n">cr</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ClassReader</span><span class="o">(</span><span class="n">classfileBuffer</span><span class="o">);</span>
</span><span class='line'>                <span class="n">cr</span><span class="o">.</span><span class="na">accept</span><span class="o">(</span><span class="n">mca</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span>
</span><span class='line'>                <span class="n">retVal</span> <span class="o">=</span> <span class="n">cw</span><span class="o">.</span><span class="na">toByteArray</span><span class="o">();</span>
</span><span class='line'>            <span class="o">}</span><span class="k">else</span><span class="o">{</span>
</span><span class='line'>                    <span class="n">retVal</span> <span class="o">=</span> <span class="n">classfileBuffer</span> <span class="o">;</span>
</span><span class='line'>                <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>            <span class="k">return</span> <span class="n">retVal</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h3>2.打包代理类</h3>

<p>只有合理打包并在 manifest 文件中记录下相应的键值对之后，才能正常执行 premain 的内容。
manifest 文件中需要添加的键值对是：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">Premain</span><span class="o">-</span><span class="n">Class</span> <span class="o">:</span> <span class="n">biaobiaoqi</span><span class="o">.</span><span class="na">asm</span><span class="o">.</span><span class="na">ASMAgent</span>
</span></code></pre></td></tr></table></div></figure>


<p>如果对字节码的处理有应用到了其他的类，需要在 manifest 中增加路径。比如使用到了 asm-3.0.jar，则增加如下语句：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">Class</span><span class="o">-</span><span class="nl">Path:</span> <span class="n">asm</span><span class="o">-</span><span class="mf">3.0</span><span class="o">.</span><span class="na">jar</span>
</span></code></pre></td></tr></table></div></figure>


<h3>3.执行</h3>

<p>执行测试程序时，添加“-javaagent:代理类的 jar[=传入 premain 的参数]”选项。</p>

<p>比如，对于博主的程序，就是</p>

<p><code>java -javaagent:ASMInstrument.jar   -jar XXXX.jar  xxxx</code></p>

<p>其中 ASMInstrument.jar 是第二步中打包的程序，  XXX.jar 是需要测试的程序， xxx 是 XXX.jar 执行时可能的命令行参数。</p>

<p>如果只是执行某.class 文件中的类，我们假设是在当前目录下的一个 XXXX 类，则是：
<code>java -javaagent:ASMInstrument.jar   -cp ./  XXXX xxx</code></p>

<p>其中 xxx 是可能的命令行参数。</p>

<h3>参考资料</h3>

<ul>
<li><a href="http://www.ibm.com/developerworks/cn/java/j-lo-jse61/index.html">《Java SE 6 新特性: Instrumentation 新功能》</a></li>
<li><a href="http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loader-in-practice/">《Java 类加载器编程实践》</a></li>
</ul>


<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/09/08/custom-premain-method/'>http://biaobiaoqi.github.io/blog/2013/09/08/custom-premain-method/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Java构造方法中的执行顺序]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/09/08/strange-behavior-using-braces-in-java/"/>
    <updated>2013-09-08T12:58:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/09/08/strange-behavior-using-braces-in-java</id>
    <content type="html"><![CDATA[<p>这道题来自 <a href="http://stackoverflow.com/questions/8185780/strange-behavior-using-braces-in-java/8186881#8186881">stackoverflow</a>。</p>

<h2>问题描述：</h2>

<p>有如下代码，求其输出内容。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Test</span>
</span><span class='line'><span class="o">{</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kt">int</span> <span class="n">a</span> <span class="o">=</span> <span class="mi">10</span><span class="o">;</span>
</span><span class='line'>    <span class="n">Test</span><span class="o">(){</span><span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;1&quot;</span><span class="o">);}</span>
</span><span class='line'>
</span><span class='line'>    <span class="o">{</span><span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;2&quot;</span><span class="o">);}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">static</span><span class="o">{</span><span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;3&quot;</span><span class="o">);}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span> <span class="n">args</span><span class="o">[])</span>
</span><span class='line'>    <span class="o">{</span>
</span><span class='line'>        <span class="k">new</span> <span class="nf">Test</span><span class="o">();</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h2>分析</h2>

<p>作为静态区段的语句，容易知道，3 是会最先出现的。容易弄错的一点是 1 和 2 的出现顺序。</p>

<p>我们可以参考字节码来分析。在 eclipse 中使用 ASM bytecode 插件，得到相应的字节码：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="c1">// class version 50.0 (50)  </span>
</span><span class='line'><span class="c1">// access flags 0x21  </span>
</span><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Test</span> <span class="o">{</span>
</span><span class='line'>  <span class="c1">// compiled from: Test.java  </span>
</span><span class='line'>  <span class="kd">static</span> <span class="o">&lt;</span><span class="n">clinit</span><span class="o">&gt;()</span> <span class="o">:</span> <span class="kt">void</span>
</span><span class='line'>    <span class="n">GETSTATIC</span> <span class="n">System</span><span class="o">.</span><span class="na">out</span> <span class="o">:</span> <span class="n">PrintStream</span>
</span><span class='line'>    <span class="n">LDC</span> <span class="s">&quot;3&quot;</span>
</span><span class='line'>    <span class="n">INVOKEVIRTUAL</span> <span class="n">PrintStream</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">String</span><span class="o">)</span> <span class="o">:</span> <span class="kt">void</span>
</span><span class='line'>    <span class="n">RETURN</span>
</span><span class='line'>
</span><span class='line'>  <span class="o">&lt;</span><span class="n">init</span><span class="o">&gt;()</span> <span class="o">:</span> <span class="kt">void</span>
</span><span class='line'>    <span class="n">ALOAD</span> <span class="mi">0</span><span class="o">:</span> <span class="k">this</span>
</span><span class='line'>    <span class="n">INVOKESPECIAL</span> <span class="n">Object</span><span class="o">.&lt;</span><span class="n">init</span><span class="o">&gt;()</span> <span class="o">:</span> <span class="kt">void</span>
</span><span class='line'>    <span class="n">ALOAD</span> <span class="mi">0</span><span class="o">:</span> <span class="k">this</span>
</span><span class='line'>    <span class="n">BIPUSH</span> <span class="mi">10</span>
</span><span class='line'>    <span class="n">PUTFIELD</span> <span class="n">Test</span><span class="o">.</span><span class="na">a</span> <span class="o">:</span> <span class="kt">int</span>
</span><span class='line'>    <span class="n">GETSTATIC</span> <span class="n">System</span><span class="o">.</span><span class="na">out</span> <span class="o">:</span> <span class="n">PrintStream</span>
</span><span class='line'>    <span class="n">LDC</span> <span class="s">&quot;2&quot;</span>
</span><span class='line'>    <span class="n">INVOKEVIRTUAL</span> <span class="n">PrintStream</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">String</span><span class="o">)</span> <span class="o">:</span> <span class="kt">void</span>
</span><span class='line'>    <span class="n">GETSTATIC</span> <span class="n">System</span><span class="o">.</span><span class="na">out</span> <span class="o">:</span> <span class="n">PrintStream</span>
</span><span class='line'>    <span class="n">LDC</span> <span class="s">&quot;1&quot;</span>
</span><span class='line'>    <span class="n">INVOKEVIRTUAL</span> <span class="n">PrintStream</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">String</span><span class="o">)</span> <span class="o">:</span> <span class="kt">void</span>
</span><span class='line'>    <span class="n">RETURN</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>  <span class="kd">public</span> <span class="kd">static</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[])</span> <span class="o">:</span> <span class="kt">void</span>
</span><span class='line'>    <span class="n">NEW</span> <span class="n">Test</span>
</span><span class='line'>    <span class="n">INVOKESPECIAL</span> <span class="n">Test</span><span class="o">.&lt;</span><span class="n">init</span><span class="o">&gt;()</span> <span class="o">:</span> <span class="kt">void</span>
</span><span class='line'>    <span class="n">RETURN</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>正如我们所想，3 是被放在类构造方法中，这是类的初始化函数，固然在类的初始化时出现。</p>

<!--more-->


<p>而在构造方法中先出现 2，之后才是 1。问题的核心集中到对象构造方法的指令顺序问题。实际上，在对象构造方法中，会先执行一些隐性的指令，比如父类的构造方法、{}区段的内容等，然后再执行显性的构造方法中的指令：</p>

<ol>
<li>Java 编译时，对象构造方法里先嵌入隐式的指令，完毕之后，再执行 Java 源代码中显示的代码。</li>
<li>那些隐式的指令，包括父类的构造方法、变量的初始化、{}区段里的内容，并严格按照这个顺序嵌入到对象的构造方法中。</li>
<li>{}区段里的内容和变量的初始化语句的执行顺序，依据源代码中本身的顺序执行。</li>
</ol>


<p>相关文章参见：<a href="http://biaobiaoqi.github.io/blog/2013/09/22/java-initialization/">《Java 类、实例的初始化顺序》</a></p>

<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/09/08/strange-behavior-using-braces-in-java/'>http://biaobiaoqi.github.io/blog/2013/09/08/strange-behavior-using-braces-in-java/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Java类的实例化总结]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/09/08/initliate-objects-in-java/"/>
    <updated>2013-09-08T02:03:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/09/08/initliate-objects-in-java</id>
    <content type="html"><![CDATA[<p>java 类的实例化(instantiation)具有显性的和隐性的区别。</p>

<p>写 Java 代码时，我们所使用 new 的方法实例化最简单直接的显性实例化。而隐性的实例化则出现在 java 程序的整个生命周期中，包括 String、Class，StringBuffer 或者 StringBuilder 的实例化等等。</p>

<h2>显性的实例化</h2>

<h3>new 关键字实例化对象</h3>

<p>调用相应的构造函数完成实例化。（类中的非静态成员变量如果有初始化语句，都会被隐式的加入到构造函数中）代码如下：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Test</span>  <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">String</span> <span class="n">strA</span> <span class="o">=</span> <span class="s">&quot;xyz&quot;</span><span class="o">;</span>
</span><span class='line'>    <span class="n">String</span> <span class="n">strB</span> <span class="o">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">public</span> <span class="nf">Test</span><span class="o">(</span><span class="n">String</span> <span class="n">str</span><span class="o">){</span>
</span><span class='line'>        <span class="n">strB</span> <span class="o">=</span> <span class="n">str</span> <span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">){</span>
</span><span class='line'>            <span class="n">Test</span> <span class="n">t</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Test</span><span class="o">(</span><span class="s">&quot;abc&quot;</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>在 eclipse 中装了 <a href="http://asm.ow2.org/">ASM bytecode</a> 插件后，观察.class 文件中的构造函数对应的字节码如下：</p>

<!--more-->


<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">INVOKESPECIAL</span> <span class="n">Object</span><span class="o">.&lt;</span><span class="n">init</span><span class="o">&gt;()</span> <span class="o">:</span> <span class="kt">void</span>
</span><span class='line'>   <span class="n">ALOAD</span> <span class="mi">0</span><span class="o">:</span> <span class="k">this</span>
</span><span class='line'>   <span class="n">LDC</span> <span class="s">&quot;xyz&quot;</span>
</span><span class='line'>   <span class="n">PUTFIELD</span> <span class="n">Test</span><span class="o">.</span><span class="na">strA</span> <span class="o">:</span> <span class="n">String</span>
</span><span class='line'>   <span class="n">ALOAD</span> <span class="mi">0</span><span class="o">:</span> <span class="k">this</span>
</span><span class='line'>   <span class="n">ALOAD</span> <span class="mi">1</span><span class="o">:</span> <span class="n">str</span>
</span><span class='line'>   <span class="n">PUTFIELD</span> <span class="n">Test</span><span class="o">.</span><span class="na">strB</span> <span class="o">:</span> <span class="n">String</span>
</span><span class='line'>   <span class="n">RETURN</span>
</span></code></pre></td></tr></table></div></figure>


<p>关键在于<code>LDC"xyz"</code>这条指令，明显可以看出，这是用于 strA 初始化的字符串。</p>

<p>由此我们可以归纳出，在没有调用本类中其他的构造函数的情况下，每次类的构造函数中都会按如下顺序进行：</p>

<ul>
<li>a)隐式（或显性）的调用父类的构造函数</li>
<li>b)然后执行写在构造函数外的成员变量的初始化赋值</li>
<li>c)最后再执行构造函数中的命令。</li>
</ul>


<p>如果是有显性的调用本类其他构造函数（必须是放在构造函数第一步执行），那么对于这个构造函数，处理过程就简单些了：</p>

<ul>
<li>a)调用那个构造函数。</li>
<li>b)执行之后的代码。</li>
</ul>


<h3>利用 java 反射机制</h3>

<p>反射机制是是 java 动态性中的关键之一，调用 java.lang.reflect.Constructor 的 newInstance()方法能创建对象。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Test</span>  <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">public</span> <span class="nf">Test</span><span class="o">(){</span>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;Created by invoking newInstance()&quot;</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">public</span> <span class="nf">Test</span><span class="o">(</span><span class="n">String</span> <span class="n">str</span><span class="o">){</span>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">str</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span>
</span><span class='line'>                    <span class="kd">throws</span> <span class="n">ClassNotFoundException</span> <span class="o">,</span> <span class="n">InstantiationException</span> <span class="o">,</span>
</span><span class='line'>                    <span class="n">IllegalAccessException</span>  <span class="o">{</span>
</span><span class='line'>            <span class="n">Test</span> <span class="n">t1</span> <span class="o">=</span> <span class="k">new</span> <span class="n">Test</span><span class="o">(</span><span class="s">&quot;Created with new&quot;</span><span class="o">);</span> <span class="c1">//常规的方法  </span>
</span><span class='line'>            <span class="n">Class</span> <span class="n">myClass</span> <span class="o">=</span> <span class="n">Class</span><span class="o">.</span><span class="na">forName</span><span class="o">(</span><span class="s">&quot;Test&quot;</span><span class="o">);</span>  <span class="c1">//获得了对应于 Test 类的 Class 对象，如果没有加载，会先加载这个类，再返回。  </span>
</span><span class='line'>            <span class="n">Test</span> <span class="n">t2</span> <span class="o">=</span> <span class="o">(</span><span class="n">Test</span><span class="o">)</span><span class="n">myClass</span><span class="o">.</span><span class="na">newInstance</span><span class="o">();</span> <span class="c1">//调用 newInstance()创建对象。  </span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<h3>其他</h3>

<p>其他还有对象的 clone()方法，以及串行化后的解串行化过程。</p>

<h2>隐性的实例化</h2>

<p>隐性的实例化主要有如下几类：</p>

<ul>
<li>1.String 和 String 数组。main(String[] args)中拥有的 args 参数为 String 数组类型，这些 command line 参数将会首先被实例化。</li>
<li>2.Class 的实例化。由于类的加载过程中，会生成相应类的 Class 对象，这些也会被隐性的实例化。</li>
<li>3.JVM 在执行类加载的过程中，对常量池中的 CONSTANT_String_info 项会实例化出对应的 String 对象。这里涉及到常量池解析的知识。</li>
<li>4.在 String 的操作中，可能存在隐性的 StringBuffer 或者 StringBuilder 的实例化。</li>
<li>5.int 和 Integer 这些类型转化过程中的装箱、拆箱。</li>
</ul>


<p>比如如下代码：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Test</span>  <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">){</span>
</span><span class='line'>            <span class="n">String</span> <span class="n">str1</span> <span class="o">=</span> <span class="s">&quot;abc&quot;</span><span class="o">;</span>
</span><span class='line'>            <span class="n">String</span> <span class="n">str2</span> <span class="o">=</span> <span class="s">&quot;def&quot;</span><span class="o">;</span>
</span><span class='line'>            <span class="n">String</span> <span class="n">str</span> <span class="o">=</span> <span class="n">str1</span> <span class="o">+</span> <span class="n">str2</span> <span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>在 eclipse 中装了 ASM bytecode 插件后，直接观察.class 文件对应的字节码：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">NEW</span> <span class="n">StringBuilder</span>
</span><span class='line'>    <span class="n">DUP</span>
</span><span class='line'>    <span class="n">ALOAD</span> <span class="mi">1</span><span class="o">:</span> <span class="n">str1</span>
</span><span class='line'>    <span class="n">INVOKESTATIC</span> <span class="n">String</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">Object</span><span class="o">)</span> <span class="o">:</span> <span class="n">String</span>
</span><span class='line'>    <span class="n">INVOKESPECIAL</span> <span class="n">StringBuilder</span><span class="o">.&lt;</span><span class="n">init</span><span class="o">&gt;(</span><span class="n">String</span><span class="o">)</span> <span class="o">:</span> <span class="kt">void</span>
</span><span class='line'>    <span class="n">ALOAD</span> <span class="mi">2</span><span class="o">:</span> <span class="n">str2</span>
</span><span class='line'>    <span class="n">INVOKEVIRTUAL</span> <span class="n">StringBuilder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">String</span><span class="o">)</span> <span class="o">:</span> <span class="n">StringBuilder</span>
</span><span class='line'>    <span class="n">INVOKEVIRTUAL</span> <span class="n">StringBuilder</span><span class="o">.</span><span class="na">toString</span><span class="o">()</span> <span class="o">:</span> <span class="n">String</span>
</span><span class='line'>    <span class="n">ASTORE</span> <span class="mi">3</span>
</span></code></pre></td></tr></table></div></figure>


<p>实际上，这里 str1 和 str2 合并的过程，是使用了 StringBuilder 来间接完成的，首先以 str1 的值构造一个 StringBuilder，然后调用其中的 append()方法，将 str2 串联上来。</p>

<p>值得注意的是：老版本的 java 使用 StringBuffer 完成这一步，但 StringBuffer 是线程安全的，效率略低，于是在新版本 java 中出现了非线程安全的 StringBuilder，这类似于 Hashtable 和 hashset 的关系。</p>

<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/09/08/initliate-objects-in-java/'>http://biaobiaoqi.github.io/blog/2013/09/08/initliate-objects-in-java/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Java类加载的延迟初始化]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/09/08/late-initialising-in-java/"/>
    <updated>2013-09-08T02:00:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/09/08/late-initialising-in-java</id>
    <content type="html"><![CDATA[<p><a href="http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loading-linking-and-initialising/">《Java 类的装载、链接和初始化》</a>中提到，链接的最后一步是解析，即对符号引用的解析。但这不是必须的，可以等到相应的符号引用第一次使用时再解析。</p>

<p>而类的初始化是在链接之后的（注意了，根据不同 JVM 有不同的实现方式，在类初始化的时候，可能已经完成了所有的符号引用的解析，也可能没有），本文所写的就是类的初始化的时机问题。</p>

<p>Java 类的动态加载机制规定，在类被主动使用(active use)之前，必须已经完成类的初始化。既然有主动调用，那么就有被动调用了。这两者有哪些区别呢？</p>

<p>下面列出所有主动使用的情况，用以区分两者：</p>

<ul>
<li>1.创造该类的一个新的实例</li>
<li>2.调用这个类中的静态方法</li>
<li>3.获取类或者接口中的非常量的静态变量</li>
<li>4.利用反射调用方法</li>
<li>5.初始化该类的某子类</li>
<li>6.被制定为 JVM 开始运行时必须初始化的类</li>
</ul>


<p>注意，3 中为何是“非常量的静态变量”。如果是常量，即声明为 final 的话，并不会出现对类的构造，虽然调用时有类名出现，但实际调用会直接使用常量，绕过了类的限制（详情见相关常量池和运行时常量池的知识）。</p>

<p>只有当一个非常量的静态变量被显示的在类或接口中声明时，它的调用才属于主动调用。对于父类中某非常量静态变量的调用属于被动使用(positive use)。</p>

<!--more-->


<p>如下代码</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Parent</span> <span class="o">{</span>
</span><span class='line'>     <span class="kd">static</span> <span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">10</span> <span class="o">;</span>
</span><span class='line'>
</span><span class='line'>     <span class="kd">static</span><span class="o">{</span>
</span><span class='line'>            <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;Parent initiate&quot;</span><span class="o">);</span>
</span><span class='line'>     <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>     <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">func</span><span class="o">(){</span>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;func&quot;</span><span class="o">);</span>
</span><span class='line'>     <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Son</span> <span class="kd">extends</span> <span class="n">Parent</span><span class="o">{</span>
</span><span class='line'>    <span class="kd">static</span><span class="o">{</span>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;Son initiate&quot;</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Test</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">static</span><span class="o">{</span>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;Test initiate&quot;</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">){</span>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">Son</span><span class="o">.</span><span class="na">i</span><span class="o">);</span>
</span><span class='line'>        <span class="n">Son</span><span class="o">.</span><span class="na">func</span><span class="o">();</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>运行的结果是：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">Test</span> <span class="n">initiate</span>
</span><span class='line'><span class="n">Parent</span> <span class="n">initiate</span>
</span><span class='line'><span class="mi">10</span>
</span><span class='line'><span class="n">func</span>
</span></code></pre></td></tr></table></div></figure>


<p>虽然有出现 Son，但 Son.i 访问的是父类的非常量静态变量。于是没有对 Son 类进行初始化，而只是初始化了明确的声明静态变量的 Parent 类。</p>

<p>由此可见，一般的，我们在某个类中定义了其他类的成员变量引用，只要该变量没有 new 出一个新的对象，则 JVM 也不会初始化这个类，类此时只是被加载了而已，而没有链接和初始化。</p>

<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/09/08/late-initialising-in-java/'>http://biaobiaoqi.github.io/blog/2013/09/08/late-initialising-in-java/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Java类的装载、链接和初始化]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loading-linking-and-initialising/"/>
    <updated>2013-09-08T00:33:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loading-linking-and-initialising</id>
    <content type="html"><![CDATA[<h2>加载(Loading)</h2>

<p>按如下三步执行</p>

<ul>
<li>1.通过类的全名产生对应类的二进制数据流。（注意，如果没找到对应类文件，只有在类实际使用时才抛出错误。）</li>
<li>2.分析并将这些二进制数据流转换为方法区(JVM 的架构：方法区、堆，栈，本地方法栈，pc 寄存器)特定的数据结构（这些数据结构是实现有关的，不同 JVM 有不同实现）。这里处理了部分检验，比如类文件的魔数的验证，检查文件是否过长或者过短，确定是否有父类（除了 Obecjt 类）。</li>
<li>3.创建对应类的 java.lang.Class 实例（注意，有了对应的 Class 实例，并不意味着这个类已经完成了加载链链接！）。</li>
</ul>


<h2>链接(Linking)</h2>

<p>链接的过程比加载过成复杂不少，这是实现 Java 的动态性的重要一步。分为三部分：验证，准备和解析。</p>

<ul>
<li><p>1.验证（verification）</p>

<p>  链接的第三部解析会把类中成员方法、成员变量、类和接口的符号引用替换为直接引用，而在这之前，需要检测被引用的类型正确性和接入属性是否正确（就是 public ,private 的的问题），诸如检查 final class 又没有被继承，检查静态变量的正确性等等。（注意到实际上有一部分验证过程已经在加载的过程中执行了。）</p></li>
</ul>


<!--more-->


<ul>
<li><p>2.准备(preparation)</p>

<p>  对类的成员变量分配空间。虽然有初始值，但这个时候不会对他们进行初始化（因为这里不会执行任何 Java 代码）。具体如下：</p>

<p>  所有原始类型的值都为 0。如 float: 0f, int: 0, boolean: 0(注意 boolean 底层实现大多使用 int)，引用类型则为 null。值得注意的是，JVM 可能会在这个时期给一些有助于程序运行效率提高的数据结构分配空间。比如方发表（类似与 C++中的虚函数表，参见另一篇博文<a href="http://biaobiaoqi.github.io/blog/2013/06/02/virtual-dispatch-and-method-table-in-java/">《Java：方法的虚分派和方法表》</a>）。</p></li>
<li><p>3.解析(Resolution)</p>

<p>  为类、接口、方法、成员变量的符号引用定位直接引用（如果符号引用先到常量池中寻找符号，再找先应的类型，无疑会耗费更多时间），完成内存结构的布局。</p>

<p>  这一步是可选的。可以在符号引用第一次被使用时完成，即所谓的延迟解析(late resolution)。但对用户而言，这一步永远是延迟解析的，即使运行时会执行 early resolution，但程序不会显示的在第一次判断出错误时抛出错误，而会在对应的类第一次主动使用的时候抛出错误！</p>

<p>  另外，这一步与之后的类初始化是不冲突的，并非一定要所有的解析结束以后才执行类的初始化。不同的 JVM 实现不同。详情见另一篇博文<a href="http://biaobiaoqi.github.io/blog/2013/09/08/late-initialising-in-java/">《Java 类加载的延迟初始化》</a>。</p></li>
</ul>


<h2>初始化类(Initialization)</h2>

<p>开发 Java 时，接触最多的是对象的初始化。实际上类也是有初始化的。相比对象初始化（参见博文 <a href="http://biaobiaoqi.github.io/blog/2013/09/08/initliate-objects-in-java/">Java 类的实例化</a>），类的初始化机制要简单不少。</p>

<p>类的初始化也是延迟的，直到类第一次被主动使用(active use)，JVM 才会初始化类。（参见博文<a href="http://biaobiaoqi.github.io/blog/2013/09/08/late-initialising-in-java/">《Java 类加载的延迟初始化》</a>）</p>

<p>类的初始化分两步：</p>

<ul>
<li>1.如果基类没有被初始化，初始化基类。</li>
<li><p>2.有类构造函数，则执行类构造函数。</p>

<p>  类构造函数是由 Java 编译器完成的。它把类成员变量的初始化和 static 区间的代码提取出，放到一个<clinit>的方法中。这个方法不能被一般的方法访问（注意，static final 成员变量不会在此执行初始化，它一般被编译器生成 constant 值）。同时，<clinit>中是不会显示的调用基类的<clinit>的，因为 1 中已经执行了基类的初始化。类的初始化还必须注意线程安全的问题。</p></li>
</ul>


<h3>PS：</h3>

<p>为什么接口不能定义成员变量，而只能定义 final static 变量。</p>

<ul>
<li>1.接口是不可实例化，它的所有元素都不必是实例（对象）层面的。static 满足了这一点。</li>
<li>2.如果接口的变量能被修改，那么一旦一个子类实现了这个接口，并修改了接口中的非 final 变量，而该子类的子类再次修改这个非 final 的变量后，造成的结果就是虽然实现了相同的接口，但接口中的变量值是不一样的。</li>
</ul>


<p>综上述，static final 更适合于接口。</p>

<p>参考：<a href="http://blog.csdn.net/a352193394/article/details/6844941">《通过类字面常量解释接口常量为什么只能定义为 static final,类加载过程&#8212;Thinking in java》</a></p>

<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loading-linking-and-initialising/'>http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loading-linking-and-initialising/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Java类加载器编程实践]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loader-in-practice/"/>
    <updated>2013-09-08T00:33:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loader-in-practice</id>
    <content type="html"><![CDATA[<p>文本通过实现自定义类加载器，实践 Java 类加载的流程。</p>

<p>阅读此文前，需要了解 Java 类加载的基本原理，参见如下两篇博文：</p>

<ul>
<li><a href="http://biaobiaoqi.github.io/blog/2013/09/07/java-classloader/">《Java 类加载器浅析》</a></li>
<li><a href="http://biaobiaoqi.github.io/blog/2013/09/08/three-principles-of-classloader-operation/">《Java 类加载器三原则》</a></li>
</ul>


<p>以上博文中所提及的 Java 类加载机制，都是 Java1.2 及以后的版本，而在最早的 Java1.1 中类加载器是没有父子关系的模式的。这里将分别对 Java1.1 和 Java1.2 及以后的类加载版本进行展示。</p>

<h2>Java1.1 中的实现</h2>

<h3>原理介绍</h3>

<p>Java1.1 的类加载机制相对单一，用户自定义加载器的重写比较复杂。</p>

<p>主要需要重写加载器中的 Class loadClass(String name)方法。</p>

<p>Class loadClass(String name)或 loadClass(String name , boolean resolve)方法是加载的核心。它根据类的全名（比如 String 类的全名是 java.lang.String）获得对应类的二进制数据，然后通过 Class defineClass(byte[] b) 将二进制数据加载到 JVM 的方法区，并返回对应类的 Class 实例，然后根据可选的参数 resolve 决定是否需要现在解析这个类。最后将这个 Class 实例作为 loadClass 方法的返回值。</p>

<p>如果无法加载和 defineClass，即无法通过本加载器直接加载类的情况，则使用 Class findSystemClass(String name) 将类加载任务委派给系统类加载器查找。如果能找到则加载，否则抛出 ClassNotFoundException 异常。</p>

<h3>编程实例</h3>

<p>以下用实例来展示这一过程：</p>

<!--more-->


<p>类 CompilingClassLoader 是一个自定义加载器，它能直接读取 Java 源文件实现类加载。CLL 类的 main 方法为程序入口，通过 ComplilingClassLoader 加载一个 Foo 类，使用反射机制调用 Foo 类的 main 方法。</p>

<p>CompilingClassLoader.java</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
<span class='line-number'>94</span>
<span class='line-number'>95</span>
<span class='line-number'>96</span>
<span class='line-number'>97</span>
<span class='line-number'>98</span>
<span class='line-number'>99</span>
<span class='line-number'>100</span>
<span class='line-number'>101</span>
<span class='line-number'>102</span>
<span class='line-number'>103</span>
<span class='line-number'>104</span>
<span class='line-number'>105</span>
<span class='line-number'>106</span>
<span class='line-number'>107</span>
<span class='line-number'>108</span>
<span class='line-number'>109</span>
<span class='line-number'>110</span>
<span class='line-number'>111</span>
<span class='line-number'>112</span>
<span class='line-number'>113</span>
<span class='line-number'>114</span>
<span class='line-number'>115</span>
<span class='line-number'>116</span>
<span class='line-number'>117</span>
<span class='line-number'>118</span>
<span class='line-number'>119</span>
<span class='line-number'>120</span>
<span class='line-number'>121</span>
<span class='line-number'>122</span>
<span class='line-number'>123</span>
<span class='line-number'>124</span>
<span class='line-number'>125</span>
<span class='line-number'>126</span>
<span class='line-number'>127</span>
<span class='line-number'>128</span>
<span class='line-number'>129</span>
<span class='line-number'>130</span>
<span class='line-number'>131</span>
<span class='line-number'>132</span>
<span class='line-number'>133</span>
<span class='line-number'>134</span>
<span class='line-number'>135</span>
<span class='line-number'>136</span>
<span class='line-number'>137</span>
<span class='line-number'>138</span>
<span class='line-number'>139</span>
<span class='line-number'>140</span>
<span class='line-number'>141</span>
<span class='line-number'>142</span>
<span class='line-number'>143</span>
<span class='line-number'>144</span>
<span class='line-number'>145</span>
<span class='line-number'>146</span>
<span class='line-number'>147</span>
<span class='line-number'>148</span>
<span class='line-number'>149</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kn">import</span> <span class="nn">java.io.*</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'><span class="cm">/* </span>
</span><span class='line'><span class="cm"> </span>
</span><span class='line'><span class="cm">A CompilingClassLoader compiles your Java source on-the-fly.  It </span>
</span><span class='line'><span class="cm">checks for nonexistent .class files, or .class files that are older </span>
</span><span class='line'><span class="cm">than their corresponding source code. </span>
</span><span class='line'><span class="cm"> </span>
</span><span class='line'><span class="cm">*/</span>
</span><span class='line'>
</span><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CompilingClassLoader</span> <span class="kd">extends</span> <span class="n">ClassLoader</span>
</span><span class='line'><span class="o">{</span>
</span><span class='line'>  <span class="c1">// Given a filename, read the entirety of that file from disk  </span>
</span><span class='line'>  <span class="c1">// and return it as a byte array.  </span>
</span><span class='line'>  <span class="kd">private</span> <span class="kt">byte</span><span class="o">[]</span> <span class="nf">getBytes</span><span class="o">(</span> <span class="n">String</span> <span class="n">filename</span> <span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span><span class='line'>    <span class="c1">// Find out the length of the file  </span>
</span><span class='line'>    <span class="n">File</span> <span class="n">file</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span> <span class="n">filename</span> <span class="o">);</span>
</span><span class='line'>    <span class="kt">long</span> <span class="n">len</span> <span class="o">=</span> <span class="n">file</span><span class="o">.</span><span class="na">length</span><span class="o">();</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Create an array that&#39;s just the right size for the file&#39;s  </span>
</span><span class='line'>    <span class="c1">// contents  </span>
</span><span class='line'>    <span class="kt">byte</span> <span class="n">raw</span><span class="o">[]</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="o">[(</span><span class="kt">int</span><span class="o">)</span><span class="n">len</span><span class="o">];</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Open the file  </span>
</span><span class='line'>    <span class="n">FileInputStream</span> <span class="n">fin</span> <span class="o">=</span> <span class="k">new</span> <span class="n">FileInputStream</span><span class="o">(</span> <span class="n">file</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Read all of it into the array; if we don&#39;t get all,  </span>
</span><span class='line'>    <span class="c1">// then it&#39;s an error.  </span>
</span><span class='line'>    <span class="kt">int</span> <span class="n">r</span> <span class="o">=</span> <span class="n">fin</span><span class="o">.</span><span class="na">read</span><span class="o">(</span> <span class="n">raw</span> <span class="o">);</span>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">r</span> <span class="o">!=</span> <span class="n">len</span><span class="o">)</span>
</span><span class='line'>      <span class="k">throw</span> <span class="k">new</span> <span class="nf">IOException</span><span class="o">(</span> <span class="s">&quot;Can&#39;t read all, &quot;</span><span class="o">+</span><span class="n">r</span><span class="o">+</span><span class="s">&quot; != &quot;</span><span class="o">+</span><span class="n">len</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Don&#39;t forget to close the file!  </span>
</span><span class='line'>    <span class="n">fin</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// And finally return the file contents as an array  </span>
</span><span class='line'>    <span class="k">return</span> <span class="n">raw</span><span class="o">;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// Spawn a process to compile the java source code file  </span>
</span><span class='line'>  <span class="c1">// specified in the &#39;javaFile&#39; parameter.  Return a true if  </span>
</span><span class='line'>  <span class="c1">// the compilation worked, false otherwise.  </span>
</span><span class='line'>  <span class="kd">private</span> <span class="kt">boolean</span> <span class="nf">compile</span><span class="o">(</span> <span class="n">String</span> <span class="n">javaFile</span> <span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">{</span>
</span><span class='line'>    <span class="c1">// Let the user know what&#39;s going on  </span>
</span><span class='line'>    <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span> <span class="s">&quot;CCL: Compiling &quot;</span><span class="o">+</span><span class="n">javaFile</span><span class="o">+</span><span class="s">&quot;...&quot;</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Start up the compiler  </span>
</span><span class='line'>    <span class="n">Process</span> <span class="n">p</span> <span class="o">=</span> <span class="n">Runtime</span><span class="o">.</span><span class="na">getRuntime</span><span class="o">().</span><span class="na">exec</span><span class="o">(</span> <span class="s">&quot;javac &quot;</span><span class="o">+</span><span class="n">javaFile</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Wait for it to finish running  </span>
</span><span class='line'>    <span class="k">try</span> <span class="o">{</span>
</span><span class='line'>      <span class="n">p</span><span class="o">.</span><span class="na">waitFor</span><span class="o">();</span>
</span><span class='line'>    <span class="o">}</span> <span class="k">catch</span><span class="o">(</span> <span class="n">InterruptedException</span> <span class="n">ie</span> <span class="o">)</span> <span class="o">{</span> <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span> <span class="n">ie</span> <span class="o">);</span> <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Check the return code, in case of a compilation error  </span>
</span><span class='line'>    <span class="kt">int</span> <span class="n">ret</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="na">exitValue</span><span class="o">();</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Tell whether the compilation worked  </span>
</span><span class='line'>    <span class="k">return</span> <span class="n">ret</span><span class="o">==</span><span class="mi">0</span><span class="o">;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>  <span class="c1">// The heart of the ClassLoader -- automatically compile  </span>
</span><span class='line'>  <span class="c1">// source as necessary when looking for class files  </span>
</span><span class='line'>  <span class="kd">public</span> <span class="n">Class</span> <span class="nf">loadClass</span><span class="o">(</span> <span class="n">String</span> <span class="n">name</span><span class="o">,</span> <span class="kt">boolean</span> <span class="n">resolve</span> <span class="o">)</span>
</span><span class='line'>      <span class="kd">throws</span> <span class="n">ClassNotFoundException</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Our goal is to get a Class object  </span>
</span><span class='line'>    <span class="n">Class</span> <span class="n">clas</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// First, see if we&#39;ve already dealt with this one  </span>
</span><span class='line'>    <span class="n">clas</span> <span class="o">=</span> <span class="n">findLoadedClass</span><span class="o">(</span> <span class="n">name</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">//System.out.println( &quot;findLoadedClass: &quot;+clas );  </span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Create a pathname from the class name  </span>
</span><span class='line'>    <span class="c1">// E.g. java.lang.Object =&gt; java/lang/Object  </span>
</span><span class='line'>    <span class="n">String</span> <span class="n">fileStub</span> <span class="o">=</span> <span class="n">name</span><span class="o">.</span><span class="na">replace</span><span class="o">(</span> <span class="sc">&#39;.&#39;</span><span class="o">,</span> <span class="sc">&#39;/&#39;</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Build objects pointing to the source code (.java) and object  </span>
</span><span class='line'>    <span class="c1">// code (.class)  </span>
</span><span class='line'>    <span class="n">String</span> <span class="n">javaFilename</span> <span class="o">=</span> <span class="n">fileStub</span><span class="o">+</span><span class="s">&quot;.java&quot;</span><span class="o">;</span>
</span><span class='line'>    <span class="n">String</span> <span class="n">classFilename</span> <span class="o">=</span> <span class="n">fileStub</span><span class="o">+</span><span class="s">&quot;.class&quot;</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="n">File</span> <span class="n">javaFile</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span> <span class="n">javaFilename</span> <span class="o">);</span>
</span><span class='line'>    <span class="n">File</span> <span class="n">classFile</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span> <span class="n">classFilename</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">//System.out.println( &quot;j &quot;+javaFile.lastModified()+&quot; c &quot;+  </span>
</span><span class='line'>    <span class="c1">//  classFile.lastModified() );  </span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// First, see if we want to try compiling.  We do if (a) there  </span>
</span><span class='line'>    <span class="c1">// is source code, and either (b0) there is no object code,  </span>
</span><span class='line'>    <span class="c1">// or (b1) there is object code, but it&#39;s older than the source  </span>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">javaFile</span><span class="o">.</span><span class="na">exists</span><span class="o">()</span> <span class="o">&amp;&amp;</span>
</span><span class='line'>         <span class="o">(!</span><span class="n">classFile</span><span class="o">.</span><span class="na">exists</span><span class="o">()</span> <span class="o">||</span>
</span><span class='line'>          <span class="n">javaFile</span><span class="o">.</span><span class="na">lastModified</span><span class="o">()</span> <span class="o">&gt;</span> <span class="n">classFile</span><span class="o">.</span><span class="na">lastModified</span><span class="o">()))</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>      <span class="k">try</span> <span class="o">{</span>
</span><span class='line'>        <span class="c1">// Try to compile it.  If this doesn&#39;t work, then  </span>
</span><span class='line'>        <span class="c1">// we must declare failure.  (It&#39;s not good enough to use  </span>
</span><span class='line'>        <span class="c1">// and already-existing, but out-of-date, classfile)  </span>
</span><span class='line'>        <span class="k">if</span> <span class="o">(!</span><span class="n">compile</span><span class="o">(</span> <span class="n">javaFilename</span> <span class="o">)</span> <span class="o">||</span> <span class="o">!</span><span class="n">classFile</span><span class="o">.</span><span class="na">exists</span><span class="o">())</span> <span class="o">{</span>
</span><span class='line'>          <span class="k">throw</span> <span class="k">new</span> <span class="nf">ClassNotFoundException</span><span class="o">(</span> <span class="s">&quot;Compile failed: &quot;</span><span class="o">+</span><span class="n">javaFilename</span> <span class="o">);</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>      <span class="o">}</span> <span class="k">catch</span><span class="o">(</span> <span class="n">IOException</span> <span class="n">ie</span> <span class="o">)</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>        <span class="c1">// Another place where we might come to if we fail  </span>
</span><span class='line'>        <span class="c1">// to compile  </span>
</span><span class='line'>        <span class="k">throw</span> <span class="k">new</span> <span class="nf">ClassNotFoundException</span><span class="o">(</span> <span class="n">ie</span><span class="o">.</span><span class="na">toString</span><span class="o">()</span> <span class="o">);</span>
</span><span class='line'>      <span class="o">}</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Let&#39;s try to load up the raw bytes, assuming they were  </span>
</span><span class='line'>    <span class="c1">// properly compiled, or didn&#39;t need to be compiled  </span>
</span><span class='line'>    <span class="k">try</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>      <span class="c1">// read the bytes  </span>
</span><span class='line'>      <span class="kt">byte</span> <span class="n">raw</span><span class="o">[]</span> <span class="o">=</span> <span class="n">getBytes</span><span class="o">(</span> <span class="n">classFilename</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>      <span class="c1">// try to turn them into a class  </span>
</span><span class='line'>      <span class="n">clas</span> <span class="o">=</span> <span class="n">defineClass</span><span class="o">(</span> <span class="n">name</span><span class="o">,</span> <span class="n">raw</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">raw</span><span class="o">.</span><span class="na">length</span> <span class="o">);</span>
</span><span class='line'>    <span class="o">}</span> <span class="k">catch</span><span class="o">(</span> <span class="n">IOException</span> <span class="n">ie</span> <span class="o">)</span> <span class="o">{</span>
</span><span class='line'>      <span class="c1">// This is not a failure!  If we reach here, it might  </span>
</span><span class='line'>      <span class="c1">// mean that we are dealing with a class in a library,  </span>
</span><span class='line'>      <span class="c1">// such as java.lang.Object  </span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">//System.out.println( &quot;defineClass: &quot;+clas );  </span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Maybe the class is in a library -- try loading  </span>
</span><span class='line'>    <span class="c1">// the normal way  </span>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">clas</span><span class="o">==</span><span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>      <span class="n">clas</span> <span class="o">=</span> <span class="n">findSystemClass</span><span class="o">(</span> <span class="n">name</span> <span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">//System.out.println( &quot;findSystemClass: &quot;+clas );  </span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Resolve the class, if any, but only if the &quot;resolve&quot;  </span>
</span><span class='line'>    <span class="c1">// flag is set to true  </span>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">resolve</span> <span class="o">&amp;&amp;</span> <span class="n">clas</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span>
</span><span class='line'>      <span class="n">resolveClass</span><span class="o">(</span> <span class="n">clas</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// If we still don&#39;t have a class, it&#39;s an error  </span>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">clas</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
</span><span class='line'>      <span class="k">throw</span> <span class="k">new</span> <span class="nf">ClassNotFoundException</span><span class="o">(</span> <span class="n">name</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Otherwise, return the class  </span>
</span><span class='line'>    <span class="k">return</span> <span class="n">clas</span><span class="o">;</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>CCLRun.java</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="cm">/* </span>
</span><span class='line'><span class="cm"> </span>
</span><span class='line'><span class="cm">CCLRun executes a Java program by loading it through a </span>
</span><span class='line'><span class="cm">CompilingClassLoader. </span>
</span><span class='line'><span class="cm"> </span>
</span><span class='line'><span class="cm">*/</span>
</span><span class='line'>
</span><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">CCLRun</span>
</span><span class='line'><span class="o">{</span>
</span><span class='line'>  <span class="kd">static</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span> <span class="n">String</span> <span class="n">args</span><span class="o">[]</span> <span class="o">)</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// The first argument is the Java program (class) the user  </span>
</span><span class='line'>    <span class="c1">// wants to run  </span>
</span><span class='line'>    <span class="n">String</span> <span class="n">progClass</span> <span class="o">=</span> <span class="n">args</span><span class="o">[</span><span class="mi">0</span><span class="o">];</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// And the arguments to that program are just  </span>
</span><span class='line'>    <span class="c1">// arguments 1..n, so separate those out into  </span>
</span><span class='line'>    <span class="c1">// their own array  </span>
</span><span class='line'>    <span class="n">String</span> <span class="n">progArgs</span><span class="o">[]</span> <span class="o">=</span> <span class="k">new</span> <span class="n">String</span><span class="o">[</span><span class="n">args</span><span class="o">.</span><span class="na">length</span><span class="o">-</span><span class="mi">1</span><span class="o">];</span>
</span><span class='line'>    <span class="n">System</span><span class="o">.</span><span class="na">arraycopy</span><span class="o">(</span> <span class="n">args</span><span class="o">,</span> <span class="mi">1</span><span class="o">,</span> <span class="n">progArgs</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">progArgs</span><span class="o">.</span><span class="na">length</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Create a CompilingClassLoader  </span>
</span><span class='line'>    <span class="n">CompilingClassLoader</span> <span class="n">ccl</span> <span class="o">=</span> <span class="k">new</span> <span class="n">CompilingClassLoader</span><span class="o">();</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Load the main class through our CCL  </span>
</span><span class='line'>    <span class="n">Class</span> <span class="n">clas</span> <span class="o">=</span> <span class="n">ccl</span><span class="o">.</span><span class="na">loadClass</span><span class="o">(</span> <span class="n">progClass</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Use reflection to call its main() method, and to  </span>
</span><span class='line'>    <span class="c1">// pass the arguments in.  </span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Get a class representing the type of the main method&#39;s argument  </span>
</span><span class='line'>    <span class="n">Class</span> <span class="n">mainArgType</span><span class="o">[]</span> <span class="o">=</span> <span class="o">{</span> <span class="o">(</span><span class="k">new</span> <span class="n">String</span><span class="o">[</span><span class="mi">0</span><span class="o">]).</span><span class="na">getClass</span><span class="o">()</span> <span class="o">};</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Find the standard main method in the class  </span>
</span><span class='line'>    <span class="n">Method</span> <span class="n">main</span> <span class="o">=</span> <span class="n">clas</span><span class="o">.</span><span class="na">getMethod</span><span class="o">(</span> <span class="s">&quot;main&quot;</span><span class="o">,</span> <span class="n">mainArgType</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Create a list containing the arguments -- in this case,  </span>
</span><span class='line'>    <span class="c1">// an array of strings  </span>
</span><span class='line'>    <span class="n">Object</span> <span class="n">argsArray</span><span class="o">[]</span> <span class="o">=</span> <span class="o">{</span> <span class="n">progArgs</span> <span class="o">};</span>
</span><span class='line'>
</span><span class='line'>    <span class="c1">// Call the method  </span>
</span><span class='line'>    <span class="n">main</span><span class="o">.</span><span class="na">invoke</span><span class="o">(</span> <span class="kc">null</span><span class="o">,</span> <span class="n">argsArray</span> <span class="o">);</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>Foo.java</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">Foo</span>
</span><span class='line'><span class="o">{</span>
</span><span class='line'>  <span class="kd">static</span> <span class="kd">public</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span> <span class="n">String</span> <span class="n">args</span><span class="o">[]</span> <span class="o">)</span> <span class="kd">throws</span> <span class="n">Exception</span> <span class="o">{</span>
</span><span class='line'>    <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span> <span class="s">&quot;foo! &quot;</span><span class="o">+</span><span class="n">args</span><span class="o">[</span><span class="mi">0</span><span class="o">]+</span><span class="s">&quot; &quot;</span><span class="o">+</span><span class="n">args</span><span class="o">[</span><span class="mi">1</span><span class="o">]</span> <span class="o">);</span>
</span><span class='line'>  <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>执行 CLL 类，命令行传入 Foo 的类名。执行结果如下：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="nl">CCL:</span> <span class="n">Compiling</span> <span class="n">Foo</span><span class="o">.</span><span class="na">java</span><span class="o">...</span>
</span><span class='line'><span class="n">foo</span><span class="o">!</span> <span class="n">arg1</span> <span class="n">arg2</span>
</span></code></pre></td></tr></table></div></figure>


<p>具体参见资料：<a href="http://www.ibm.com/developerworks/java/tutorials/j-classloader/index.html">IBM DeveloperWorks ： Understanding the Java ClassLoader</a>， 这是一篇 2001 年的文章，是早年 Java1.1 的实现方式。如今的 Java 已经变得更加人性化，多功能化，鲁棒性也更强了。</p>

<h2>Java1.2 以后的实现</h2>

<h3>原理介绍</h3>

<p>Java1.2 以后，类加载器实现了 parent-child 模型，能更好的控制安全性方面的问题。</p>

<p>由于 loadClass()方法中包含了 parent-child 的责任链模式逻辑，自定义类加载的自定义部分用策略模式从 loadClass()方法中剥离到了 findClass()中。对应的有一个 findLoadedClass()，这个方法用来实现查找当前加载器是否有加载该某类。</p>

<p>loadClass()：加载一个类时，先调 findLoadedClass()，如果没有找到，则调用父亲加载器的加载方法。如果父亲找到了该类，就返回 Class 实例，没有找到，则父亲加载器会抛出一个异常，捕捉到这个异常后，儿子加载器会自己调用 findClass()尝试实现对类的加载。如果依然没有成功加载，则再向外抛出一个异常。通过向父亲加载器迭代实现了 parent-first 的委托关系。</p>

<p>类加载的流程如图：这里显示了一个类未能成功加载所要经历的流程。（来自 <a href="http://stackoverflow.com/questions/3544614/how-is-the-control-flow-to-findclass-of">stackoverflow:how-is-the-control-flow-to-findclass-of</a>）</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'>  <span class="n">A</span><span class="o">.</span><span class="na">loadClass</span><span class="o">()</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'>   <span class="o">(</span><span class="n">not</span><span class="o">-</span><span class="n">found</span><span class="o">?)</span> <span class="o">(</span><span class="n">by</span> <span class="n">findLoadedClass</span><span class="o">)</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'>  <span class="n">B</span><span class="o">.</span><span class="na">loadClass</span><span class="o">()</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'>   <span class="o">(</span><span class="n">not</span> <span class="n">found</span><span class="o">?)</span> <span class="o">(</span><span class="n">by</span> <span class="n">findLoadedClass</span><span class="o">)</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'><span class="n">systemclassloader</span><span class="o">.</span><span class="na">loadClass</span>  <span class="o">(</span><span class="n">Bs</span> <span class="n">parent</span><span class="o">,</span> <span class="n">also</span> <span class="n">can</span> <span class="n">be</span>
</span><span class='line'>       <span class="o">|</span>                  <span class="n">called</span> <span class="n">classpath</span> <span class="n">classloader</span><span class="o">)</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'>   <span class="o">(</span><span class="n">not</span> <span class="n">found</span><span class="o">?)</span> <span class="o">(</span><span class="n">by</span> <span class="n">findLoadedClass</span><span class="o">)</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'><span class="n">bootstrap</span> <span class="n">classloader</span><span class="o">.</span><span class="na">loadClass</span> <span class="o">(</span><span class="n">the</span> <span class="n">bootstrap</span> <span class="n">classloader</span><span class="o">,</span>
</span><span class='line'>       <span class="o">|</span>                   <span class="o">(</span><span class="k">this</span> <span class="n">has</span> <span class="n">no</span> <span class="n">parent</span><span class="o">)</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'>   <span class="o">(</span><span class="n">not</span> <span class="n">found</span><span class="o">?)</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'><span class="n">systemclassloader</span><span class="o">.</span><span class="na">findClass</span>  <span class="o">(</span><span class="n">on</span> <span class="n">system</span> <span class="n">classloader</span><span class="o">,</span>
</span><span class='line'>       <span class="o">|</span>                   <span class="n">will</span> <span class="k">try</span> <span class="n">to</span> <span class="s">&quot;find&quot;</span> <span class="kd">class</span> <span class="nc">in</span> <span class="s">&quot;classpath&quot;</span><span class="o">)</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'>   <span class="o">(</span><span class="n">not</span> <span class="n">found</span><span class="o">?)</span> <span class="n">ClassNotFoundException</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'>   <span class="n">B</span><span class="o">.</span><span class="na">findClass</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'>   <span class="o">(</span><span class="n">not</span> <span class="n">found</span><span class="o">?)</span> <span class="n">ClassNotFoundException</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'>   <span class="n">A</span><span class="o">.</span><span class="na">findClass</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'>    <span class="o">(</span><span class="n">not</span> <span class="n">found</span><span class="o">?)</span>
</span><span class='line'>       <span class="o">|</span>
</span><span class='line'>   <span class="n">ClassNotFoundException</span>
</span></code></pre></td></tr></table></div></figure>


<p>注意，对于扩展类加载器，通过 getParent()方法返回的父亲加载器是 null，因为引导类加载器是本地实现的，并非 Java 实现。那么如何从扩展类加载器向上回溯呢？答案如下：</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="k">try</span> <span class="o">{</span>
</span><span class='line'>    <span class="k">if</span> <span class="o">(</span><span class="n">parent</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>       <span class="n">c</span> <span class="o">=</span> <span class="n">parent</span><span class="o">.</span><span class="na">loadClass</span><span class="o">(</span><span class="n">name</span><span class="o">,</span> <span class="kc">false</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span> <span class="k">else</span> <span class="o">{</span>
</span><span class='line'>       <span class="n">c</span> <span class="o">=</span> <span class="n">findBootstrapClassOrNull</span><span class="o">(</span><span class="n">name</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">ClassNotFoundException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>   <span class="c1">// ClassNotFoundException thrown if class not found  </span>
</span><span class='line'>     <span class="c1">// from the non-null parent class loader  </span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>
这是类加载器的源代码，对于父加载器为 null 的情况，会直接调用 findBootstrapClassOrNull()方法尝试用引导类加载器加载。通过源代码，能够很好的理解这里的 parent-child 模型了。</p>

<p>另注意，对于基于 parent-child 模型的类加载器实现，都需要定义一个以 parent 类加载器作为参数的构造函数，以指定父加载器。如果直接调用没有参数的构造函数，则默认制定的是 systemclassloader 作为 parent。</p>

<h3>编程实例</h3>

<p>下面的例子是我用来实现动态分析 Java 类关系的加载器代码。</p>

<p>具体逻辑是：调用 ASM 开源库的 API，在加载器加载类时，修改类文件中的字节码，插入相应的字节码语句，让对象在创建或执行相应指令时，在 log 文件中记录自己的行为。</p>

<p>在编码的过程中，我遇到的一个错误：将需要使用自定义加载器加载的类文件直接放在了 eclipse 工程中的 bin 目录下。而这个目录是可以通过系统类加载器找到路径并加载的。根据 parent-first 的实现，这些类直接被系统类加载器加载了，也就绕过了自定义加载器的处理机制。修改过路径以后没有出现相应问题了。</p>

<p>ASMClassLoader.java</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
<span class='line-number'>32</span>
<span class='line-number'>33</span>
<span class='line-number'>34</span>
<span class='line-number'>35</span>
<span class='line-number'>36</span>
<span class='line-number'>37</span>
<span class='line-number'>38</span>
<span class='line-number'>39</span>
<span class='line-number'>40</span>
<span class='line-number'>41</span>
<span class='line-number'>42</span>
<span class='line-number'>43</span>
<span class='line-number'>44</span>
<span class='line-number'>45</span>
<span class='line-number'>46</span>
<span class='line-number'>47</span>
<span class='line-number'>48</span>
<span class='line-number'>49</span>
<span class='line-number'>50</span>
<span class='line-number'>51</span>
<span class='line-number'>52</span>
<span class='line-number'>53</span>
<span class='line-number'>54</span>
<span class='line-number'>55</span>
<span class='line-number'>56</span>
<span class='line-number'>57</span>
<span class='line-number'>58</span>
<span class='line-number'>59</span>
<span class='line-number'>60</span>
<span class='line-number'>61</span>
<span class='line-number'>62</span>
<span class='line-number'>63</span>
<span class='line-number'>64</span>
<span class='line-number'>65</span>
<span class='line-number'>66</span>
<span class='line-number'>67</span>
<span class='line-number'>68</span>
<span class='line-number'>69</span>
<span class='line-number'>70</span>
<span class='line-number'>71</span>
<span class='line-number'>72</span>
<span class='line-number'>73</span>
<span class='line-number'>74</span>
<span class='line-number'>75</span>
<span class='line-number'>76</span>
<span class='line-number'>77</span>
<span class='line-number'>78</span>
<span class='line-number'>79</span>
<span class='line-number'>80</span>
<span class='line-number'>81</span>
<span class='line-number'>82</span>
<span class='line-number'>83</span>
<span class='line-number'>84</span>
<span class='line-number'>85</span>
<span class='line-number'>86</span>
<span class='line-number'>87</span>
<span class='line-number'>88</span>
<span class='line-number'>89</span>
<span class='line-number'>90</span>
<span class='line-number'>91</span>
<span class='line-number'>92</span>
<span class='line-number'>93</span>
<span class='line-number'>94</span>
<span class='line-number'>95</span>
<span class='line-number'>96</span>
<span class='line-number'>97</span>
<span class='line-number'>98</span>
<span class='line-number'>99</span>
<span class='line-number'>100</span>
<span class='line-number'>101</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kn">package</span> <span class="n">biaobiaoqi</span><span class="o">.</span><span class="na">classLoader</span><span class="o">;</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">java.io.*</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'><span class="kn">import</span> <span class="nn">org.objectweb.asm.ClassReader</span><span class="o">;</span>
</span><span class='line'><span class="kn">import</span> <span class="nn">org.objectweb.asm.ClassWriter</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'><span class="kn">import</span> <span class="nn">biaobiaoqi.asm.*</span><span class="o">;</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ASMClassLoader</span> <span class="kd">extends</span> <span class="n">ClassLoader</span>
</span><span class='line'><span class="o">{</span>
</span><span class='line'>    <span class="n">String</span> <span class="n">basePath</span> <span class="o">;</span>
</span><span class='line'>
</span><span class='line'>    <span class="cm">/**reference to System Classloader as the parent class loader </span>
</span><span class='line'><span class="cm">     * @param path &lt;br&gt; the path of .class files will be loaded </span>
</span><span class='line'><span class="cm">     */</span>
</span><span class='line'>    <span class="kd">public</span> <span class="nf">ASMClassLoader</span><span class="o">(</span><span class="n">String</span> <span class="n">path</span><span class="o">){</span>
</span><span class='line'>        <span class="n">basePath</span> <span class="o">=</span> <span class="n">path</span> <span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="cm">/** </span>
</span><span class='line'><span class="cm">     * reference to parent as it&#39;s parent classloader </span>
</span><span class='line'><span class="cm">     * @param path </span>
</span><span class='line'><span class="cm">     * @param parent </span>
</span><span class='line'><span class="cm">     */</span>
</span><span class='line'>    <span class="kd">public</span> <span class="nf">ASMClassLoader</span><span class="o">(</span><span class="n">String</span> <span class="n">path</span> <span class="o">,</span> <span class="n">ClassLoader</span> <span class="n">parent</span><span class="o">){</span>
</span><span class='line'>        <span class="kd">super</span><span class="o">(</span><span class="n">parent</span><span class="o">);</span>
</span><span class='line'>        <span class="n">basePath</span> <span class="o">=</span> <span class="n">path</span> <span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">Class</span> <span class="nf">findClass</span><span class="o">(</span><span class="n">String</span> <span class="n">name</span><span class="o">)</span> <span class="kd">throws</span> <span class="n">ClassNotFoundException</span><span class="o">{</span>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;findClass&quot;</span><span class="o">);</span>
</span><span class='line'>        <span class="kt">byte</span><span class="o">[]</span> <span class="n">raw</span><span class="o">;</span>
</span><span class='line'>        <span class="k">try</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">raw</span> <span class="o">=</span> <span class="n">getBytesFromBasePath</span><span class="o">(</span> <span class="n">name</span> <span class="o">);</span>
</span><span class='line'>        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="n">IOException</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>            <span class="n">e</span><span class="o">.</span><span class="na">printStackTrace</span><span class="o">();</span>
</span><span class='line'>            <span class="k">throw</span> <span class="k">new</span> <span class="nf">ClassNotFoundException</span><span class="o">();</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>        <span class="kt">byte</span><span class="o">[]</span> <span class="n">transformed</span> <span class="o">=</span> <span class="n">instrumentBtyeCode</span><span class="o">(</span><span class="n">raw</span><span class="o">);</span>
</span><span class='line'>        <span class="cm">/* </span>
</span><span class='line'><span class="cm">        try{ </span>
</span><span class='line'><span class="cm">            FileOutputStream file = new FileOutputStream( &quot;/home/biaobiaoqi/&quot; +name.replace( &#39;.&#39;, &#39;/&#39; )+&quot;.class&quot;); </span>
</span><span class='line'><span class="cm">            file.write( transformed); </span>
</span><span class='line'><span class="cm">            file.close(); </span>
</span><span class='line'><span class="cm">        } </span>
</span><span class='line'><span class="cm">        catch (IOException e) { </span>
</span><span class='line'><span class="cm">            e.printStackTrace(); </span>
</span><span class='line'><span class="cm">        } </span>
</span><span class='line'><span class="cm">        */</span>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">transformed</span> <span class="o">==</span> <span class="kc">null</span><span class="o">){</span>
</span><span class='line'>            <span class="k">throw</span> <span class="k">new</span> <span class="nf">ClassNotFoundException</span><span class="o">();</span>
</span><span class='line'>        <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>        <span class="k">return</span> <span class="nf">defineClass</span><span class="o">(</span><span class="n">name</span><span class="o">,</span> <span class="n">transformed</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="n">transformed</span><span class="o">.</span><span class="na">length</span> <span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">private</span> <span class="kt">byte</span><span class="o">[]</span> <span class="nf">getBytesFromBasePath</span><span class="o">(</span> <span class="n">String</span> <span class="n">className</span> <span class="o">)</span> <span class="kd">throws</span> <span class="n">IOException</span> <span class="o">,</span><span class="n">ClassNotFoundException</span><span class="o">{</span>
</span><span class='line'>        <span class="n">String</span> <span class="n">fileStub</span> <span class="o">=</span> <span class="n">className</span><span class="o">.</span><span class="na">replace</span><span class="o">(</span> <span class="sc">&#39;.&#39;</span><span class="o">,</span> <span class="sc">&#39;/&#39;</span> <span class="o">);</span>
</span><span class='line'>        <span class="n">String</span> <span class="n">classFileName</span> <span class="o">=</span> <span class="n">basePath</span> <span class="o">+</span><span class="n">fileStub</span><span class="o">+</span><span class="s">&quot;.class&quot;</span><span class="o">;</span>
</span><span class='line'>        <span class="n">File</span> <span class="n">file</span> <span class="o">=</span> <span class="k">new</span> <span class="n">File</span><span class="o">(</span> <span class="n">classFileName</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>        <span class="kt">long</span> <span class="n">len</span> <span class="o">=</span> <span class="n">file</span><span class="o">.</span><span class="na">length</span><span class="o">();</span>
</span><span class='line'>        <span class="kt">byte</span> <span class="n">raw</span><span class="o">[]</span> <span class="o">=</span> <span class="k">new</span> <span class="kt">byte</span><span class="o">[(</span><span class="kt">int</span><span class="o">)</span><span class="n">len</span><span class="o">];</span>
</span><span class='line'>        <span class="n">FileInputStream</span> <span class="n">fin</span> <span class="o">=</span> <span class="k">new</span> <span class="n">FileInputStream</span><span class="o">(</span> <span class="n">file</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>        <span class="kt">int</span> <span class="n">r</span> <span class="o">=</span> <span class="n">fin</span><span class="o">.</span><span class="na">read</span><span class="o">(</span> <span class="n">raw</span> <span class="o">);</span>
</span><span class='line'>        <span class="k">if</span> <span class="o">(</span><span class="n">r</span> <span class="o">!=</span> <span class="n">len</span><span class="o">)</span>
</span><span class='line'>            <span class="k">throw</span> <span class="k">new</span> <span class="nf">IOException</span><span class="o">(</span> <span class="s">&quot;Can&#39;t read all, &quot;</span><span class="o">+</span><span class="n">r</span><span class="o">+</span><span class="s">&quot; != &quot;</span><span class="o">+</span><span class="n">len</span> <span class="o">);</span>
</span><span class='line'>
</span><span class='line'>        <span class="n">fin</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">raw</span><span class="o">;</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="kd">private</span> <span class="kt">byte</span><span class="o">[]</span> <span class="nf">instrumentBtyeCode</span><span class="o">(</span><span class="kt">byte</span><span class="o">[]</span> <span class="n">raw</span><span class="o">){</span>
</span><span class='line'>        <span class="n">ClassWriter</span> <span class="n">cw</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ClassWriter</span><span class="o">(</span><span class="n">ClassWriter</span><span class="o">.</span><span class="na">COMPUTE_FRAMES</span><span class="o">);</span>
</span><span class='line'>        <span class="n">ASMClassAdapter</span> <span class="n">mca</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ASMClassAdapter</span><span class="o">(</span><span class="n">cw</span><span class="o">);</span>
</span><span class='line'>        <span class="n">ClassReader</span> <span class="n">cr</span> <span class="o">=</span> <span class="k">new</span> <span class="n">ClassReader</span><span class="o">(</span><span class="n">raw</span><span class="o">);</span>
</span><span class='line'>        <span class="n">cr</span><span class="o">.</span><span class="na">accept</span><span class="o">(</span><span class="n">mca</span><span class="o">,</span> <span class="mi">0</span><span class="o">);</span>
</span><span class='line'>        <span class="k">return</span> <span class="n">cw</span><span class="o">.</span><span class="na">toByteArray</span><span class="o">();</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">Class</span> <span class="nf">loadClass</span><span class="o">(</span> <span class="n">String</span> <span class="n">name</span><span class="o">,</span> <span class="kt">boolean</span> <span class="n">resolve</span> <span class="o">)</span>
</span><span class='line'>        <span class="kd">throws</span> <span class="n">ClassNotFoundException</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;loadClass_resolve&quot;</span><span class="o">);</span>
</span><span class='line'>          <span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">loadClass</span><span class="o">(</span><span class="n">name</span> <span class="o">,</span><span class="n">resolve</span><span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'>
</span><span class='line'>    <span class="nd">@Override</span>
</span><span class='line'>    <span class="kd">public</span> <span class="n">Class</span> <span class="nf">loadClass</span><span class="o">(</span> <span class="n">String</span> <span class="n">name</span> <span class="o">)</span>
</span><span class='line'>        <span class="kd">throws</span> <span class="n">ClassNotFoundException</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;loadClass&quot;</span><span class="o">);</span>
</span><span class='line'>          <span class="k">return</span> <span class="kd">super</span><span class="o">.</span><span class="na">loadClass</span><span class="o">(</span><span class="n">name</span> <span class="o">);</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loader-in-practice/'>http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loader-in-practice/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Java 类加载器]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/09/07/java-classloader/"/>
    <updated>2013-09-07T23:15:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/09/07/java-classloader</id>
    <content type="html"><![CDATA[<h2>背景知识</h2>

<p>Java 平台无关的特性是由 JVM(Java 虚拟机)支撑的。不同平台有不同的 JVM 支持。</p>

<p>计算机领域有这么一句话：</p>

<blockquote><p>计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决。</p></blockquote>

<p>JVM 其实也可以看做是运行的 Java 程序和实际的硬件架构之间的一个新抽象层。</p>

<p>一个 Java 程序从编写到执行的流程一句话概括如下：首先将 Java 源代码（*.java 文件）编译成字节码（*.class 文件，字节码之于 Java 源码，类比于汇编代码之于 C 源码），然后由 JVM 运行那些字节码文件。</p>

<p>JVM 工作原理如下图</p>

<p><img src="https://dl.dropboxusercontent.com/u/64021093/Java%20Classloader/JVM%E5%B7%A5%E4%BD%9C%E5%8E%9F%E7%90%86.gif" alt="JVM framework" /></p>

<p>Java 中所有的类文件都需要由类加载器（Class Loader）装载到 JVM 中。可以简单的将 JVM 理解为一个工厂，类文件就是等待加工的原料，加载器则是装载货物的工人。Java 类被装载之后，才能进入到 JVM 的运行时机制中，开始运行。</p>

<h2>类加载器的作用</h2>

<p>顾名思义，Java 类加载器的作用是向 JVM 中装载类。</p>

<p>这种动态装载的技术是 Java 的一种创新，让类能够动态加载到 JVM 中执行（更详细的介绍参见 <a href="http://www.ibm.com/developerworks/java/library/j-dyn0429/">Java programming dynamics, Part 1: Java classes and class loading</a>）。</p>

<!--more-->


<p>而它的意义远非仅仅是加载类这么简单。实际上，类加载器对 Java 的沙箱模型具有重大意义。他和安全管理模块（负责对类文件中的字节码进行校验，防止恶意代码的攻击）一起保证了 JVM 运行的安全性。</p>

<h2>类加载机制</h2>

<p>大体上，每个 Java 应用使用了如下几种类型的类加载器:</p>

<ul>
<li><p>1.引导类加载器（bootstrap class loader）</p>

<p>  它由 C++编写（注意，它非常特殊，并非 Java 中的 ClassLoader 类的子类）。当 JVM 启动时，引导类加载器也随之启动，它负责加载 Java 核心类，如 JRE 的 rt.jar、charsets.jar 等。从 Java1.2 开始，它只加载 Java 核心 API 部分。</p>

<p>  因为这些类是系统信任的类，所以这里的装载，跳过了很多对字节码的验证过程。</p></li>
<li><p>2.扩展类加载器（extension class loader）</p>

<p> 它负责加载/lib/ext 中的 java 扩展类。</p></li>
<li><p>3.系统类加载器（System Class Loader）</p>

<p>这是很重要的一个加载器，加载 Java 的路径 classpath 下的类。应用程序的装载默认由它负责。</p></li>
<li><p>4.自定义类加载器</p>

<p> 由系统类加载器继承得到。它的存在让我们能定制出各种不同功能的加载器，增加了 Java 的可扩展性。自定义的类加载器如果没有显示的继承关系，则其父类默认为系统类加载器。</p></li>
</ul>


<p>一个 JVM，只拥有一个引导类加载器，同时可以拥有多个自定义类加载器，方便不同应用环境的用户定制。比如，自定义类加载器能够动态的修改字节码，让它能接收并加载从网上传来的类文件或 Jar 包，甚至是任何编码方式的压缩包。只要自定义类加载器能够正确识别并调用相应方法来实现类的加载和解析，一切都有可能。</p>

<p>四种加载器不是四个独立的部分，他们之间具有一种特殊的父子关系，每个类加载器都保持着他们的父加载器的应用，共同组成了一条父子关系链，被称作 parent-delegation 模式。如下图</p>

<p><img src="https://dl.dropboxusercontent.com/u/64021093/Java%20Classloader/java%20classloader%20hierachy.gif" alt="class loader hirerachy" /></p>

<p>类加载器按照如此树形排列。类加载的查找顺序是：</p>

<p>cache &#8211;> parent &#8211;> self</p>

<p>子类加载器需要加载某个类时，并不是直接加载，而是首先查看 cache（cache 可以理解为加载器已经加载过的类的记录）。如果没有，则向父加载器提出请求，查看是否存在于父加载器的 cache 中。如此往上，直到根部的引导类加载器。如果引导类加载器的 cache 也没有这个类，则它尝试直接加载这个类，如果无法成功，则请求儿子加载器加载，依次往下。</p>

<p>直接接受程序请求加载某类的加载器被称作初始类加载器（Initiating class loader），而最终加载了该类的加载器则成为定义类加载器（defining class loader）。</p>

<p>类加载器的<code>getParent()</code>方法可以获得加载器的父亲。下面的代码用于输出各个层级的类加载器。</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="kd">public</span> <span class="kd">class</span> <span class="nc">ClassLoaderTest</span> <span class="o">{</span>
</span><span class='line'>    <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
</span><span class='line'>        <span class="n">ClassLoader</span> <span class="n">loader</span> <span class="o">=</span> <span class="n">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">().</span><span class="na">getContextClassLoader</span><span class="o">();</span>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;current loader:&quot;</span><span class="o">+</span><span class="n">loader</span><span class="o">);</span>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;parent loader:&quot;</span><span class="o">+</span><span class="n">loader</span><span class="o">.</span><span class="na">getParent</span><span class="o">());</span>
</span><span class='line'>        <span class="n">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">&quot;grandparent loader:&quot;</span><span class="o">+</span><span class="n">loader</span><span class="o">.</span><span class="na">getParent</span><span class="o">().</span><span class="na">getParent</span><span class="o">());</span>
</span><span class='line'>    <span class="o">}</span>
</span><span class='line'><span class="o">}</span>
</span></code></pre></td></tr></table></div></figure>


<p>显示的结果是</p>

<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='java'><span class='line'><span class="n">current</span> <span class="nl">loader:</span><span class="n">sun</span><span class="o">.</span><span class="na">misc</span><span class="o">.</span><span class="na">Launcher</span><span class="n">$AppClassLoader</span><span class="err">@</span><span class="mi">1</span><span class="n">c78e57</span>
</span><span class='line'><span class="n">parent</span> <span class="nl">loader:</span><span class="n">sun</span><span class="o">.</span><span class="na">misc</span><span class="o">.</span><span class="na">Launcher</span><span class="n">$ExtClassLoader</span><span class="err">@</span><span class="mi">5224</span><span class="n">ee</span>
</span><span class='line'><span class="n">grandparent</span> <span class="nl">loader:</span><span class="kc">null</span>
</span></code></pre></td></tr></table></div></figure>


<p>grandparent 显示的值是 null，并不意味着他没有 parent，而是因为它是由 C++编写的引导类加载器。他并不是 ClassLoader 类的子类，也就无法使用 getParent()方法获得返回了。</p>

<p>如此，parent class loader 总是拥有更高的加载优先级，这让想利用自定义加载器伪装加载某些重要类的恶意代码无法得逞。如果好奇，你可以尝试自己写 package java.lang 里的 String 类，加载执行试试～
另外，当类 A 调用另类 B 时，B 会由加载 A 的 class loader 加载，从而实现。</p>

<h2>加载类的流程</h2>

<p>类的装载大致可以分为三个步骤（如下图）：</p>

<ul>
<li>1.装载(loading)</li>
<li>2.链接(linking)</li>
<li>3.初始化(initialising)</li>
</ul>


<p><img src="https://dl.dropboxusercontent.com/u/64021093/Java%20Classloader/ClassLoaderProcess.gif" alt="class loader process" /></p>

<p>跟 C++或者 C 程序有很大的不同，编译过后的类文件中的字节码并没有设计好内存布局，这些需要等到加载之后的链接阶段，才会完成。这也是 java 可移植性中精彩的一笔！</p>

<p>关于类的加载、链接和初始化，请参见另一篇博文：<a href="http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loading-linking-and-initialising/">《Java 类的装载、链接和初始化》</a></p>

<p>关于类加载器的编程实践，请参见另一篇博文：<a href="http://biaobiaoqi.github.io/blog/2013/09/08/java-class-loader-in-practice/">《Java 类加载器编程实践》</a></p>

<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/09/07/java-classloader/'>http://biaobiaoqi.github.io/blog/2013/09/07/java-classloader/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[octopress第三方插件：博文同步工具syncPost]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/08/31/octopress-3rd-party-plugin-syncpost/"/>
    <updated>2013-08-31T19:30:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/08/31/octopress-3rd-party-plugin-syncpost</id>
    <content type="html"><![CDATA[<p>为了增加外链等考虑，独立博客往往有将博文同步到其他博客社区的需求。自己人肉黏贴的方式笨拙、重复，对于程序猿而言，着实不可取。</p>

<p>我在 github 上找到了 <a href="https://github.com/huangbowen521/octopress-syncPost">syncPost</a> 这个针对 octopress 的第三方工具，能够通过一些论坛提供的 <a href="http://en.wikipedia.org/wiki/MetaWeblog">metaWeblog</a> 服务实现 octopress 最新一篇博文的同步提交。</p>

<p>这大概就是我要找的东西吧。不过，其中有些细节并不是我想要的：</p>

<ul>
<li>1.在本地配置文件存储论坛账户的密码。虽然可以设置为 ignore 不提交到 git 代码库中，但这也并不安全。</li>
<li>2.只能同步最新的一篇博客，没有整体的博文搬家功能。</li>
<li>3.先比其他的 octopress 插件，原来版本的代码结构难于维护，比如有自己单独的配置文件，而不是使用全局的<code>_config.yml</code>(在那个版本中大概是为了不把明文密码提交到版本库中)，比如 ruby 文件单独在一个<code>_custom</code>文件夹下等</li>
</ul>


<p>基于这些点，我 fork 了 <a href="http://huangbowen.net/">huangbowen</a> 大哥的代码，定制成了它<a href="https://github.com/biaobiaoqi/octopress-syncPost">现在的样子</a>。</p>

<!--more-->


<h2>功能</h2>

<p>通过 rake 的方式实现同步功能：</p>

<ul>
<li>1.同步所有 octopress 的博文到目标博客。 (<code>rake sync_all_posts</code>)</li>
<li>2.同步最新一篇博文到目标博客。(<code>rake sync_lates_post</code>)</li>
</ul>


<h2>开发</h2>

<p>这个 octopress 插件的结构很简单：</p>

<ul>
<li>安装<code>nokogiri</code>和 <code>metaweblog</code>两个 gem。前者实现对 octopress 生成的 html 页面的解析，找到对应的博文。提供了客户端发起 MetaWeblog 请求的功能。</li>
<li>在全局配置文件<code>_config.yml</code>中配置了目标博客 MetaWeblog 的服务地址、用户的账号信息。</li>
<li>octopress/plugins/sync_*.rb 三个文件则是代码实现。其中 sync_post.rb 中包含了 post 类，是主要的逻辑代码所在，sync_all_posts.rb 和 sync_latest_post.rb 则是对前者的调用。</li>
<li>修改了 octopress 的 Rakefile，通过 rake 的方式实现功能。</li>
</ul>


<h2>下一步</h2>

<p>希望能进一步完善这个插件，现在考虑到的有：</p>

<ul>
<li>增加同步所有博文功能中，对目标博客中已有博文的查重功能。即对于已经同步过去的博客，不再发送请求，或者实现编辑功能的同步功能。由于站点的 MetaWeblog 服务大多对请求有时限控制，同步过程中可能出现中断，此时部分博文已经同步过去，而部分没有，这种情况下，暂时没有好的解决方式。</li>
<li>测试其他有 MetaWeblog 服务的博客站点（现在仅测试了 cnblog）。</li>
<li><del>cnblog 的博文显示中，似乎不支持 octopress 的代码块样式。</del> (注：原作者 huangbowen 大哥已经解释了是 css 样式的问题)</li>
</ul>


<p>欢迎感兴趣的朋友参与到这个 repo 中来:)</p>

<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/08/31/octopress-3rd-party-plugin-syncpost/'>http://biaobiaoqi.github.io/blog/2013/08/31/octopress-3rd-party-plugin-syncpost/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[《俞敏洪口述》]]></title>
    <link href="http://biaobiaoqi.github.io/blog/2013/07/07/yuminhong/"/>
    <updated>2013-07-07T23:53:00+08:00</updated>
    <id>http://biaobiaoqi.github.io/blog/2013/07/07/yuminhong</id>
    <content type="html"><![CDATA[<h2>质朴、诚恳和坚韧</h2>

<p>这本书是某次在 Amazon 上购书『满 100 送书一本』时挑的。放在抽屉里有段日子了。</p>

<p>一直挺崇拜俞敏洪老师。初次了解到他是高中的时候看的『赢在中国』，俞敏洪老师是嘉宾点评。而后混在考 T 的大流中去上了新东方的课程，几个老师讲得不错，比较可惜的是远离了老俞的时代。恰好前两天热映的『中国合伙人』，又把他拉回视野前沿。周日，趁着没心情码代码，忙里偷闲的读完了这本自传体的文字。</p>

<p>我不太喜欢读『活人』的传记。没有盖棺定论的事儿，或多或少掺杂些美华。但读这本书，却丝毫没有表现出浮夸，就像他的性格一般质朴、诚恳和坚韧。</p>

<p>或许在某些名人的传记里，永远不会看到他通过喝酒打通关系搞定了某个问题，而老俞有。书里多次的描述了他是怎样融入了这个『人情社会』。不可否认，他的本分和待人诚恳让他有人缘，这是他的优势。</p>

<p>他能在面对东方学校过河拆桥的举动下，沉住气，以和为贵，用合理的手段占据主动。</p>

<p>他能在北大读本科的阶段受众人嘲讽的环境下成长，不断学习，在毕业时，喊出『你们干十年成的我干二十年，你们二十年成的我干四十年。如果实在不行，我会保持心情愉快、身体健康，到八十岁以后把你们送走了我再走』。</p>

<p>凡此种种……</p>

<h2>世界观</h2>

<p>从每个成功的人的经历中，都能总结出数不清的箴言。成功的理由永远说不完。而这本书里，对我而言感触最深的无非是下面这些话：</p>

<blockquote><p>人生就是这样，你不受这个苦就会受那个苦。一个人如果从苦中能找到乐和幸福，那他就是幸运的。……我深刻的意识到什么也不做的痛苦比任何其他痛苦更加深刻，所以我一定要做事，做事的标准就是必须做对社会有好处的事情，以最大的努力在痛苦的世界中尽力而为。</p></blockquote>

<p>对我而言，这句话实在受用。我无法证明这些字在未来的时间里能对我造成的影响，却打在我最近几年来受困扰最多的点上。</p>

<p>乐观让人笑着面对生活。生活总归是有各种委屈、各种阻力，笑脸的背后，时常是受伤的心，区别在于受伤的心能埋得多深。我宁愿相信，乐观是因为能接受苦，能从苦中找到快乐，而不是没有苦。</p>

<p>时常的，我被不好的状态左右。那是种很奇怪的感觉，是面对压力的逃避、拖延，独自缩在没有人看得见的角落，在电脑屏幕前刷着网页，时间慢慢流走。然后心情越来越差，因为一事无成的颓废感。我是闲不下来的人。是的，『什么也不做的痛苦，比任何其他痛苦更加深刻』！</p>

<p>曾经在 ASES 的分享会上，听 dd 讲起过<a href="http://cuitianyi.com/blog/authentic-happiness-%E8%AF%BB%E4%B9%A6%E6%80%BB%E7%BB%93/">『积极心理学』</a>。其中有个观点是：『享乐是心灵资本的消费，而心流中的满足则是心灵资本的投资』。简单的讲，创造价值的事情会让我得到心流的满足，而简单的娱乐则会消费这种满足感。或许当两者之差为负的时候，我就感受到了那种空虚和煎熬，不止什么都不做，还消费了更多。</p>

<p>正是对社会责任的追求，老俞没有沉浸再新东方的飞速发展中。对他而言，现在的新东方的经营一切都要权衡公司的利益和教育理念。虽然他的新东方充满了实用主义和人文情怀，虽然他能让新东方尽到企业的社会责任，但这依然不是他想要的。他正在筹划办私立高校，用他的教育理念，给中国的教育发展贡献力量。这是中国的希望。</p>

<p>最后，以老俞的一句话结尾：</p>

<blockquote><p>人生的苦难肯定是没有尽头的，人要做的是在苦难中奋发起来，做自己能够做的和应该做的事儿，这就是我的世界观。</p></blockquote>

<p class='post-footer'>&nbsp;原文地址：<a href='http://biaobiaoqi.github.io/blog/2013/07/07/yuminhong/'>http://biaobiaoqi.github.io/blog/2013/07/07/yuminhong/</a><br/>&nbsp;版权声明：自由转载-非商用-非衍生-保持署名| <a href='http://creativecommons.org/licenses/by-nc-nd/3.0/deed.zh'>Creative Commons BY-NC-ND 3.0</a></p>

]]></content>
  </entry>
  
</feed>
