T61升级记:8G DDR2内存64位系统

08年初买了一台港版的T61,当时配置是2G内存,160G硬盘(SATA-I),用了这许多年觉得有必要做些升级。
因为笔记本的内存插槽有限,平时开的应用多内存吃的厉害,所以考虑升级为8G内存。

一、可行性分析

升级8G内存需要考虑机器硬件上能否支持,操作系统能否识别。

1、主板是否支持单根4G,双根8G内存

DDR2出现早期,单根4G的内存还不流行,所以一些早起的笔记本其主板是否支持单根4G的内存需要查阅主板的相关技术文档。
网上转了一圈之后,发现 金士顿官网 上有个页面可以查询主板对内存的支持情况。
例如我的T61查询结果为:

主板支持8G内存也就是硬件上就可以支持8G的内存寻址了。

2、操作系统识别8G内存

32位的操作系统最大支持4G的寻址。因为操作系统的信号中断、其他各类硬件设备(如显卡的显存)都要占用一部分寻址地址,
扣除这部分地址之后剩余的地址大小就是你的机器所能识别的最大内存大小了。如果你的机器上硬件设备越多,
那么占用的寻址地址越多,操作系统能用于内存寻址的地址就越少。扣除之后一般在3G~3.5G上下。
这就是为什么你插了4G内存条,操作系统却只识别出3G左右的原因。

为了让操作系统能识别4G以上的内存,需要安装64位操作系统。

2、CPU支持运行64位操作系统

CPU要能够支持64位的指令集才可以运行64位的操作系统。家用PC一般都是X86架构,
那么你的CPU要能够支持X86-64指令集才可运行64位操作系统。
通过 EVEREST 可以查看你的CPU是否支持64位指令集。如图:

支持X86-64指令集的的CPU可以安装 win7_x64 系统,可以兼容运行x86-32位的程序。
如果你的CPU支持的是IA-64指令集,那么你需要安装 win7_IA64 系统。

二、安装64位操作系统注意事项

配置了8G内存,安装win7_x64操作系统时,操作系统分区(一般是C盘)的大小要设置大点。
如果是笔记本的话,建议设置在80G以上(我的就是设置为80G)。
微软官方建议,安装了64位操作系统后系统盘的可用空间应在20G以上。
原因如下:

1、windows的页面文件

windows需要设置虚拟内存,否则有些程序可能无法正常运行。
系统默认配置是从系统盘分配内存大小的 1~1.5倍 空间作为虚拟内存。
所以,当你的内存为8G时,页面文件大小将是8G~14G左右。
页面文件是系统文件,默认是隐藏的,文件名叫pagefile.sys,显示隐藏文件后可见。
如图:

2、windows的休眠功能

当你启用休眠功能时,windows将把内存的快照数据保存到硬盘中,然后关闭系统。
所以快照文件最大可达8G(同内存大小),快照文件的实际大小与当前已使用的内存大小有关。
windows默认将内存快照文件存放在系统盘,该文件是系统文件默认隐藏,文件名叫hiberfil.sys。
见上图。

3、程序与临时文件

有些程序,即使你将其安装目录指向非系统盘,它也需要往系统盘下的程序文件夹或者windows文件夹放入一些文件。
如果你安装的程序很多的话,就将占用较多的磁盘空间。
此外系统的临时目录也在系统盘下,如果你不经常清理的话,那么这些垃圾文件也会占用大量的磁盘空间。

三、效果

机器升级完之后效果如下:

因为显卡可以按比例使用内存作为共享显存,所以显存的大小已经达到3710M!

JAVA调优:字符串操作性能比较

旧文搬迁:2008-5-27

字符串处理代码的效率与编译器对代码的优化算法是相关的。

1、字符串与常量的连接

1
2
3
String str = "abc" + "123" + 456;
//经java编译器优化后,执行的代码等同于:
String str = "abc123456";

2、字符串与变量的连接

