一起去看海

2010/04/15

【每日一技】JS性能优化

Filed under: Dian团队 — Tags: , — admin @ 21:57

CrazyDo

浏览器解释HTML时最耗时的部分是Reflow(回流),而当JS操作DOM的时候就会引发浏览器的Reflow,因此减少操作DOM的次数可以提高性能。

当需要操作DOM时,比如在一个div中添加n个div,我们可能这样操作:
[javascript]var ctrl = document.getElementById("mainContainer");
for (var i = 0; i < 100; i++) {
    var child = document.createElement("div");
    ctrl.appendChild(child);
}[/javascript]

或者
[js]var ctrl = document.getElementById("mainContainer");
var inner = ""
for (var i = 0; i < 100; i++) {
    inner += "<div></div>";
}[/js]

ctrl.innerHTML = inner;
乍一看,第一种方法直接操作DOM,省去了HTML->DOM的Parse过程,但是每次执行appendChild,就要Reflow一次,而第二种方法,相当于一次性操作DOM,只有一次Reflow,而且目前大部分浏览器对于Parse的过程都有优化,因此性能开销十分小。实测添加10000个div,第一种方法在160ms左右,第二中方法只要50ms左右。

———————————————–

spllot:如果我没记错的话,使用innerHTML,有些情况下是不怎么好的.能不能分析下使用innerHTML的好处与坏处

———————————————-

joehust:更复杂的情况呢?我比较喜欢用innerHTML直接设置,不过有人提醒过我说有一部分我写的代码太慢了,换成了分步的dom操作,

是否在所有情况下都是直接修改innerHTML更快呢?

———————————————

CrazyDo:一般来说,如果innerHTML里面嵌套关系过于复杂的时候建议先createElement创建儿子节点,然后在其中appendChild加入孙子节点,以此类推,最后将儿子节点加入父亲节点,这样在Firefox下也是一次Reflow,但是IE下似乎还是多次Reflow。

———————————————

joehust:很多时候再构造页面的时候其实是先将内容拼好,再append到某个节点下面去,在拼接过程中应该是不会有reflow的,这个时候用dom语句去构造就会省去parse html的过程,速度应当快些,如果是对已经呈现出来的页面进行修改的话可以考虑先将其hide,或者移除,等修改完成后再添加回去,这样可以避免修改的过程中发生reflow,从而提高效
率吧

2010/04/14

【每日一技】JVM参数与性能优化

Filed under: Dian团队 — Tags: — admin @ 10:13

henix

在很多地方都会用到 JVM 参数,比如你运行 ant check-style,跑挂了,抛了个OutOfMemory 错误。这时你上网搜索,他们会告诉你,应该修改 ANT_OPTS 环境变量,加上“-Xmx512m”。

再比如 JBoss 或 tomcat 都有启动脚本(startup.bat等),里面也用到 JVM 参数。还有Eclipse 的 eclipse.ini 中也可以加入 JVM 参数。灵活使用这些 JVM 参数可以使我们的 Java 程序获得更好的性能。

=== 常用参数 ===

-server
一定要作为第一个参数,会使JVM启动速度变慢,但会显著提升JVM性能

-Xms64m
设置初始 Heap 大小,使用的最小内存

-Xmx1024m
设置最大 Heap 大小

-Xss256k
设置每个线程的 Stack 大小

-XX:MaxPermSize=128M
设置最大永久区大小,永久保存区用于存放Class信息和元信息

=== GC(垃圾回收)参数 ===

Sun HotSpot JVM 通常使用的 GC 算法是分代回收。即把对象按生命周期不同分为年轻代和年老代。新的对象会先生成在Young Area,在几次 GC 以后,如果没有收集到,就会逐渐升级到Tenured Area。

Sun JVM 默认的回收器称为 serial collector ,即串行回收,我们也可以使用多线程并发回收来提高效率。需要注意的是,在单核的机器上,使用默认的回收器可能会更好。

-XX:+UseParNewGC
为年轻代对象使用并发回收,会缩短年轻代对象的回收时间

-XX:+UseConcMarkSweepGC
为年老代使用并发回收

-XX:+UseParallelGC
该收集器和 UseConcMarkSweepGC 是互相排斥的,为新生代使用并行清除,年老代使用单线程Mark-Sweep-Compact的垃圾收集器。

=== 其他参数 ===

