存档

2009年6月 的存档

优化你的前端-《高性能网站建设指南》读后感

2009年6月3日 hashei 没有评论

之前的几篇文章,都是从中间件的角度来思考程序该如何优化。可是我们知道,一个应用的响应时间,除了从中间件接受请求到返回请求之间的处理时间,还包括在网络上传输的时间、浏览器展现的时间。如果你的应用是对Internet提供服务的,就不得不考虑这一层情况,否则中间件、数据库做了大量优化,能承担很大的并发量,却因为前端没有做过优化导致响应时间过长、用户体验变差,那么是相当郁闷的一件事。

我自己也亲身经历过这样的情况。一个OA项目开发完成后需要做性能测试,发现登录的响应速度很慢,loadrunner显示的吞吐率很高,完成的事务数却不多。用TPV查看WebSphere的使用率,发现JVM回收很正常,于是以为瓶颈在数据库查询上,但是数据库查询SQL执行的很快。最后用Firefox启用Firebug插件,不看不要紧,一看吓一跳:首页有8M大。虽然是内网,但也禁不住这么折腾啊。原来是客户上传了一张照片,程序没有做任何处理就在首页作为新闻放出了缩略图,所以网络流量很高,但是完成的登录数寥寥无几。

于是花了两天时间,在路上和中午阅读了一下《 高性能网站建设指南》。本书是由原Yahoo的Chief Performance撰写。书很薄,2个小时就能看完,但是内容却很丰富,15章章章有精彩。除了要修改代码的“减少HTTP请求”、“使用外部和CSS”、“避免CSS表达式”、“使Ajax可缓存”,更多的是不用修改任何代码,只要做一些配置就能享受到的性能提升——“添加Expires头”、“将样式表放在顶部”、“将脚本放在底部”、“精简JavaScript”、“配置Etag”。虽然这些内容似乎网上随处可见,但是原理解释到位,又结合实验数据说明,外加完整的示例代码的文章却不多见。看完这本书,可以让你不仅知其然,更知其所以然。

我的博“聚沙成塔-小哈的记事簿”也根据这些原则做了优化,把图片和CSS的Expires头都设置为了“增加10年”,这样第二次访问我的网页会迅速很多。同时启用了WordPress2.7默认关闭的gzip功能,同时使用了smartoptimizer工具来压缩CSS和JS,这样整个页面的传输量小了近1/3。由此我也能停用了SuperCache插件(其实就俺这些访问量用不着supercache,当时启用也是为了访问速度的考虑),启用Postviews来记录文章访问的情况。就这么简简单单的一些配置,让我的Yslow标准成绩从D提升到了C(还有些优化牵涉到CSS的修改,本人不是很擅长,还有些方面因为是个人博客不能避免的会引用到其它的服务),Small Site or Blog成绩为B。

怎么样,有没有兴趣买来读一读。如果只想具体了解一些的话,可以先看看Yahoo的这篇文章《Best Practices for Speeding Up Your Web Site》,和大牛Fenng《高性能网站建设指南》读后随感

最后附上简单有效的配置,在.htaccess文件中,新加入

<IfModule mod_expires.c>
<FilesMatch “\.(gif|jpg|jpeg|png|swf|css|js|htm?|xml|txt)$”>
ExpiresActive On
ExpiresDefault “access plus 10 years”
</FilesMatch>
</IfModule>
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*\.(js|css))$ smartoptimizer/?$1
<IfModule mod_expires.c>
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(.*\.(js|css|htm?|xml|txt))$ smartoptimizer/?$1
</IfModule>
<IfModule !mod_expires.c>
RewriteCond %{REQUEST_FILENAME} -f
RewriteRule ^(.*\.(gif|jpg|jpeg|png|swf|css|js|htm?|xml|txt))$ smartoptimizer/?$1
</IfModule>
</IfModule>
<FilesMatch “\.(gif|jpg|jpeg|png|swf|css|js|htm?|xml|txt)$”>
FileETag none
</FilesMatch>

即可添加长期的Expires头,转发js、css被smartoptimizer压缩,关闭Etag。

在根目录的index.php中define(‘WP_USE_THEMES’, true);后添加

if(ereg(‘gzip’,$_SERVER['HTTP_ACCEPT_ENCODING'])){
if(substr($_SERVER['REQUEST_URI'],0,10)!=’/wp-content/uploads/’)
ob_start(‘ob_gzhandler’);
}

wordpress的gzip压缩功能。

JAVA性能优化-GC日志分析

2009年6月1日 hashei 1 条评论

前两篇说到IBM JDK和Sun的HotSpot JDK的调优策略,当中一直提到的重要一点是需要根据GC详细日志来调整参数的设置,那么当我们收集到日志后如何分析,如何根据日志的情况来调整参数?这就是本文所要阐述的。

使用IBM的JDK的Windows平台和AIX平台,我们只要在WebSphere管理控制台的java进程属性里勾选“详细垃圾回收”,那么就会在native_stdout.log中生成如下的信息。