1
2
3
4
5
6
7
(1)
int i = 123;
String str = "abc" + i;
(2)
int i = 123;
String str = "" + i;

字符串之间的运算符 + 的实现,实际上是通过新建一个StringBuffer来实现的。

编译之后,实际运行的等效代码如下:

1
2
3
4
5
6
7
8
9
10
11
//(1)
int i = 123;
StringBuffer sbuff = new StringBuffer("abc");
sbuff.append(i);
String str = sbuff.toString();
//(2)
int i = 123;
StringBuffer sbuff = new StringBuffer();
sbuff.append(i);
String str = sbuff.toString();

所以,类似(2)的代码应该使用 String.valueOf(i) 替代,避免创建StringBuffer对象。

1
2
3
4
//(3)
int ia = 123;
int ib = 456;
String str = "abc" + ia + "def" + ib;

等同于:

1
2
3
4
5
6
7
8
//(3)
int ia = 123;
int ib = 456;
StringBuffer sbuff = new StringBuffer("abc");
sbuff.append(ia);
sbuff.append("def");
sbuff.append(ib);
String str = sbuff.toString();

事实上,每个字符串连接的表达式会且仅会创建一个StringBuffer对象。 例:

1
2
3
4
5
//(4)
int ia = 123;
String str = "abc" + ia;
int ib = 456;
str = str + ib;

编译后执行的等价代码是:

1
2
3
4
5
6
7
8
9
10
11
//(4)
int ia = 123;
StringBuffer sbuff = new StringBuffer("abc");
sbuff.append(ia);
String str = sbuff.toString();
int ib = 456;
StringBuffer sbuff = new StringBuffer(str);
sbuff.append("def");
sbuff.append(ib);
str = sbuff.toString();

所以,如果你在进行字符串相加时 使用了多个表达式, 那么一定要使用自己创建的 StringBuffer来进行字符串连接。

3、反编译分析

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* StringTuning
*
* @author crazyzh1984
* @version 1.0, 2008-5-22
*/
public class StringTuning {
protected static final Log logger = LogFactory.getLog(StringTuning.class);
protected static void test1() {
String str = "abc" + 123;// abc123
logger.debug(str);
}
protected static void test2() {
int i = 123;
String str = "abc" + i;
logger.debug(str);
}
protected static void test3() {
int i = 123;
String str = "" + i;
logger.debug(str);
}
protected static void test4() {
int ia = 123;
int ib = 456;
String str = "abc" + ia + "def" + ib;
logger.debug(str);
}
protected static void test5() {
int ia = 123;
int ib = 456;
String str1 = "abc" + ia;
str1 = str1 + ib;
logger.debug(str1);
}
}

