﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-经验不在于年限，在于积累---专注互联网软件开发-随笔分类-工作感悟</title><link>http://www.blogjava.net/hankchen/category/41481.html</link><description>把工作当事业做，把项目当作品做！</description><language>zh-cn</language><lastBuildDate>Wed, 29 Aug 2012 07:30:20 GMT</lastBuildDate><pubDate>Wed, 29 Aug 2012 07:30:20 GMT</pubDate><ttl>60</ttl><item><title>从经理的角度看技术债务【转】</title><link>http://www.blogjava.net/hankchen/archive/2012/08/29/386485.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Wed, 29 Aug 2012 01:43:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/08/29/386485.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/386485.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/08/29/386485.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/386485.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/386485.html</trackback:ping><description><![CDATA[<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">英文原文：<a style="font-size: 15px; color: rgb(27,139,224); line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal; text-decoration: none" href="http://www.infoq.com/articles/technical-debt-levison" data-mce-href="http://www.infoq.com/articles/technical-debt-levison">Technical Debt a Perspective for Managers</a></p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">作者：Mark Levison 译者：赖勤毅 发布于 2010年11月5日</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">现在已经到第十次迭代开发周期了，你的项目开发速度开始变慢。在之前的几个迭代周期中，团队没有像以前那样完成很多的&#8220;故事场景&#8221;（stories）。此外，最近在新的故事场景和回溯中却发现更多缺陷(bug)。项目经理知道，团队成员没有变，他们也花同样的时间工作。但是，客户会发问：&#8220;发生什么事情了？这个团队还在努力工作吗？&#8221;</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">很多敏捷团队的产品改进率为150-500%，可是你们的项目看起来却貌似只有20-40%左右的改进率。这到底是怎么回事呢？在此我们找不到什么大问题；相反，只是有无数的小问题。有时，这些只是一些为了方便而使用的捷径（开发人员没有时间去清理这些修改），有时开发人员仅仅是不熟悉这中语言。还有一些问题就是，代码跟灌木丛一样凌乱，需要大幅度的修整。所有这些都属于&#8220;技术债务&#8221;。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">什么是&#8220;技术债务&#8221;？</strong></p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">它就是&#8220;那些内在的事物，现在你不去解决，遗留下来（不干完），它就会阻碍未来开发&#8221;[<a style="font-size: 15px; color: rgb(27,139,224); line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal; text-decoration: none" href="http://c2.com/cgi/wiki?TechnicalDebt" target="_blank" data-mce-href="http://c2.com/cgi/wiki?TechnicalDebt">Ward Cunningham</a>]。 表面上，应用程序看起来质量很高且状况良好，但是这些问题却隐藏在下面。 QA（质量保证部门）甚至可能告诉你说，这个应用程序真是不错，几乎找不到缺陷，但是其中仍然存在&#8220;技术债务&#8221;，如果我们没有很好地管理并设法降低这些&#8220;技术债务&#8221;，那么，程序编写和维护的代价最终将会超过它对客户的价值。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">技术债务就像信用卡一样，会有很高的利息率，就如同给团队留下了大量的帐务开销。这种情况下，开销将会体现在时间花费和解决问题所需的努力上面。开发团队拖延债务的时间越长，所积累的利息就越多（会额外增加很多工作），付出的成本也就越高。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">另外，这还增加了实际的财务支出：开发团队处理技术债务所花费的时间，可以用在对团队有价值的其它工作上。同时，这些难读的代码引起的技术债务也让我们难以找到软件的缺陷。再且，理解代码所损失的时间还可以用来做其它更有价值的事情呢。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">我们为何要累积技术债务呢？</strong></p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">项目编码初期，不整理代码，不写单元测试，也不做测试驱动开发，整个团队粗制滥造出更多的&#8220;故事场景&#8221;。 这些问题通常都不会马上暴露出来，而循规蹈矩地编写代码往往需要更多的时间，特别是在早期阶段。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">技术债务来自哪里？</strong></p>
<ul style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px 0px 1.62em 2.5em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; padding-top: 0px; white-space: normal; list-style-type: square; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">没有经验的开发人员 &#8212;&#8212; 有些项目里面，编写Java/C#/Ruby的开发人员没有接受过培训，或者没有面向对象的观念。结果呢，他们会一直编写适用于他们曾经习惯的编程语言&#8212;&#8212;像Visual Basic等&#8212;&#8212;的代码。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">项目工期的压力 &#8212;&#8212; 那些来自于经理或客户的显性压力和其它一些潜在的压力。&#8220;我们承诺在以后迭代（发布）的故事场景中做到这些&#8221;。开发团队会发现他们不会交付这些承诺的发行版本（迭代版本），而是采取便捷的手段。&#8220;我们不得不把事情做完；我们无法承担修整代码所耗费的时间。如果这不是新特性/缺陷，那么就不用去做&#8221;。 不幸的是，这些观点还会得到管理人员的支持，特别是在管理层没有意识到这些成本的时候。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">凌乱而难读的代码。当一个方法或类中存在一些难读的代码，下一个开发人员继续这些工作的时候，就觉得他也没有必要迫使自己编写清晰的代码。所以，每次有人接手这些代码的时候，一小段脏乱的代码将会变成一大堆乱七八糟的代码。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">专业领域的代码 &#8212;&#8212; 往往有这样的观念：这些代码看起来就是很差劲，但是这不属于我的领域，所以我不能或不会改变它们。另外，对于修改专业领域的代码，开发人员也会觉得力不从心。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">过度复杂 &#8212;&#8212; 开发人员经常趋向于在需求之前设计解决方案。而且，很多情况下他们的代码没有找到正确的方向，还会写一些没有用处的代码。或者，这些代码没有真正地符合需求，因为代码在问题还没有完全明白的时候就已经写完了。还有另外一种情况，过度设计花费了额外的时间，产生的代码不是不能使用就是不符合项目的需求。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">糟糕的设计 &#8212;&#8212; 有些解决方案只是设计不佳。但在设计不好的代码上面继续扩展，而不去解决这个问题，会使问题更加恶化。</li></ul>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">解决问题</strong></p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">这个问题的并不是一下子可以解决的，解决方案需要通过几个迭代周期。并且，你需要耐心，并要从多个角度寻找解决途径。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">解决方案中的要点</p>
<ol style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px 0px 1.62em 2.5em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; padding-top: 0px; white-space: normal; list-style-type: decimal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">让开发人员接受语言方面的基本培训并教授他们面向对象的原理，从而把他们的理解力提升至入门阶段。要想既成功又有效的话，这需要花几个礼拜的时间培训，还需要有精通这方面的人员来跟进和支持这一系列培训。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">告诉开发人员和管理人员，当前的这些问题都是需要花费企业资金的。这点尤其重要，因为它会使解决这些问题的意义更加明确。你要清楚地告诉他们，管理人员会重视这些问题，并且已经开始着手偿还这些技术债务了。不断支持这些工作使之成为常规的原则之一，这样整个团队就会信任这个准则。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">提供一些培训，包括代码的坏味道，重构，单元测试和测试驱动开发等。还可以结合课堂会议，基于网络的材料和书籍来进行培训。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">在工作的时候，给他们一些空余时间去研究和练习他们的技能（一个礼拜两个小时应该是达成这个目标的最小的时间量）。练习的代码应该被丢弃，这样，他们就能无拘无束地尝试和试验一些事情，不管怎么样，他们不用在基础代码库上面进行练习。花点时间练习和研究应该是最有用的建议了。假如没有为他们提供空闲的时间，就压根不会发现真正合理的敏捷开发方式。这种组织方式也被称为&#8220;道场&#8221;-Dojo（更加详细的资料可以参考&nbsp;<a style="font-size: 15px; color: rgb(27,139,224); line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal; text-decoration: none" href="http://www.notesfromatooluser.com/2008/10/tdd-randori-session.html" target="_blank" data-mce-href="http://www.notesfromatooluser.com/2008/10/tdd-randori-session.html">TDD Randori Session</a><span class="Apple-converted-space">&nbsp;</span>和&nbsp;<a style="font-size: 15px; color: rgb(27,139,224); line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal; text-decoration: none" href="http://www.notesfromatooluser.com/2008/10/tdd-randori-workshop.html" target="_blank" data-mce-href="http://www.notesfromatooluser.com/2008/10/tdd-randori-workshop.html">TDD Randori Workshop</a>）。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">使用工具（静态分析，单元测试，持续集成，自动化可接受性测试）来帮助团队发现、减少和衡量他们的技术债务量。应该在团队层面利用这些度量数据，而不能分享给管理人员。团队成员需要知道，他们并不会因为对这些技术债务的度量而接受奖励或者遭受惩罚。如果我们把这些度量数据报告给管理层，并由管理层来追踪的话，开发人员很快会找到一些窍门来规避它们，这样就失去了本来应有的价值了。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">当人们完成了他们的培训或者在技术上取得了少许提升时，应该得到奖励。奖励可以是给予一次认可的表彰或者是一些小小的礼物，但不要直接给予现金奖励。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">每两个礼拜进行一次午餐聚会和一些关于技术方面的学习型会议。利用这些会议来促进开发成员之间的讨论。提供午餐可以提高参会人数。另外比较重要的是需要管理人员每次都来出席，这样就很清楚的表明他们也一直支持大家提高技术。</li><li style="font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">维护关于技术债务的备忘录 &#8211; 任何时候，如果开发人员发现一些无法立即应付的技术债务时，就需要填写一份技术债务登记卡。开发人员应该优先考虑这些技术债务，并花费10-15%的精力来偿还这些技术债务。大多数项目中，任何的一些小事都会使问题变得更加严重。</li></ol>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">基于这样的立场，你会发现问题，也会拥有机会。你的问题是：项目的基础代码一直在积累技术债务，但是这些债务已经开始下降了。然而，现在跟客户申请用于处理这些问题的资金会跟处理这些问题一样困难。你的机遇是：提高了开发人员的技能；清楚地表达了管理层对这项工作的支持，还有，软件的代码质量会不断改善，软件缺陷的数目也会不断减少。同时这也会很好的提高团队的整体开发能力。</p><img src ="http://www.blogjava.net/hankchen/aggbug/386485.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-08-29 09:43 <a href="http://www.blogjava.net/hankchen/archive/2012/08/29/386485.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>游戏数据平台规划</title><link>http://www.blogjava.net/hankchen/archive/2012/08/29/386482.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Wed, 29 Aug 2012 01:40:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/08/29/386482.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/386482.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/08/29/386482.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/386482.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/386482.html</trackback:ping><description><![CDATA[<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">总的来说，游戏数据平台的工作内容就是围绕<strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">&#8220;数据&#8221;</strong><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">+</strong><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">&#8220;服务&#8221;</strong>。现在专注独立运营游戏之后，数据平台的重要性就更加突出了。但是，目前的各个系统还不能很好地支撑这次业务上的重大转变，我们需要一个全局的思维来重新规划整个数据平台，包括怎么收集和处理数据、怎么更好地对外提供服务等等。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">一、从数据的角度来看，数据包括：收集、存储、分析（挖掘）、展示、提取。</strong>兼顾目前的系统，收集、存储、挖掘、提取，这几个环节的系统需要加强。特别是收集和提取这块。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">由于历史的原因，加之没有系统规划过，之前的数据收集来源比较零散，这样导致分析数据时需要从各个地方来同步数据。当业务多了之后，这些乱七八糟的来源就够让人头疼了。这次规划的一个重点就是，建立一套游戏数据收集系统。游戏分析涉及的数据（日志类）都从这个系统中获取，不再单独分析各自的业务数据。业务系统采用数据上报的方式，按照固定的格式来上报数据。例如：登录、注册、充值、消耗等等。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">采用这套游戏数据收集系统，还有一个重要的原因。现在我们的游戏都是独立运营游戏（我们是甲方），那么我们就可以采用类似腾讯的办法，事先定义好数据规范，要求游戏方按照我们的格式上报相关数据给我们。这样我们的数据分析系统就可以做到非常通用。不管接多少游戏，分析和展示系统都统一。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">数据提取，这里主要是指临时数据。这是我们这边的一个顽疾。临时数据其实包括两个部分：数据来源和数据分析。一个常见的需求就是：分析一堆帐号（数据来源）的后续行为（数据分析）。因为临时数据的业务规则复杂，并且数据来源千奇百怪，之前采用过全手工、全自动的方式来实现，但是都失败了。现在想到的一个解决方案就是，基于游戏数据收集系统之上，再开发一个临时数据分析系统。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">游戏数据收集系统统一了数据的来源和格式，方便存储和提取原始数据。这基本解决了数据来源的问题。临时数据分析系统，可以事先实现常见的分析逻辑（例如：登录、留存、保有、付费人数、付费金额、消耗等等），然后采用过滤器模式或包装器模式来实现。这其实是一个半自动化的方案。系统的用户是开发人员和产品人员。开发完这套系统应该可以省掉60%以上的临时数据任务。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">另外，只要数据都是来自这套游戏数据收集系统，存储、分析，包括挖掘都会简单很多。存储都会在HDFS上，分析基本都使用Hive，挖掘用Mahout。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">游戏数据挖掘是需要单独发展的一块业务，特别是现在专注独立运营之后。目前这块我们还需要更多的时间来积累经验。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px"><strong style="font-weight: bold; font-size: 15px; line-height: 1.625; font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; font-variant: normal">二、从服务的角度来看，服务包括：服务框架、服务管理、服务监控</strong>。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">服务也是一个大范畴。服务化框架、页面登录服务器、GM接口，这些都属于服务相关的内容。兼顾目前的系统，这些内容都已经基本实现，这次只是做比较大的版本改进。</p>
<p style="margin-bottom: 1.62em; word-spacing: 0px; font: 300 15px/24px 'Helvetica Neue', Helvetica, Arial, sans-serif; text-transform: none; color: rgb(51,51,51); text-indent: 0px; white-space: normal; letter-spacing: normal; orphans: 2; widows: 2; -webkit-text-size-adjust: auto; -webkit-text-stroke-width: 0px">（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong><font color="#444444">http://www.blogjava.net/hankchen</font></strong></a>）</p><img src ="http://www.blogjava.net/hankchen/aggbug/386482.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-08-29 09:40 <a href="http://www.blogjava.net/hankchen/archive/2012/08/29/386482.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>线上应用故障排查系列</title><link>http://www.blogjava.net/hankchen/archive/2012/05/09/377738.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Wed, 09 May 2012 12:35:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/05/09/377738.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/377738.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/05/09/377738.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/377738.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/377738.html</trackback:ping><description><![CDATA[<p><font style="background-color: #cce8cf"><font style="background-color: #cce8cf">线上应用的故障排查能力是一个高级软件开发人员的必修课，也最能体现一个技术人员的工作经验和能力。所以，这也是我面试别人的一个必考题目之一。</font></p> <p><font style="background-color: #cce8cf">我打算把自己在这方面的实际经验写成系列文章，主要包括下面这些内容：</font></p> <p></font></p> <p><font color="#0000ff">1、</font><a href="http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html"><font color="#0000ff">线上应用故障排查之一：高CPU占用</font></a></p> <p><font color="#0000ff">2、</font><a href="http://www.blogjava.net/hankchen/archive/2012/05/09/377736.html"><font color="#0000ff">线上应用故障排查之二：高内存占用</font></a></p> <p>3、线上应用故障排查之三：高I/O占用，包括磁盘I/O、网络I/O、数据库I/O等。</p> <p>4、线上应用故障排查之四：程序僵死</p> <p>敬请期待！</p> <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong></p> <img src ="http://www.blogjava.net/hankchen/aggbug/377738.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-05-09 20:35 <a href="http://www.blogjava.net/hankchen/archive/2012/05/09/377738.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>线上应用故障排查之二：高内存占用</title><link>http://www.blogjava.net/hankchen/archive/2012/05/09/377736.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Wed, 09 May 2012 12:21:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/05/09/377736.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/377736.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/05/09/377736.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/377736.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/377736.html</trackback:ping><description><![CDATA[<p>前一篇介绍了<a href=" http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html"><font color="#0000ff">线上应用故障排查之一：高CPU占用</font></a>，这篇主要分析高内存占用故障的排查。</p> <p>搞Java开发的，经常会碰到下面两种异常：</p> <p>1、java.lang.OutOfMemoryError: PermGen space <p>2、java.lang.OutOfMemoryError: Java heap space <p>要详细解释这两种异常，需要简单重提下Java内存模型。</p> <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong></p> <p><strong>Java内存模型</strong>是描述Java程序中各变量（实例域、静态域和数组元素）之间的关系，以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节。</p> <p>在Java虚拟机中，内存分为三个代：新生代（<strong>New</strong>）、老生代（<strong>Old</strong>）、永久代（<strong>Perm</strong>）。</p> <p>（1）新生代New：新建的对象都存放这里</p> <p>（2）老生代Old：存放从新生代New中迁移过来的生命周期较久的对象。新生代New和老生代Old共同组成了堆内存。</p> <p>（3）永久代Perm：是非堆内存的组成部分。主要存放加载的Class类级对象如class本身，method，field等等。</p> <p>如果出现java.lang.OutOfMemoryError: Java heap space异常，说明Java虚拟机的堆内存不够。原因有二：</p> <p>（1）Java虚拟机的堆内存设置不够，可以通过参数-Xms、-Xmx来调整。</p> <p>（2）代码中创建了大量大对象，并且长时间不能被垃圾收集器收集（存在被引用）。</p> <p>如果出现java.lang.OutOfMemoryError: PermGen space，说明是Java虚拟机对永久代Perm内存设置不够。</p> <p>一般出现这种情况，都是程序启动需要加载大量的第三方jar包。例如：在一个Tomcat下部署了太多的应用。</p> <p>&nbsp;</p> <p>从代码的角度，软件开发人员主要关注java.lang.OutOfMemoryError: Java heap space异常，减少不必要的对象创建，同时避免内存泄漏。</p> <p>现在以一个实际的例子分析内存占用的故障排查。</p> <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/11d51d896b37_1287F/2G19(%7B7(0%7DN(FIL09LH175N_2.jpg"><img title="2G19({7(0}N(FIL09LH175N" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="228" alt="2G19({7(0}N(FIL09LH175N" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/11d51d896b37_1287F/2G19(%7B7(0%7DN(FIL09LH175N_thumb.jpg" width="535" border="0"></a> <p>通过top命令，发现PID为9004的Java进程一直占用比较高的内存不释放（24.7%），出现高内存占用的故障。 <p>想起上一篇<a href=" http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html"><font color="#0000ff">线上应用故障排查之一：高CPU占用</font></a>介绍的PS命令，能否找到具体是哪个的线程呢？ <p><font color="#ff0000">ps -mp 9004 -o THREAD,tid,time,rss,size,%mem</font> <p><font color="#ff0000"><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/11d51d896b37_1287F/1_2.jpg"><img title="1" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="219" alt="1" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/11d51d896b37_1287F/1_thumb.jpg" width="539" border="0"></a> </font></p> <p>遗憾的是，发现PS命令可以查到<strong>具体进程</strong>的CPU占用情况，但是不能查到<strong>一个进程下具体线程</strong>的内存占用情况。 <p>&nbsp; <p>只好寻求其他方法了，幸好Java提供了一个很好的内存监控工具：<font color="#ff0000">jmap命令</font> <p>jmap命令有下面几种常用的用法： <p>•jmap [pid] <p>•jmap -histo:live [pid] &gt;a.log <p>•jmap -dump:live,format=b,file=xxx.xxx [pid] <p>用得最多是后面两个。其中，jmap -histo:live [pid] 可以查看当前Java进程创建的活跃对象数目和占用内存大小。 <p>jmap -dump:live,format=b,file=xxx.xxx [pid] 则可以将当前Java进程的内存占用情况导出来，方便用专门的内存分析工具（例如：MAT）来分析。 <p>这个命令对于分析是否有内存泄漏很有帮助。具体怎么使用可以查看本博的另一篇文章：<a href="http://www.blogjava.net/hankchen/archive/2012/03/21/372389.html"><font color="#0000ff">利用Eclipse Memory Analyzer Tool（MAT）分析内存泄漏</font></a> <p><strong></strong>&nbsp; <p><strong>这里详细介绍下jmap -histo:live [pid] 命令：</strong> <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/11d51d896b37_1287F/1_4.jpg"><img title="1" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="380" alt="1" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/11d51d896b37_1287F/1_thumb_1.jpg" width="482" border="0"></a>  <p>从上图可以看出，int数组、constMethodKlass、methodKlass、constantPoolKlass都占用了大量的内存。 <p>特别是占用了大量内存的int数组，需要仔细检查相关代码。 <p>&nbsp; <p><strong>最后，总结下排查内存故障的方法和技巧有哪些：</strong> <p>1、top命令：Linux命令。可以查看实时的内存使用情况。&nbsp;&nbsp; <p>2、jmap -histo:live [pid]，然后分析具体的对象数目和占用内存大小，从而定位代码。 <p>3、jmap -dump:live,format=b,file=xxx.xxx [pid]，然后利用MAT工具分析是否存在内存泄漏等等。 <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong><img src ="http://www.blogjava.net/hankchen/aggbug/377736.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-05-09 20:21 <a href="http://www.blogjava.net/hankchen/archive/2012/05/09/377736.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>线上应用故障排查之一：高CPU占用</title><link>http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Wed, 09 May 2012 12:20:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/377735.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/377735.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/377735.html</trackback:ping><description><![CDATA[<p>一个应用占用CPU很高，除了确实是计算密集型应用之外，通常原因都是出现了死循环。</p> <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong></p> <p>以我们最近出现的一个实际故障为例，介绍怎么定位和解决这类问题。</p> <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/clip_image002_2.jpg"><img title="clip_image002" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="216" alt="clip_image002" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/clip_image002_thumb.jpg" width="600" border="0"></a></p> <p>根据top命令，发现PID为28555的Java进程占用CPU高达200%，出现故障。</p> <p>通过ps aux | grep PID命令，可以进一步确定是tomcat进程出现了问题。但是，怎么定位到具体线程或者代码呢？</p> <p><b>首先显示线程列表</b><b>:</b>  <p><font color="#ff0000">ps -mp pid -o THREAD,tid,time</font>  <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/1.png"><img title="1" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="291" alt="1" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/1_thumb.png" width="469" border="0"></a> </p> <p>找到了耗时最高的线程28802，占用CPU时间快两个小时了！  <p><b>其次将需要的线程ID转换为16进制格式：</b>  <p><font color="#ff0000">printf "%x\n" tid</font>  <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/2.png"><img title="2" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="55" alt="2" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/2_thumb.png" width="372" border="0"></a>  <p><b>最后打印线程的堆栈信息：</b>  <p><font color="#ff0000">jstack pid |grep tid -A 30</font>  <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/3.png"><img title="3" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="401" alt="3" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/CPU_121DA/3_thumb.png" width="927" border="0"></a>  <p>找到出现问题的代码了！  <p>现在来分析下具体的代码：ShortSocketIO.readBytes(ShortSocketIO.java:106)</p> <p>ShortSocketIO是应用封装的一个用短连接Socket通信的工具类。readBytes函数的代码如下：</p> <p>public byte[] readBytes(int length) throws IOException { <p>&nbsp;&nbsp;&nbsp; if ((this.socket == null) || (!this.socket.isConnected())) { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new IOException("++++ attempting to read from closed socket"); <p>&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp; byte[] result = null; <p>&nbsp;&nbsp;&nbsp; ByteArrayOutputStream bos = new ByteArrayOutputStream(); <p>&nbsp;&nbsp;&nbsp; if (this.recIndex &gt;= length) { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bos.write(this.recBuf, 0, length); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte[] newBuf = new byte[this.recBufSize]; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (this.recIndex &gt; length) { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.arraycopy(this.recBuf, length, newBuf, 0, this.recIndex - length); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recBuf = newBuf; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recIndex -= length; <p>&nbsp;&nbsp;&nbsp; } else { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int totalread = length; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (this.recIndex &gt; 0) { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; totalread -= this.recIndex; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bos.write(this.recBuf, 0, this.recIndex); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recBuf = new byte[this.recBufSize]; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recIndex = 0; <p>&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp; int readCount = 0; <p>&nbsp;&nbsp;&nbsp; <font color="#ff0000">while (totalread &gt; 0) {</font> <p><font color="#ff0000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ((readCount = this.in.read(this.recBuf)) &gt; 0) {</font> <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (totalread &gt; readCount) { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bos.write(this.recBuf, 0, readCount); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recBuf = new byte[this.recBufSize]; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recIndex = 0; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } else { <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bos.write(this.recBuf, 0, totalread); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; byte[] newBuf = new byte[this.recBufSize]; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.arraycopy(this.recBuf, totalread, newBuf, 0, readCount - totalread); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recBuf = newBuf; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.recIndex = (readCount - totalread); <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; totalread -= readCount; <p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <p>&nbsp;&nbsp; } <p>} <p>问题就出在标红的代码部分。如果this.in.read()返回的数据小于等于0时，循环就一直进行下去了。而这种情况在网络拥塞的时候是可能发生的。 <p>至于具体怎么修改就看业务逻辑应该怎么对待这种特殊情况了。 <p>&nbsp; <p><strong>最后，总结下排查CPU故障的方法和技巧有哪些：</strong> <p>1、top命令：Linux命令。可以查看实时的CPU使用情况。也可以查看最近一段时间的CPU使用情况。 <p>2、PS命令：Linux命令。强大的进程状态监控命令。可以查看进程以及进程中线程的当前CPU使用情况。属于当前状态的采样数据。 <p><font color="#ff0000">3、jstack：Java提供的命令。可以查看某个进程的当前线程栈运行情况。根据这个命令的输出可以定位某个进程的所有线程的当前运行状态、运行代码，以及是否死锁等等。</font> <p>4、pstack：Linux命令。可以查看某个进程的当前线程栈运行情况。 <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong></p><img src ="http://www.blogjava.net/hankchen/aggbug/377735.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-05-09 20:20 <a href="http://www.blogjava.net/hankchen/archive/2012/05/09/377735.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jstack -F 命令在Linux 64位机器报错：get_thread_regs failed for a lwp</title><link>http://www.blogjava.net/hankchen/archive/2012/04/09/373640.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Mon, 09 Apr 2012 08:04:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/04/09/373640.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/373640.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/04/09/373640.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/373640.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/373640.html</trackback:ping><description><![CDATA[<p><font style="background-color: #c7edcc" face="Verdana">最近发现一个线上项目运行一段时间之后会僵死。程序不报任何异常，占有系统资源也都正常，就是对外提供不了服务了。</font></p> <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong> <p><font style="background-color: #c7edcc" face="Verdana">根据经验，应该是程序有死锁情况，于是在线上运行“jstack –F &lt;pid&gt;”命令，想把线程堆栈dump下来。</font></p> <p><font style="background-color: #c7edcc" face="Verdana">但是，发现这个命令老是报下面的错误：</font></p> <p>Thread 27316: (state = BLOCKED)<br>Error occurred during stack walking:<br>sun.jvm.hotspot.debugger.DebuggerException: sun.jvm.hotspot.debugger.DebuggerException: <font color="#ff0000">get_thread_regs failed for a lwp<br></font></p> <p><font color="#000000">线上环境是：</font></p> <p>Linux 2.6.18-194.el5 x86_64 x86_64 x86_64 GNU/Linux</p> <p>java version "1.6.0_21"<br>Java(TM) SE Runtime Environment (build 1.6.0_21-b04)<br>Java HotSpot(TM) 64-Bit Server VM (build 20.6-b01, mixed mode)</p> <p>经过分析发现，这是JDK6u23之前版本的一个Bug，将JDK升级到最新版本（1.6.0_31）就可以解决问题了。</p> <p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/Windows-Live-Writer/jstack--F-Linux-64get_thread_regs-failed_E24F/%7BB0E1C5D8-CE33-4940-B416-2E75003A59FA%7D.png"><img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="{B0E1C5D8-CE33-4940-B416-2E75003A59FA}" border="0" alt="{B0E1C5D8-CE33-4940-B416-2E75003A59FA}" src="http://www.blogjava.net/images/blogjava_net/hankchen/Windows-Live-Writer/jstack--F-Linux-64get_thread_regs-failed_E24F/%7BB0E1C5D8-CE33-4940-B416-2E75003A59FA%7D_thumb.png" width="498" height="199"></a></p><font color="#ff0000"></font> <p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong></p><img src ="http://www.blogjava.net/hankchen/aggbug/373640.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-04-09 16:04 <a href="http://www.blogjava.net/hankchen/archive/2012/04/09/373640.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>给开发维护大型项目开发者的建议 zz</title><link>http://www.blogjava.net/hankchen/archive/2012/04/08/373568.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Sun, 08 Apr 2012 01:02:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/04/08/373568.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/373568.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/04/08/373568.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/373568.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/373568.html</trackback:ping><description><![CDATA[<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">假设你是正在开发和维护一个包含2000个类并使用了很多框架的Java开发人员。你要如何理解这些代码？在一个典型的Java企业项目小组中，大部分能够帮你的高级工程师看起来都很忙。文档也很少。你需要尽快交付成果，并向项目组证明自己的能力。你会如何处理这种状况？这篇文章为开始一个新项目的Java开发者提供了一些建议。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px"><strong style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px">0. 不要试图一下子搞懂整个项目</strong></p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">好好考虑一下，为什么理解项目代码是第一位的？大部分情况是你被要求修复一个bug或者加强系统已有功能。你要做的第一件事情不是理解整个项目的架构。当对项目进行维护时，这样（理解整个项目架构）可能会对你造成巨大的压力。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">即便是有着10年可靠编程经验的Java开发者可能也没有理解项目的核心工作机制，尽管他们可能已经在这个项目工作超过一年（假设他们并非原始开发人员）。比如，对于认证机制或事务管理机制。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">他们是怎么做的？他们对于自己负责的部分非常了解，并且能够交付价值给小组。每天的交付价值远比了解一些以后还不确定有没有的东西重要的多。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px"><strong style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px">1. 关注于尽快交付价值</strong></p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">那我是否定了你对于项目架构理解的热情了么？完全不。我只是要求你尽早的交付价值，一旦你开始一个项目，搭建了开发环境，你就不应该花一两周时间才交付什么，无论他的规模大小。假如你是一个有经验的<span class="wp_keywordlink" style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"><a title="程序员的本质" style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; color: #0b5c77; padding-top: 0px; text-decoration: none" href="http://blog.jobbole.com/821/">程序员</a></span>却两周都没有任何交付，你的经理怎么会知道你是真的在工作还是在看新闻。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">所以交付可以使大家都轻松起来。不要认为你能够做有价值的交付前必须理解整个项目。这是完全错误的。加一段javascript的验证代码对业务就很有价值，经理能够通过你的交付达到对你的信任。这样能够向上级领导证明你的贡献以及员工价值。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">日复一日，在你不断修复bug及增强功能之后，就能够慢慢开始理解项目架构。不要低估对系统方方面面理解时需要花费的时间。花3-4天理解认证机制，2-3天理解事物管理。这些都是依靠之前的相似项目的经历，但关键还是要花时间才能透彻的理解。要在日常工作中挤出时间，不要向经理要求特定的时间来做这些。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">找找项目是否有一些不断维护的单元测试用例。有效的单元测试用例是理解大型项目代码的很好途径。单元测试能够帮助理解代码片段，包括一个单元的外部接口（单元如何被调用以及返回内容）及其内部实现（调试单元测试比调试整个实际用例简单许多）。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">你如果能够很好的理解一些内容，写一些笔记，或者画一些类图、时序图、数据模型图，以便你或日后其他的开发者维护。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px"><strong style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px">2. 维护大型项目所必须的技能</strong></p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">你能从事当前的工作，必然已经具有良好的java技术。我们来谈谈能够让你在新项目中良好表现的其他技能。大部分时间，你在项目中的任务是修复bug和增强功能。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">有两项很重要的技能能够协助你维护大型项目代码。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px"><strong style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px">2.1 能够迅速发现需要的类</strong></p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">在任何维护活动中，无论是修复bug或增强功能，第一个动作就是识别出当前修复或增强的用例中调用的类。当你定位到需要修复或增强的类/方法，就已经完工了一半。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px"><strong style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px">2.2 能够分析变更的影响</strong></p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">当你在完成必要的修改或增强工作后，最重要的就是要确认你的修改没有破坏代码的其他部分。你要用你的java技术及对其他框架的理解找出变更可能影响的部分。下面有两个简单的例子详细描述了最后提及的情况：</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">a）当类A的equals()方法变更后，调用一个保护A实例的List的contains()方法时就会被影响到。若Java知识不够，很难考虑到这样的影响。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">b）在一个web项目中，我们假设&#8220;user id&#8221;保存在session中。一个新入程序员可能在&#8220;user id&#8221;中加入一些信息作为bug修复的方法，但是却不知道会影响到那些关联&#8220;user id&#8221;的用例。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">当你提高了如上两个技能，尽管你对项目不是非常了解，但大部分的维护任务会变得简单很多。若你修复一个bug，你会定位并修复这个bug，并且保证变更不会破坏项目的其他部分。若你增强或加入一个特性，基本上你只需要模仿现有的特性使用相似的设计。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">在一个在线银行项目中，为什么&#8220;查看账户摘要&#8221;和&#8220;查看交易历史&#8221;的设计需要巨大的差别呢？如果你理解了&#8220;查看账户摘要&#8221;的设计，完全可以模仿开发出&#8220;查看交易历史&#8221;的功能。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">就修复bug和增强来说，你不必完全理解所有2000个类的工作内容和代码如何运行来推动系统。你若有上面的技能，就能很快定位需要修改的代码的部分，使用良好的java和框架技能修复，保证变更不会破坏项目的其他部分并交付，尽管你可能只知道一小部分项目的设计。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px"><strong style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px">3. 使用工具找到需要的变更内容以及变更产生的影响</strong></p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">继续我们尽快交付的主题，你应当寻找那些能够通过尽量少的了解项目但能帮助你尽快实施交付的工具作为辅助。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px"><strong style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px">3.1 迅速发现需要变更内容的工具</strong></p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">无论是修复bug还是系统增强，首先都要找到该用例调用的你需要修改的类及方法。基本有两种方式理解一个用例的工作方式，静态代码分析和运行时分析。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">源码分析统计扫描所有代码并且展示类之间的关系。市场上有很多设备与工具。比如：Architexa， AgileJ， UModel， Poseidon等。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">所有的静态代码分析工具缺点在于无法确切展示用例中类或方法的运行时调用情况。因此Java新加入了特性，如回调机制（callback patterns）。如静态分析工具无法推断出当页面提交按钮被点击时哪个Servlet被调用了。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">运行时分析工具能够展示类和方法在用例运行时的状态。工具包括：MaintainJ， Diver，jSonde，Java Call Tracer等。这些工具可以捕获运行时的堆栈状态，并以此为一个用例生成序列图和类图。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">序列图展示了该用例在运行时所有调用的方法。若你在修复一个bug，那这个bug很可能就是这些被调用的方法之一。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">若你在增强已有功能，利用序列图理解调用流程然后再修改。可能是新增一个验证，修改DAO等。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">若你在新增功能，找到一些相似的特性，利用序列图理解调用流程然后模仿开发新功能。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">要小心挑选运行时分析工具。信息过多是这类工具的主要问题。选择一些提供简单过滤无效信息并能够方便的查看各种视图的工具。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px"><strong style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px">3.2 迅速发现需要变更内容的工具</strong></p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">若单元测试有效，可以通过运行单元测试发现变更有没有破坏其他测试用例。有效维护并且覆盖大型企业应用的单元测试还是比较少的。下面有一些针对该情况的工具。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">仍然是有两种技术静态代码分析和运行时分析可以使用。市场中有很多静态代码分析工具可用。如：Lattix, Structure101, Coverity, nWire and IntelliJ&#8217;s DSM。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">给定一个变更后的类，上述工具均可识别对该类存在依赖的类的集合。开发者需要根据这些信息&#8220;猜测&#8221;可能产生影响的用例，因为这些工具无法展示运行时类之间的调用关系。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">市场上的可以用于运行时影响分析的工具并不多，除了MaintainJ。MaintainJ先捕获在一个用例中调用的所有类和方法。当所有用例的上述信息都被捕获之后，就很容易发现类的变更对用例的影响。MaintainJ能够有效工作的前置条件就是项目的所有用例都应当先运行一遍，以便能够获得运行时的依赖关系。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">总之，目前你在迅速准确分析变更影响方面，还是可以从工具中获得有限的帮助。首先根据需要实施一些影响分析，然后根据自己或小组其他高级成员评审来判断变更的影响。你可能需要上面提到的工具对你的判断进行反复确认。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px"><strong style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px">4. 对上述内容的两个忠告</strong></p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px"><strong style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px">4.1 不要降低代码质量</strong></p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">为了快速交付，所以没有全盘理解架构，但绝不能以降低代码质量为条件。下面是一些你可能因为只考虑快速交付而引发的代码质量问题。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">因为修改代码涉及到很多的依赖，所以新增代码相对而言风险较小。例如，有5个用例都调用了某个方法。为了改进某个用例，你需要修改这个方法的实现。最简单的做法就是复制这个方法，重命名，然后在改进的用例中调用新方法。千万不要这么做。代码冗余绝对是非常有害的。尝试对方法进行包装或者重写，甚至是直接修改，然后重新测试所有用例，通常停下来想一想，然后亲手去实施，是一个比较好的方式。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">&nbsp;另一个例子是将&#8220;private&#8221;方法改为&#8220;public&#8221;，使得别的类也可以调用。尽量不要将非必须的部分暴露出来。假如为了更好的设计需要<span class="wp_keywordlink" style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px"><a class="external" title="重构" style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; color: #0b5c77; padding-top: 0px; text-decoration: none" href="http://www.amazon.cn/gp/product/B003BY6PLK/ref=as_li_qf_sp_asin_il_tl?ie=UTF8&amp;tag=vastwork-23&amp;linkCode=as2&amp;camp=536&amp;creative=3200&amp;creativeASIN=B003BY6PLK" target="_blank">重构</a></span>，就应当着手去做。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">大部分应用都有确定的结构和模式来实施。修复或增强程序时，确认你没有偏离这样的模式。若对约定不确定，请其他的高级开发者来审核你的变更。若你必须做一些违背约定的实施，尽量放置于一个规模较小的类中（一个200行代码的类中的私有函数应当不会影响应用的整体设计）</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px"><strong style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px">4.2 不要停止深入理解项目架构</strong></p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">按照文章列出的方式，假设你能够在对项目了解较少的情况下进行交付并以此持续下去，可能你会停止对项目架构的深入了解。这样从长远角度来说对你的职业生涯没有帮助。当你的经验增加时，你应当承担比较大的模块任务。如构建一个完整的新特性或者修改项目的一些基础设计等较大的改进。当你能够做这些改进时，你对项目的整体架构应该相当了解。文中列举的方法是让你在最短的时间内提升自己，而不是阻止你完整理解整个项目。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px"><strong style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; padding-top: 0px">5. 结论</strong></p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">整篇文章集中在对项目进行必要了解的前提下进行快速交付。你可以在不降低代码质量的前提下这么做。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">若修复一个bug，迅速定位并修复。有必要可以使用运行时分析工具。若新增一个特写，可以寻找相似特写，理解流程（有必要使用工具）并编写。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">或许这些听起来很简单，但是实用吗？当然。但前提是你有良好的java技术以及对框架足够了解才能先修改代码，然后对变更影响进行分析。对变更影响的分析比实施变更需要更多的技巧。你可能需要高级开发人员协助你分析变更影响。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">大约有50%的IT可操作预算用于简单的bug修复和功能增强。根据文中的建议，对于维护活动中的经费的节省应当还是很有帮助的。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">作者 Choudary Kothapalli 也是 MaintainJ 项目的建立者。</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">英文原文：<a class="external" style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; color: #0b5c77; padding-top: 0px; text-decoration: none" href="http://www.infoq.com/articles/tips-to-developers-starting-on-large-apps" target="_blank">Choudary Kothapalli</a>&nbsp; &nbsp; 本文由 &nbsp;<a style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; color: #0b5c77; padding-top: 0px; text-decoration: none" href="http://blog.jobbole.com/16526/" target="_blank">陈晨</a>&nbsp;编译并投稿于<a style="padding-right: 0px; padding-left: 0px; padding-bottom: 0px; margin: 0px; color: #0b5c77; padding-top: 0px; text-decoration: none" href="http://www.jobbole.com/" target="_blank">伯乐</a>在线</p>
<p style="padding-right: 0px; padding-left: 0px; font-size: 14px; padding-bottom: 10px; color: #383838; text-indent: 2em; line-height: 22px; padding-top: 0px">转载自伯乐在线&nbsp;<a href="http://blog.jobbole.com/16526/"><font color="#108ac6">http://blog.jobbole.com/16526/</font></a></p><img src ="http://www.blogjava.net/hankchen/aggbug/373568.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-04-08 09:02 <a href="http://www.blogjava.net/hankchen/archive/2012/04/08/373568.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用Eclipse Memory Analyzer Tool（MAT）分析内存泄漏</title><link>http://www.blogjava.net/hankchen/archive/2012/03/21/372389.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Wed, 21 Mar 2012 09:52:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2012/03/21/372389.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/372389.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2012/03/21/372389.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/372389.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/372389.html</trackback:ping><description><![CDATA[<font style="background-color: #cce8cf" face="Verdana"></font>
<p>&nbsp; 利用Hadoop分析BHO上报日志时，发现很多日志文件会出现下面的错误： 
<p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image002_2.jpg"><img title="clip_image002" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="69" alt="clip_image002" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image002_thumb.jpg" width="608" border="0" /></a></p>
<p>&nbsp; 即在map结束的时候抛出Java堆栈溢出异常！</p>
<p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong> 
<p><strong></strong>&nbsp; 首先设置下面的参数： 
<p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image004_2.jpg"><img title="clip_image004" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="254" alt="clip_image004" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image004_thumb.jpg" width="606" border="0" /></a></p>
<p>发现还是解决不了问题。开始怀疑代码问题，于是进行了一系列的优化：</p>
<p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image006_2.jpg"><img title="clip_image006" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="169" alt="clip_image006" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image006_thumb.jpg" width="599" border="0" /></a> 
<p>主要的优化是，在map和reduce程序中，重用key和value对象。但是发现还是解决不了问题。并且mapred.child.java.opts设置3G也无济于事。 
<p>没有办法，只好继续找原因。最后发现一个规律：报这个异常出错的日志不一定是最大的日志。 
<p>只好使用最后一招了，直接分析报错时的Java堆内存情况！<br /><br />再次出现异常的时候，把集群里面所有的机器的Hadoop进程的堆内存导出来分析！ 
<p>主要是下面的几类进程： 
<p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image008_2.jpg"><img title="clip_image008" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="215" alt="clip_image008" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image008_thumb.jpg" width="251" border="0" /></a> 
<p>同时，关注每台机器的top命令输出，可以从内存的使用情况中发现主要是哪台机器出问题！然后重点导出这台机器的Java堆参数！ 
<p><span style="color: red">jmap -dump:live,format=b,file=heapt0923</span><span style="color: red">.bin pid </span>
<p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image010_2.jpg"><img title="clip_image010" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="74" alt="clip_image010" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image010_thumb.jpg" width="509" border="0" /></a> 
<p>剩下来的就是把/data/bhopid_output/heapt0923.bin这个文件复制到本地，利用Eclipse Memory Analyzer Tool 进行分析！ 
<p>下面是分析的结果： 
<p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image012_2.jpg"><img title="clip_image012" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="288" alt="clip_image012" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image012_thumb.jpg" width="652" border="0" /></a> 
<p>发现有两个内存泄漏的情况： 
<p>1、 
<p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image014_2.jpg"><img title="clip_image014" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="158" alt="clip_image014" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image014_thumb.jpg" width="642" border="0" /></a></p>
<p>2、</p>
<p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image016_2.jpg"><img title="clip_image016" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="173" alt="clip_image016" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image016_thumb.jpg" width="638" border="0" /></a></p>
<p><span style="color: red">找到根本原因：说明日志文件有这样很大的空记录导致的！</span>在代码中把这些记录忽略掉即可！</p>
<p><a href="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image002%5B11%5D.jpg"><img title="clip_image002[11]" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="138" alt="clip_image002[11]" src="http://www.blogjava.net/images/blogjava_net/hankchen/WindowsLiveWriter/EclipseMemoryAnalyzerToolMAT_FC20/clip_image002%5B11%5D_thumb.jpg" width="640" border="0" /></a></p>
<p>&nbsp;</p>
<p>（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）<br /><br /></strong></p><img src ="http://www.blogjava.net/hankchen/aggbug/372389.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2012-03-21 17:52 <a href="http://www.blogjava.net/hankchen/archive/2012/03/21/372389.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>如何学好java---JavaIDC技术总监给新进技术人员的建议 zz</title><link>http://www.blogjava.net/hankchen/archive/2010/05/14/320962.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Fri, 14 May 2010 03:12:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2010/05/14/320962.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/320962.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2010/05/14/320962.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/320962.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/320962.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp; 在开始之前有必要再讨论一下J2ME，J2SE，J2EE这些概念。J2ME，The Micro Edition of the Java 2 Platform。主要用于嵌入式Java，如手机，PDA等等。J2SE，Java 2 Platform，Standard Edition，我们通常所说的JDK(Java Development Kit)包含在此，是J2EE的基础。J2EE，Java...&nbsp;&nbsp;<a href='http://www.blogjava.net/hankchen/archive/2010/05/14/320962.html'>阅读全文</a><img src ="http://www.blogjava.net/hankchen/aggbug/320962.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2010-05-14 11:12 <a href="http://www.blogjava.net/hankchen/archive/2010/05/14/320962.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>工作感悟---系统优化经历</title><link>http://www.blogjava.net/hankchen/archive/2010/01/21/310439.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Thu, 21 Jan 2010 13:57:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2010/01/21/310439.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/310439.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2010/01/21/310439.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/310439.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/310439.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;刚刚优化完一个系统，这个系统是自己一年前参与开发的。早就想优化它，一直没有时间。这次有机会终于动真家伙了。<br />
&nbsp;&nbsp;&nbsp;&nbsp;看到自己一年以前写的代码，感觉很恶心。当时完全是为了完成任务，赶时间做出来的。当然，技术也有很多不成熟的地方。
<p>　　<br />
&nbsp;&nbsp;&nbsp;&nbsp;优化的地方涉及：</p>
<p>　　1.减少重复代码，保证功能修改只会发生在一个地方。</p>
<p>　　2.优化界面，布局更加合理，美观。</p>
<p>　　3.优化SQL语句，提高性能。</p>
<p>　　4.JS文件的整合，公用的函数提取出来成为单独的文件，减少HTTP请求的次数（浏览器缓存），同时也减少当前页面的大小。</p>
<p>　　5.加了缓存模块</p>
<p>　　经过这些优化之后，系统性能提升不少。最重要的是，系统的可维护性大大地提高了。相信以后增加功能不会那么累了。看来连续两个晚上的加班时值得的。</p>
<img src ="http://www.blogjava.net/hankchen/aggbug/310439.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2010-01-21 21:57 <a href="http://www.blogjava.net/hankchen/archive/2010/01/21/310439.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>工作感悟--技术</title><link>http://www.blogjava.net/hankchen/archive/2010/01/15/309622.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Thu, 14 Jan 2010 16:11:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2010/01/15/309622.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/309622.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2010/01/15/309622.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/309622.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/309622.html</trackback:ping><description><![CDATA[<br />
1. 熟练使用各种框架，是必须的。但是，仅仅这样顶多算个好的Coder。如果想成为成为Designer，就必须熟悉框架注意背后的思想。了解别人是为了解决什么问题的，用了什么策略解决的，实现策略中使用那些技巧。而要深入了解这些，就需要查看框架的源代码了。<br />
<br />
2. 基础和思想是最重要的。<br />
<br />
3.把项目当成作品来做，而不是当成任务来完成。自己做的东西，哪怕是一个简单的页面，也要努力做到极致。<br />
比如：<br />
（1）几乎没有bug；<br />
（2）界面美观，符合用户的使用习惯；<br />
（3）支持多种浏览器；<br />
（4）页面加载非常快（1秒之内）；<br />
（5）采用了缓存（浏览器缓存；合并JS、CSS、优化图片；服务器数据缓存；服务器页面缓存；等等）<br />
（6）页面布局合理、操作简单（点击时，考虑了用户移动鼠标的时间和距离）；<br />
（7）采用Ajax局部刷新；<br />
（8）JS文件可以重用，提取公共的JS库；<br />
（9）服务器端性能尽可能优化（MySQL、Cache、多线程等）<br />
<br />
4. 熟练Linux下的常用环境搭建和部署。不要太依赖系统工程部的同学，在时间允许的情况下，自己可以安装JDK、MySQL、Tomcat等软件。<br />
常用的Linux环境部署要熟练掌握，例如：<br />
（1）部署Web工程，搭建测试环境。<br />
（2）MySQL相关命令。如，导入导出MySQL数据、建表建库。<br />
（3）启动关闭服务。<br />
（4）编写简单Shell脚本。如，Java服务器的启动和关闭脚本；同步文件脚本；定时任务脚本等等；<br />
（5）常用的Linux操作命令。如：文件管理，vi命令，配置host。<br />
<br />
5.多向其他人学习，特别是同一个项目组的。每个人肯定有自己的长处和短处。<br />
技术上学习别人长处有两个好的方法：<br />
（1）直接和别人交流请教。<br />
（2）查看别人写的代码。思考别人的解决方案，并和自己的想法做比较，吸收好的想法和思路。<br />
<br />
待补充<br />
<br />
（友情提示：本博文章欢迎转载，但请注明出处：hankchen，<a title="http://www.blogjava.net/hankchen" href="http://www.blogjava.net/hankchen"><strong>http://www.blogjava.net/hankchen</strong></a><strong>）</strong><br />
<br />
<br />
<br />
<img src ="http://www.blogjava.net/hankchen/aggbug/309622.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2010-01-15 00:11 <a href="http://www.blogjava.net/hankchen/archive/2010/01/15/309622.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>开博说明</title><link>http://www.blogjava.net/hankchen/archive/2009/08/31/293287.html</link><dc:creator>hankchen</dc:creator><author>hankchen</author><pubDate>Mon, 31 Aug 2009 03:53:00 GMT</pubDate><guid>http://www.blogjava.net/hankchen/archive/2009/08/31/293287.html</guid><wfw:comment>http://www.blogjava.net/hankchen/comments/293287.html</wfw:comment><comments>http://www.blogjava.net/hankchen/archive/2009/08/31/293287.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hankchen/comments/commentRss/293287.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hankchen/services/trackbacks/293287.html</trackback:ping><description><![CDATA[<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实，我算是一个喜欢总结和归纳的人，工作一年来，写了一些技术文章和工作感悟。但多半是以Word的形式存档起来，或者仅仅是发布在QQ的个人空间。我一直欣赏一个前辈的话：经验在于积累，而不在于年限。要提升自己的功力，除了不断的学习之外，就是不断的自我总结和归纳。今天，我突然发现，其实还有一条：就是分享！所以，我要把自己的感悟和总结晒出来，大家一起学习，一起进步。</p>
 <img src ="http://www.blogjava.net/hankchen/aggbug/293287.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hankchen/" target="_blank">hankchen</a> 2009-08-31 11:53 <a href="http://www.blogjava.net/hankchen/archive/2009/08/31/293287.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>