image

这幅图很形象的给出了gc日志的主要关注点:垃圾回收的原因、垃圾回收的间隔、垃圾回收前后的剩余空间、垃圾回收持续的时间……

“nursery”表示这次分配失败(Allocation Failure)发生在新生代(nursery),是第44次(id=44)因为新生代的分配失败而进行垃圾回收了(开启GC日志以来),离上一次发生GC的间隔是12746ms,无法分配的空间大小是8216Byte,而进行垃圾回收前,新生代的可用空间为1776Byte,小于8216Byte,虽然年老区还剩余45%的空间,但是新的内存空间是无法直接在年老区分配的,由此引发了一次清理过程(scavenger)。

GC type表明了这次垃圾回收是一个清理过程,也是第44次scavenger过程,同时恰是第44个gc过程,说明没有发生过诸如<gc type=”global”>的垃圾回收过程。flipped objectcount说明将要把4523143个存活下来的对象被复制到了幸存区(survivor space),而73768个对象则经过多次幸存区的复制,有幸熬出头,被转移到了长存区(tenured space)。我们看到清理的倾斜比率(scavenger tiltratio)为89%,而不是对半开,说明经过多次复制清理,JVM已经意识到每次只有很少的对象能存活下来,于是它自动增大了年轻代中Eden区的大小,以使得为新对象可以腾出更多的内存。

接下来的就比较简单,经过垃圾回收,新生代的空间剩余89%,因为分配失败发生在新生代,进行的是快速的Minor gc,所以长存区的可用空间依然是45%,这次回收的时间为384.469ms。在长存区发生的是Major gc,回收时间一般要长于Minor gc,所以健康的JVM 环境Minor gc:Minor gc=1:10(也不用太在意)。

在垃圾回收准备开始的那一段时间(time exclusiveaccessms),以及回收的过程中,另一个线程发现内存无法分配了,于是也要求一次gc过程,这时候就产生了gc队列(会有<warning details=”exclusive access time includes previous garbage collections” />显示)。一般前一次垃圾回收就会释放出足够这两次分配需要的空间,于是第二次gc过程就终止了,当然要是无法足够分配的话,马上就会再进行一次垃圾回收。不过这种情况下,估计要进行Full GC了。

Full GC的gc type就是global,这是stop the world的最耗费时间的回收方式,所以需要通过尽量调整参数来避免。如果发现不是由AF引起的,而是在开头写着SYS标签,那么就要好好问问开发,有没有使用System.gc()的必要?可以的话使用-Xdisableexplicitgc来禁止(Sun等JDK下为-XX:+DisableExplicitGC)。

以上所说的都是IBM JDK下垃圾回收日志的情况。在Sun和HP的JDK下情况相似,不过需要使用-XX:PrintHeapAtGC:参数来打印GC前后的详细堆栈信息。另外我建议加上-Xloggc:filename或者-Xverbosegclog:file_name(方便用工具分析)输出到特定文件,因为不知为何有时候会输出到native_stderr.log,或者在native_stdout.log中和thread dump夹杂在一起,不方便分析。

{Heap before GC invocations=116:
Heap
def new generation   total 157376K, used 139904K [63400000, 6dec0000, 78950000)
eden space 139904K, 100% used [63400000, 6bca0000, 6bca0000)
from space 17472K,   0% used [6bca0000, 6bca0000, 6cdb0000)
to   space 17472K,   0% used [6cdb0000, 6cdb0000, 6dec0000)
tenured generation   total 349568K, used 79067K [38800000, 4dd60000, 632b0000)
the space 349568K,  22% used [38800000, 3d536ce0, 3d536e00, 4dd60000)
compacting perm gen  total 132096K, used 132023K [28800000, 30900000, 38800000)
the space 132096K,  99% used [28800000, 308edf50, 308ee000, 30900000)
[GC 218971K->83116K(506944K), 0.0976948 secs]
Heap after GC invocations=117:
Heap
def new generation   total 157376K, used 4049K [63400000, 6dec0000, 78950000)
eden space 139904K,   0% used [63400000, 63400000, 6bca0000)
from space 17472K,  23% used [6cdb0000, 6d1a4628, 6dec0000)
to   space 17472K,   0% used [6bca0000, 6bca0000, 6cdb0000)
tenured generation   total 349568K, used 79067K [38800000, 4dd60000, 632b0000)
the space 349568K,  22% used [38800000, 3d536ce0, 3d536e00, 4dd60000)
compacting perm gen  total 132096K, used 132023K [28800000, 30900000, 38800000)
the space 132096K,  99% used [28800000, 308edf50, 308ee000, 30900000)
}

这是一个在使用HPJDK输出的实例,基本和IBM JDK没啥区别,可以加上-XX:+PrintGCTimeStamps显示每次回收的间隔。

这些“人肉”分析可以让我们清楚JVM运行的情况,但是还是不够直观,所以下次会介绍分析JVM日志的工具,尽情期待。现已完成,欢迎访问