反编译的结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
Compiled from "StringTuning.java"
public class StringTuning extends java.lang.Object{
protected static final org.apache.commons.logging.Log logger;
static java.lang.Class class$0;
static {};
Code:
0: getstatic #13; //Field class$0java/lang/Class;
3: dup
4: ifnonnull 32
7: pop
8: ldc #15; //String com.thunisoft.codereview1.StringTuning
10: invokestatic #17; //Method java/lang/Class.forNameLjava/lang/String;)Ljava/lang/Class;
13: dup
14: putstatic #13; //Field class$0java/lang/Class;
17: goto 32
20: new #23; //class java/lang/NoClassDefFoundError
23: dup_x1
24: swap
25: invokevirtual #25; //Method java/lang/Throwable.getMessage)Ljava/lang/String;
28: invokespecial #31; //Method java/lang/NoClassDefFoundError.""Ljava/lang/String;)V
31: athrow
32: invokestatic #35; //Method org/apache/commons/logging/LogFactory.getLogLjava/lang/Class;)Lorg/apache/commons/logging/Log;
35: putstatic #41; //Field loggerorg/apache/commons/logging/Log;
38: return
Exception table:
from to target type
8 13 20 Class java/lang/ClassNotFoundException
public com.thunisoft.codereview1.StringTuning();
Code:
0: aload_0
1: invokespecial #47; //Method java/lang/Object."")V
4: return
//函数1
protected static void test1();
Code:
0: ldc #52; //String abc123 注:字符串赋值为 "abc123"
2: astore_0
3: getstatic #41; //Field loggerorg/apache/commons/logging/Log;
6: aload_0
7: invokeinterface #54, 2; //InterfaceMethod org/apache/commons/logging/Log.debugLjava/lang/Object;)V
12: return
//函数2
protected static void test2();
Code:
0: bipush 123
2: istore_0
3: new #63; //class java/lang/StringBuffer 注:创建了一个StringBuffer对象
6: dup
7: ldc #65; //String abc
9: invokespecial #67; //Method java/lang/StringBuffer.""Ljava/lang/String;)V
12: iload_0
13: invokevirtual #68; //Method java/lang/StringBuffer.appendI)Ljava/lang/StringBuffer;
16: invokevirtual #72; //Method java/lang/StringBuffer.toString)Ljava/lang/String;
19: astore_1
20: getstatic #41; //Field loggerorg/apache/commons/logging/Log;
23: aload_1
24: invokeinterface #54, 2; //InterfaceMethod org/apache/commons/logging/Log.debugLjava/lang/Object;)V
29: return
//函数3
protected static void test3();
Code:
0: bipush 123
2: istore_0
3: new #63; //class java/lang/StringBuffer 注:创建了一个StringBuffer对象
6: dup
7: invokespecial #78; //Method java/lang/StringBuffer."")V
10: iload_0
11: invokevirtual #68; //Method java/lang/StringBuffer.appendI)Ljava/lang/StringBuffer;
14: invokevirtual #72; //Method java/lang/StringBuffer.toString)Ljava/lang/String;
17: astore_1
18: getstatic #41; //Field loggerorg/apache/commons/logging/Log;
21: aload_1
22: invokeinterface #54, 2; //InterfaceMethod org/apache/commons/logging/Log.debugLjava/lang/Object;)V
27: return
//函数4
protected static void test4();
Code:
0: bipush 123
2: istore_0
3: sipush 456
6: istore_1
7: new #63; //class java/lang/StringBuffer 注:创建了一个StringBuffer对象
10: dup
11: ldc #65; //String abc
13: invokespecial #67; //Method java/lang/StringBuffer.""Ljava/lang/String;)V
16: iload_0
17: invokevirtual #68; //Method java/lang/StringBuffer.appendI)Ljava/lang/StringBuffer;
20: ldc #80; //String def
22: invokevirtual #82; //Method java/lang/StringBuffer.appendLjava/lang/String;)Ljava/lang/StringBuffer;
25: iload_1
26: invokevirtual #68; //Method java/lang/StringBuffer.appendI)Ljava/lang/StringBuffer;
29: invokevirtual #72; //Method java/lang/StringBuffer.toString)Ljava/lang/String;
32: astore_2
33: getstatic #41; //Field loggerorg/apache/commons/logging/Log;
36: aload_2
37: invokeinterface #54, 2; //InterfaceMethod org/apache/commons/logging/Log.debugLjava/lang/Object;)V
42: return
//函数5
protected static void test5();
Code:
0: bipush 123
2: istore_0
3: sipush 456
6: istore_1
7: new #63; //class java/lang/StringBuffer 注:创建了一个StringBuffer对象
10: dup
11: ldc #65; //String abc
13: invokespecial #67; //Method java/lang/StringBuffer.""Ljava/lang/String;)V
16: iload_0
17: invokevirtual #68; //Method java/lang/StringBuffer.appendI)Ljava/lang/StringBuffer;
20: invokevirtual #72; //Method java/lang/StringBuffer.toString)Ljava/lang/String;
23: astore_2
24: new #63; //class java/lang/StringBuffer 注:又创建了一个StringBuffer对象
27: dup
28: aload_2
29: invokestatic #88; //Method java/lang/String.valueOfLjava/lang/Object;)Ljava/lang/String;
32: invokespecial #67; //Method java/lang/StringBuffer.""Ljava/lang/String;)V
35: iload_1
36: invokevirtual #68; //Method java/lang/StringBuffer.appendI)Ljava/lang/StringBuffer;
39: invokevirtual #72; //Method java/lang/StringBuffer.toString)Ljava/lang/String;
42: astore_2
43: getstatic #41; //Field loggerorg/apache/commons/logging/Log;
46: aload_2
47: invokeinterface #54, 2; //InterfaceMethod org/apache/commons/logging/Log.debugLjava/lang/Object;)V
52: return
}