-XX:CompileThreshold=100
该值表示编译之前必须调用方法的次数。HotSpot JVM 使用 JIT(Just-In-Time Compilation, http://en.wikipedia.org/wiki/Just-in-time_compilation) 技术将字节码编译成本地码,默认值为1000,即一个方法被调用了 1000 次之后 JVM 会编译它。降低这个值可能会使程序的性能得到提升,但会使启动速度变慢。

-XX:+DoEscapeAnalysis
启用逃逸分析,这个选项只能用于 Java 1.6。原理参见:http://kenwublog.com/jvm-optimization-escape-analysis

=== JVM参数的设置 ===

可以直接在java命令后面附加,比如:java -Xmx512m -XX:+UseParNewGC Test

还可以在环境变量中设置,如 JAVA_OPTS 和 ANT_OPTS 。还可以在eclipse.ini中设置,需要写在 -vmargs 一行的后面

=== 参考资料 ===
* Java 6 JVM参数选项大全(中文版):http://kenwublog.com/docs/java6-jvm-options-chinese-edition.htm
* Garbage Collection – Frequently Asked Questions:http://java.sun.com/docs/hotspot/gc1.4.2/faq.html
* 调整 JVM 开关以获得最佳性能:http://gceclub.sun.com.cn/NetBeans/tutorials/tuning/index.html

转自Dian团队内部技术讨论。

2010/04/12

【每日一技】提高文件读取速度的NIO

Filed under: Dian团队, Java笔记 — Tags: , — admin @ 17:19

NIO即new io.主要是采用通道机制来提高文件读取速度。以下为一个简单的读取文本文
件的例子:

[java]public String readTxtFile(String path) throws IOException {

long start = System.currentTimeMillis();

//设置缓冲区的大小

final int BUFFER_SIZE = 0×900000;

String content = "";

// 创建一个文件输入流

FileInputStream fis = new FileInputStream(path);

// 获得该输入流的通道

FileChannel fc = fis.getChannel();

// 创建一个字节缓冲区

ByteBuffer bb = ByteBuffer.allocate(BUFFER_SIZE);

// 将字节序列从此通道读入给定的缓冲区

fc.read(bb);

// 反转此缓冲区。首先将限制设置为当前位置,然后将位置设置为 0

bb.flip();

// 获得缓冲区中剩余元素数,并创建相应的字节数组

byte[] bytes = new byte[bb.remaining()];

// 将缓冲区中的字节序列存放在字节数组里

bb.get(bytes);

// 将字节数组中的内容转换成字符串

content = new String(bytes);

long end = System.currentTimeMillis();

System.out.println(end – start);

return content;

}[/java]

我做了一个简单的测试,对于一般几K到几十K的文本文件,只需要几毫秒到20几毫秒
,对于一个好几M的文本文件,只需要200多个毫秒。希望有兴趣的同学可以进一步的研
究,共同探讨相关话题。

另外推荐一篇关于流的文章:http://www.ibm.com/developerworks/cn/java/j-io1/

———————————————————

henix: 那跟FileInputStream再套上BufferedInputStream有什么区别?

———————————————————

spllot:

我觉得一个很重要的区别是,NIO中的做法更灵活,操作起来比较简单.我写的小例子只是
其中的一角,还有像MappedByteBuffer等缓冲区,这些缓冲区都有清除、反转和重绕方法
,用以标记当前位置,以及将当前位置重置为前一个标记处.并且还定义了一系列用于将
数据移出或移入缓冲区的 get 和 put 方法,用于压缩、复制 和切片 缓冲区的方法,
以及用于分配新缓冲区和将现有数组包装到缓冲区中.

对于我写的那个小程序,确实给人的感觉是空间换时间.

就我写的那个程序来说,对于一个小的文件,缓冲区设置小一点是没有什么问题的,但是如
果是一个好几个M的文件,如果缓冲区设置的过小的话,会出现读出来的内容不完全的情况
.所以为了简单的引进NIO,我仅仅是做了一个示例.

对于文件比较大的情况,如果将缓冲区设置比较小的话,也是可以解决的,分块来读就行了
.NIO是按块来读的,从这点来看,就比传统的IO读起来会快很多.另外NIO是无阻塞的,传统
的IO读文件的时候,需要将数据读到缓冲区后,再等待处理,会浪费很多时间,但NIO则不需
要,通过通道来传输,减少了等待的时间,从而提高了读取速度.

P.S 我的毕业设计是桌面搜索引擎,其中会涉及到文件的读取,有可能文件是比较大的,并
且数量是比较多的,所以如何减少读取时间,提高索引速度是很重要的,因此我考虑到使用
NIO.有兴趣的同学可以和我一起研究.

—————————————————

转自Dian团队内部技术讨论。

Powered by WordPress