对于被定义成final的原始数据类型的常量,会被静态编译到执行的代码中。 例:

1
2
3
4
5
6
7
8
9
protected static final int CODE_TEST = 888;
protected static void test6() {
int ia = 123;
final int ib = 999;
String str = "abc" + CODE_TEST + ia;
str = "abc" + ib + ia;
logger.debug(str);
}

编译后的执行代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
protected static void test6();
Code:
0: bipush 123
2: istore_0
3: sipush 999
6: istore_1
7: new #67; //class java/lang/StringBuffer
10: dup
11: ldc #99; //String abc888 注:"abc" + CODE_TEST 被静态编译为 "abc888"
13: invokespecial #71; //Method java/lang/StringBuffer."":(Ljava/lang/String;)V
16: iload_0
17: invokevirtual #72; //Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;
20: invokevirtual #76; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
23: astore_2
24: new #67; //class java/lang/StringBuffer
27: dup
28: ldc #101; //String abc999 注:"abc" + ib 被静态编译为 "abc999"
30: invokespecial #71; //Method java/lang/StringBuffer."":(Ljava/lang/String;)V
33: iload_0
34: invokevirtual #72; //Method java/lang/StringBuffer.append:(I)Ljava/lang/StringBuffer;
37: invokevirtual #76; //Method java/lang/StringBuffer.toString:()Ljava/lang/String;
40: astore_2
41: getstatic #45; //Field logger:Lorg/apache/commons/logging/Log;
44: aload_2
45: invokeinterface #58, 2; //InterfaceMethod org/apache/commons/logging/Log.debug:(Ljava/lang/Object;)V
50: return

4、测试

假定每秒要处理 count 次字符串相加运算,则测试代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) {
final int count = 10000;
// 使用字符串相加
long start = System.currentTimeMillis();
String str = "str=";
for (int i = 0; i < count; i++) {
str = str + i;
}
long elapse = System.currentTimeMillis() - start;
logger.info("elapse 1: " + elapse);
// 使用StringBuffer
start = System.currentTimeMillis();
StringBuffer sbuff = new StringBuffer("str=");
for (int i = 0; i < count; i++) {
sbuff.append(i);
}
str = sbuff.toString();
elapse = System.currentTimeMillis() - start;
logger.info("elapse 2: " + elapse);
}

当 count=10000 时,运行结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
2008-5-27 19:50:24 StringTuning main
信息: elapse 1: 968
2008-5-27 19:50:24 StringTuning main
信息: elapse 2: 0
2008-5-27 19:50:36 StringTuning main
信息: elapse 1: 984
2008-5-27 19:50:36 StringTuning main
信息: elapse 2: 0
2008-5-27 19:50:43 StringTuning main
信息: elapse 1: 968
2008-5-27 19:50:43 StringTuning main
信息: elapse 2: 15

当 count=20000 时,运行结果为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
2008-5-27 19:48:54 StringTuning main
信息: elapse 1: 9281
2008-5-27 19:48:54 StringTuning main
信息: elapse 2: 0
2008-5-27 19:51:04 StringTuning main
信息: elapse 1: 9218
2008-5-27 19:51:04 StringTuning main
信息: elapse 2: 0
2008-5-27 19:51:26 StringTuning main
信息: elapse 1: 9281
2008-5-27 19:51:26 StringTuning main
信息: elapse 2: 16

我们发现,耗费的时间比运算量的增加的更快,创建每个StringBuffer的成本急剧增加。
为什么呢?原因是内存,频繁的对象创建将导致JVM频繁的GC。
我们可以估算一下2种方式创建的对象个数。

方式1创建的对象个数为:
count*2 (每次”+”操作创建一个StringBuffer和一个String对象)

方式2创建的对象个数为:
log2(count)+1 (因为StringBuffer的数组扩容每次乘2)

实际上,扩容的次数跟StringBuffer的初始大小以及要操作的字符串大小有关。

克艳情史

C克与X艳情比金坚,但二位如何相识诸位知否?

吾羡鸳鸯,深感好奇,遂查史料。

据正史所载,当时的情形是这样的:

某年某月某日,日将晚,日落西山,霞满天下。
时克与艳携游某园,行至水边,湖光山色甚美,驻足远望。
艳于霞光晚风中,丝发轻扬,隐约如仙子下凡。克心荡漾,猝牵艳手。
艳遭此袭,面涨通红,急欲挣脱,怎奈何陈克威猛,摆脱不得!
艳复挣扎,克执益坚。反复数次,终不得脱,艳料天意如此,于是乃降。
克艳情事由此开始。
—— 摘自《陈克列传》

另据野史记载,当时情形如下:

某年某月某日,日将晚,日落西山,霞洒天下。
时克与艳携游某园,行至水边,湖光山色甚美,驻足远望。
克于霞光晚风中,甚为威武,威威然有如泰山。艳见此景,仰慕不已,猝吻克颊。
克遭此袭,面涨通红,手足无措!
艳与曰:“愿侍郎君,矢志不渝!”。
克甚感动,复曰:“得妻如此,夫复何求?”。
相携而归。
—— 摘自《陈克演义》

众说纷纭,无据可考。有诗云:

花开时节,月圆时候
露湿胭脂初透
明月照满西楼
似这般良辰美景
似这般蜜意绸缪
且将淡泊相依
且将殷勤相守
让花常开月常圆人常久

此祝:天长地久!

commons-logging vs log4j

今天使用日志记录的时候突然发现,系统中有2套获取日志记录对象的方法,

1
2
3
4
5
// using org.apache.commons.logging
Log logger = LogFactory.getLog(XXX.class);
// using org.apache.log4j
Logger logger = Logger.getLogger(XXX.class);

这2者间有什么区别呢?

叫人疑惑,上网搜了下,找到了 commons-logging (org.apache.commons.logging) 的说明文档

The Logging Component

When writing a library it is very useful to log information. However there are many logging implementations out there, and a library cannot impose the use of a particular one on the overall application that the library is a part of.

The Logging package is an ultra-thin bridge between different logging implementations. A library that uses the commons-logging API can be used with any logging implementation at runtime. Commons-logging comes with support for a number of popular logging implementations, and writing adapters for others is a reasonably simple task.

Applications (rather than libraries) may also choose to use commons-logging. While logging-implementation independence is not as important for applications as it is for libraries, using commons-logging does allow the application to change to a different logging implementation without recompiling code.

Note that commons-logging does not attempt to initialise or terminate the underlying logging implementation that is used at runtime; that is the responsibility of the application. However many popular logging implementations do automatically initialise themselves; in this case an application may be able to avoid containing any code that is specific to the logging implementation used.

简单的说,commons-logging是一个桥接器,可以在运行时自动加载具体的log实现框架,实现应用程序对具体log实现框架的依赖。这样的好处是,更换log实现框架不需要重新编译你的程序。

So,

1
Log logger = LogFactory.getLog(XXX.class);

is better than

1
Logger logger = Logger.getLogger(XXX.class);

(旧文迁移)