Android开发当中的内存分配错误

基本都是内存不够分配,后来采取的是写外部文件的方法来完成的,参见http://www.iteye.com/topic/1014035
写了一个应用,估计把内存分配不够的问题全遇上了

1、
04-27 09:34:34.292: INFO/dalvikvm(9840): Jit: resizing JitTable from 4096 to 8192
04-27 09:56:19.612: DEBUG/dalvikvm(9840): GC_EXPLICIT freed 69K, 73% free 3838K/13767K, external 1175K/1569K, paused 115ms
04-27 09:56:21.062: INFO/ActivityManager(61): Process com.example.android (pid 9840) has died.
04-27 09:56:21.062: INFO/WindowManager(61): WIN DEATH: Window{40716078 com.example.android/com.example.android.Main paused=false}

04-27 10:32:46.042: INFO/dalvikvm-heap(18965): Clamp target GC heap from 16.226MB to 16.000MB
04-27 10:33:06.522: ERROR/GraphicsJNI(18965): VM won’t let us allocate 4096 bytes
04-27 10:32:30.382: INFO/ActivityManager(61): Process android.process.acore (pid 25279) has died.
04-27 10:32:30.382: INFO/ActivityManager(61): Low Memory: No more background processes.

2、
04-25 21:52:31.226: ERROR/AndroidRuntime(9853): Caused by: java.lang.OutOfMemoryError
04-25 21:52:31.226: ERROR/AndroidRuntime(9853): at java.io.ByteArrayOutputStream.expand(ByteArrayOutputStream.java:93)
04-25 21:52:31.226: ERROR/AndroidRuntime(9853): at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:218)
04-25 21:52:31.226: ERROR/AndroidRuntime(9853): at java.io.OutputStreamWriter.flush(OutputStreamWriter.java:175)
04-25 21:52:31.226: ERROR/AndroidRuntime(9853): at java.io.OutputStreamWriter.convert(OutputStreamWriter.java:252)
04-25 21:52:31.226: ERROR/AndroidRuntime(9853): at java.io.OutputStreamWriter.write(OutputStreamWriter.java:241)
04-25 21:52:31.226: ERROR/AndroidRuntime(9853): at java.io.BufferedWriter.write(BufferedWriter.java:224)
04-25 21:52:31.226: ERROR/AndroidRuntime(9853): at java.io.Writer.write(Writer.java:101)
04-25 21:52:31.226: ERROR/AndroidRuntime(9853): at java.io.Writer.write(Writer.java:155)
04-25 21:52:31.226: ERROR/AndroidRuntime(9853): at org.kxml2.io.KXmlSerializer.endTag(KXmlSerializer.java:508)

3、
04-25 19:51:44.196: INFO/dalvikvm-heap(6801): Forcing collection of SoftReferences for 1783864-byte allocation
04-25 19:51:44.216: ERROR/dalvikvm(6801): HeapWorker is wedged: 34640ms spent inside Lcom/android/internal/os/BinderInternal$GcWatcher;.finalize()V
04-25 19:51:44.216: INFO/dalvikvm(6801): DALVIK THREADS:
04-25 19:51:44.226: INFO/dalvikvm(6801): “main” prio=5 tid=3 VMWAIT
04-25 19:51:44.236: INFO/dalvikvm(6801): | group=”main” sCount=1 dsCount=0 s=N obj=0x4001b268 self=0xbd00
04-25 19:51:44.236: INFO/dalvikvm(6801): | sysTid=6801 nice=0 sched=0/0 cgrp=default handle=-1344001384
04-25 19:51:44.246: INFO/dalvikvm(6801): at android.app.ActivityThread.handleLowMemory(ActivityThread.java:~3840)
04-25 19:51:44.256: INFO/dalvikvm(6801): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1949)
04-25 19:51:44.256: INFO/dalvikvm(6801): at android.os.Handler.dispatchMessage(Handler.java:99)
04-25 19:51:44.266: INFO/dalvikvm(6801): at android.os.Looper.loop(Looper.java:123)
04-25 19:51:44.266: INFO/dalvikvm(6801): at android.app.ActivityThread.main(ActivityThread.java:4363)
04-25 19:51:44.266: INFO/dalvikvm(6801): at java.lang.reflect.Method.invokeNative(Native Method)
04-25 19:51:44.266: INFO/dalvikvm(6801): at java.lang.reflect.Method.invoke(Method.java:521)
04-25 19:51:44.276: INFO/dalvikvm(6801): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:860)
04-25 19:51:44.276: INFO/dalvikvm(6801): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:618)
04-25 19:51:44.276: INFO/dalvikvm(6801): at dalvik.system.NativeStart.main(Native Method)
04-25 19:51:44.286: INFO/dalvikvm(6801): “Binder Thread #8″ prio=5 tid=27 NATIVE
04-25 19:51:44.286: INFO/dalvikvm(6801): | group=”main” sCount=1 dsCount=0 s=N obj=0x440ceef0 self=0x2fa898
04-25 19:51:44.286: INFO/dalvikvm(6801): | sysTid=7415 nice=0 sched=0/0 cgrp=default handle=2884512
04-25 19:51:44.286: INFO/dalvikvm(6801): at dalvik.system.NativeStart.run(Native Method)
04-25 19:51:44.286: INFO/dalvikvm(6801): “Binder Thread #7″ prio=5 tid=25 NATIVE
04-25 19:51:44.286: INFO/dalvikvm(6801): | group=”main” sCount=1 dsCount=0 s=N obj=0x43f82530 self=0x2e3248
04-25 19:51:44.286: INFO/dalvikvm(6801): | sysTid=7256 nice=0 sched=0/0 cgrp=default handle=3027464
04-25 19:51:44.286: INFO/dalvikvm(6801): at dalvik.system.NativeStart.run(Native Method)
04-25 19:51:44.286: INFO/dalvikvm(6801): “Binder Thread #6″ prio=5 tid=23 NATIVE
04-25 19:51:44.296: INFO/dalvikvm(6801): | group=”main” sCount=1 dsCount=0 s=N obj=0x43df2e98 self=0x2f7890
04-25 19:51:44.296: INFO/dalvikvm(6801): | sysTid=7074 nice=0 sched=0/0 cgrp=default handle=3112240
04-25 19:51:44.296: INFO/dalvikvm(6801): at dalvik.system.NativeStart.run(Native Method)
04-25 19:51:44.296: INFO/dalvikvm(6801): “Binder Thread #5″ prio=5 tid=21 NATIVE
04-25 19:51:44.296: INFO/dalvikvm(6801): | group=”main” sCount=1 dsCount=0 s=N obj=0x43ba60d8 self=0x2eea38
04-25 19:51:44.296: INFO/dalvikvm(6801): | sysTid=7008 nice=0 sched=0/0 cgrp=default handle=3057296
04-25 19:51:44.296: INFO/dalvikvm(6801): at dalvik.system.NativeStart.run(Native Method)
04-25 19:51:44.306: INFO/dalvikvm(6801): “Binder Thread #4″ prio=5 tid=19 NATIVE
04-25 19:51:44.306: INFO/dalvikvm(6801): | group=”main” sCount=1 dsCount=0 s=N obj=0x43c3a100 self=0x2e7358
04-25 19:51:44.306: INFO/dalvikvm(6801): | sysTid=6991 nice=0 sched=0/0 cgrp=default handle=3065008
04-25 19:51:44.306: INFO/dalvikvm(6801): at dalvik.system.NativeStart.run(Native Method)
04-25 19:51:44.316: INFO/dalvikvm(6801): “Binder Thread #3″ prio=5 tid=17 NATIVE
04-25 19:51:44.316: INFO/dalvikvm(6801): | group=”main” sCount=1 dsCount=0 s=N obj=0x43c19078 self=0x2e9618
04-25 19:51:44.316: INFO/dalvikvm(6801): | sysTid=6986 nice=0 sched=0/0 cgrp=default handle=3052944
04-25 19:51:44.316: INFO/dalvikvm(6801): at dalvik.system.NativeStart.run(Native Method)
04-25 19:51:44.326: INFO/dalvikvm(6801): “AsyncTask #1″ prio=5 tid=15 RUNNABLE
04-25 19:51:44.326: INFO/dalvikvm(6801): | group=”main” sCount=0 dsCount=0 s=N obj=0x43c3a250 self=0x2cf6e0
04-25 19:51:44.326: INFO/dalvikvm(6801): | sysTid=6977 nice=0 sched=0/0 cgrp=default handle=2946352
04-25 19:51:44.339: INFO/dalvikvm(6801): at java.nio.CharArrayBuffer.(CharArrayBuffer.java:~43)
04-25 19:51:44.346: INFO/dalvikvm(6801): at java.nio.ReadWriteCharArrayBuffer.(ReadWriteCharArrayBuffer.java:47)
04-25 19:51:44.346: INFO/dalvikvm(6801): at java.nio.BufferFactory.newCharBuffer(BufferFactory.java:84)
04-25 19:51:44.346: INFO/dalvikvm(6801): at java.nio.CharBuffer.allocate(CharBuffer.java:57)
04-25 19:51:44.356: INFO/dalvikvm(6801): at java.nio.charset.CharsetDecoder.allocateMore(CharsetDecoder.java:293)
04-25 19:51:44.356: INFO/dalvikvm(6801): at java.nio.charset.CharsetDecoder.decode(CharsetDecoder.java:250)
04-25 19:51:44.356: INFO/dalvikvm(6801): at java.nio.charset.Charset.decode(Charset.java:768)
04-25 19:51:44.356: INFO/dalvikvm(6801): at java.lang.String.(String.java:238)
04-25 19:51:44.366: INFO/dalvikvm(6801): at java.io.ByteArrayOutputStream.toString(ByteArrayOutputStream.java:139)

4、
04-20 09:08:44.060: INFO/global(227): Default buffer size used in BufferedReader constructor. It would be better to be explicit if an 8k-char buffer is required.
04-20 09:38:25.450: ERROR/AndroidRuntime(721): Caused by: java.lang.OutOfMemoryError
04-20 09:38:25.450: ERROR/AndroidRuntime(721): at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:97)
04-20 09:38:25.450: ERROR/AndroidRuntime(721): at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:155)
04-20 09:38:25.450: ERROR/AndroidRuntime(721): at java.lang.StringBuffer.append(StringBuffer.java:214)

5、
04-20 11:54:40.510: ERROR/dalvikvm-heap(4237): Out of memory on a 1672096-byte allocation.
04-20 11:54:40.510: INFO/dalvikvm(4237): “AsyncTask #1″ prio=5 tid=15 RUNNABLE
04-20 11:54:40.510: INFO/dalvikvm(4237): | group=”main” sCount=0 dsCount=0 s=N obj=0x43c1f610 self=0x2cb260
04-20 11:54:40.510: INFO/dalvikvm(4237): | sysTid=4270 nice=10 sched=0/0 cgrp=bg_non_interactive handle=2907136
04-20 11:54:40.510: INFO/dalvikvm(4237): at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:~97)
04-20 11:54:40.510: INFO/dalvikvm(4237): at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:155)
04-20 11:54:40.510: INFO/dalvikvm(4237): at java.lang.StringBuilder.append(StringBuilder.java:216)

6、
04-20 11:54:40.540: ERROR/AndroidRuntime(4237): Uncaught handler: thread AsyncTask #1 exiting due to uncaught exception
04-20 11:54:40.550: ERROR/AndroidRuntime(4237): java.lang.RuntimeException: An error occured while executing doInBackground()
04-20 11:54:40.550: ERROR/AndroidRuntime(4237): at android.os.AsyncTask$3.done(AsyncTask.java:200)
04-20 11:54:40.550: ERROR/AndroidRuntime(4237): at java.util.concurrent.FutureTask$Sync.innerSetException(FutureTask.java:273)
04-20 11:54:40.550: ERROR/AndroidRuntime(4237): at java.util.concurrent.FutureTask.setException(FutureTask.java:124)
04-20 11:54:40.550: ERROR/AndroidRuntime(4237): at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:307)
04-20 11:54:40.550: ERROR/AndroidRuntime(4237): at java.util.concurrent.FutureTask.run(FutureTask.java:137)
04-20 11:54:40.550: ERROR/AndroidRuntime(4237): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1068)
04-20 11:54:40.550: ERROR/AndroidRuntime(4237): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:561)
04-20 11:54:40.550: ERROR/AndroidRuntime(4237): at java.lang.Thread.run(Thread.java:1096)
04-20 11:54:40.550: ERROR/AndroidRuntime(4237): Caused by: java.lang.OutOfMemoryError
04-20 11:54:40.550: ERROR/AndroidRuntime(4237): at java.lang.AbstractStringBuilder.enlargeBuffer(AbstractStringBuilder.java:97)
04-20 11:54:40.550: ERROR/AndroidRuntime(4237): at java.lang.AbstractStringBuilder.append0(AbstractStringBuilder.java:155)
04-20 11:54:40.550: ERROR/AndroidRuntime(4237): at java.lang.StringBuilder.append(StringBuilder.java:216)

7、
04-20 13:32:45.590: ERROR/dalvikvm(7314): Rejecting allocation of 2147483647-element array
04-20 13:32:45.640: ERROR/AndroidRuntime(7314): java.lang.OutOfMemoryError: array size too large

试玩HandlerSocket for MySQL

有这么一个场景,单台MySQL数据库服务器,最大表约500万记录,常用表100万记录,每行记录的数据量不是很大
查询还算频繁,插入修改删除数据不是那么多,多个应用同时访问这个数据库
有个应用就是查询为主,没有修改,少量的删除
查询SQL语句很简单,根据用户ID查询他下面的记录(某些字段),根据记录ID查询整条记录,两表关联查询100万的关联500万
目前应用的实现也很简单,简陋的数据库连接池,客户端发送SQL语句到SQL服务器
现在也没有遇到很大的问题,没有宕机什么的问题,但就是客户端反映慢,但是看服务器的负载都不是很高

所以老大就说太慢了,需要解决

这个慢是比较显然的啊,每次解析执行SQL语句,网络传输,数据库服务器内存较小,缓存不够

所以当然就想到了在应用层加缓存,但是在几个月之前曾经看到过HandlerSocket的简单介绍,只知道它可以加快查询,没有细看

所以突然觉得目前这个也可以改用HandlerSocket,而且更合适,向老大建议,当然建议都是木有用的,因为我做不鸟主,老大有老大的想法

所以只能自己研究看看这个东西适合不了

于是就开始下载编译安装这个东西了,步骤还算简单

具体安装参见installation.en.txt

我开始想用5.1的源码和5.5的二进制版本混合编译(目前我自己用的5.5的MySQL),天知道能不能成功(最后是不能成功的,我又用5.1的源码重新编译了一个MySQL数据库出来,然后编译HandlerSocket并把它安装到5.1中了,后来看到介绍是源码版本一定要和当前安装的数据库的版本一致,之所以需要源码是因为HandlerSocket插件用到了MySQL源码之中的几个头文件之类的吧,总之不看README是很头疼的。比如有的酒店的电梯是要用房卡刷了电梯之后才能按楼层的,README写的清清楚楚,汗)
我的编译步骤如下
$ ./autogen.sh
$ ./configure –with-mysql-source=/home/guohai/dev/src/database/mysql-5.1.57 –with-mysql-bindir=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/bin –with-mysql-plugindir=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/lib/mysql/plugin

$ make
$ sudo make install

mysql> install plugin handlersocket soname ‘handlersocket.so’;
Query OK, 0 rows affected (0.03 sec)

mysql> show plugins;
+—————+——–+—————-+——————+———+
| Name | Status | Type | Library | License |
+—————+——–+—————-+——————+———+
| binlog | ACTIVE | STORAGE ENGINE | NULL | GPL |
| partition | ACTIVE | STORAGE ENGINE | NULL | GPL |
| CSV | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MEMORY | ACTIVE | STORAGE ENGINE | NULL | GPL |
| InnoDB | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MyISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
| MRG_MYISAM | ACTIVE | STORAGE ENGINE | NULL | GPL |
| handlersocket | ACTIVE | DAEMON | handlersocket.so | BSD |
+—————+——–+—————-+——————+———+
8 rows in set (0.00 sec)

这样插件就安装好了,接下来就是装个客户端试试看了,用的淘宝的那个

当然还要在my.cnf中增加类似参数
loose_handlersocket_port = 9998
# the port number to bind to (for read requests)
loose_handlersocket_port_wr = 9999
# the port number to bind to (for write requests)
loose_handlersocket_threads = 16
# the number of worker threads (for read requests)
loose_handlersocket_threads_wr = 1
# the number of worker threads (for write requests)
open_files_limit = 65535
# to allow handlersocket accept many concurrent
# connections, make open_files_limit as large as
# possible.

弄hs4j还是报了几个错误,什么open_table,原因是我表名没有写对,巨汗,不过这个提示也够瓜的
估计也是我没有理解这个东西

然后我又报了个错误
Exception in thread “main” com.google.code.hs4j.exception.HandlerSocketException: Connection has been closed
at com.google.code.hs4j.impl.HSClientImpl.awaitResponse(HSClientImpl.java:417)
at com.google.code.hs4j.impl.HSClientImpl.insert0(HSClientImpl.java:325)
at com.google.code.hs4j.impl.HSClientImpl.insert(HSClientImpl.java:309)
at com.google.code.hs4j.example.HS4JExample.main(HS4JExample.java:29)
这个我把keys写少了一个,所以报错了
总之都是我的问题

就这样解决几个问题之后客户端就基本可用了

然后我还试了下Python的客户端,装好Python的插件http://packages.python.org/python-handler-socket/
就可以开始玩了,照着例子也很简单

看了还算多的文章介绍以及issues,这个东西在某些特定的情况下还是可以开始用的吧,以前提的比较多的问题,什么auto_increment之类的也都解决了

觉得最关键的是就是参数怎么配置,才能让这个东西在需要的场景下发挥最大的能力

还得多看看,多试试才有更充分的把握

参考
https://github.com/ahiguti/HandlerSocket-Plugin-for-MySQL
http://yoshinorimatsunobu.blogspot.com/2010/10/using-mysql-as-nosql-story-for.html 你懂的
https://github.com/killme2008/hs4j
http://whitesock.iteye.com/blog/811339
http://rdc.taobao.com/team/jm/archives/545
http://www.cnblogs.com/inrie/archive/2011/01/28/1946572.html 系列文章
http://huoding.com/2011/04/10/62

流水帐总是很简单,再把编译MySQL的主要过程也记在这里吧,凑点字数
我用的是Ubuntu 10.10 X86,以前编译过很多软件开发包,所以如果编译的时候遇到缺什么包装上就行了
下载就不说了,下载都不会的就不要从源码编译了,我用的是mysql-5.1.57.tar.gz
编译参见INSTALL-SOURCE
2.11.2. Installing MySQL from a Standard Source Distribution

$ ./configure –prefix=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686 –with-unix-socket-path=/tmp/mysql51.sock –with-tcp-port=3307 –with-named-curses-libs=/lib/libncurses.so.5 –enable-assembler –with-plugins=partition,innobase –with-charset=utf8 –with-collation=utf8_general_ci –with-extra-charsets=big5,ascii,gbk,utf8,latin1 –with-big-tables –without-debug

$ make
$ make install

$ cd /home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686
$ chown -R mysql .
$ chgrp -R mysql .

我以前已经装过MySQL5.5所以这些用户以及组都是有的

guohai@KNIGHT:~/dev/a/mysql/mysql-5.1.57-linux2.6-i686$ sudo cp ‘/home/guohai/dev/src/database/mysql-5.1.57/support-files/my-medium.cnf’ ./my.cnf

$ sudo bin/mysql_install_db –user=mysql –basedir=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686 –datadir=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/data

启动数据库
$ sudo bin/mysqld_safe –defaults-file=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/my.cnf –user=mysql &
注意这个参数传递的顺序,我开始一直把–user=mysql放前面,启动的时候报错,MySQL日志记载unknown variable ‘defaults-file’
110601 23:23:28 [ERROR] /home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/libexec/mysqld: unknown variable ‘defaults-file=/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/my.cnf’
110601 23:23:28 [ERROR] Aborting
110601 23:23:28 InnoDB: Starting shutdown…
110601 23:23:33 InnoDB: Shutdown completed; log sequence number 0 44233
110601 23:23:33 [Note] /home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686/libexec/mysqld: Shutdown complete

然后看了mysqld_safe下这个脚本的写法,发现它有对传入的第一个参数判断是否为defaults,于是我就把defaults-file这个参数放到第一位,就正常启动了

一切都好了,然后就是设置了
/home/guohai/dev/a/mysql/mysql-5.1.57-linux2.6-i686$ sudo bin/mysql_secure_installation
一步步来就好了,注意默认my.cnf的位置,我的就放在mysql-5.1.57安装目录下

可能还要修改下MySQL数据文件存放的路径,我最开始编译的时候没有指定,然后它帮我生成的是放在mysql-5.1.57安装目录的一个var目录下面的,而我在my.cnf中指定的是data目录
所以我就修改了一个脚本中的,把var改为data了,具体是哪个脚本忘记了

再重启下,就可以用mysql命令连接上去了,基本正常

MySQL编译简单,关键是编译出自己想要的MySQL,编译出符合自己业务高性能的MySQL就是技术活了,参数很关键
只要参数用的好,没有程序写不鸟;只要工具用的好,没有程序写不鸟;夸张了点

还有刚开始编译出来你可能找不到mysqld这个东西,我反正一开始是没有找到,以为自己编译的出错了,后来才发现在libexec下而不是在bin下面,多用用工具搜索搜索就出来了

另外也可以参见Ubuntu二进制安装MySQL 5.5
地址http://lucane.iteye.com/blog/866355

Lyrics: Rox In The Box-The Decemberists

FROM http://www.songmeanings.net/songs/view/3530822107858854930/

Get the rocks in the box
Get the water right down to your socks
This bulkhead’s built of fallen brethren’s bones
We all do what we can
We endure our fellow man
And we sing our songs to the headframe’s creaks and moans

And it’s one, two, three
On the wrong side of the lea
What were you meant for?
What were you meant for?
And it’s seven, eight, nine
You get your shovel back in line
And if you ever make it to ten
You won’t make it again
And if you ever make it to ten
You won’t make it again

And you won’t make a dime
On this gray granite mountain mine
Of dirt you’re made, of dirt you will return
So while we’re living here
Let’s get this little one thing clear
There’s plenty of men to die, don’t jump your turn

And it’s one, two, three
On the wrong side of the lea
What were you meant for?
What were you meant for?
And it’s seven, eight, nine
You get your shovel back in line
And if you ever make it to ten
You won’t make it again
And if you ever make it to ten
You won’t make it again

And it’s one, two, three
On the wrong side of the lea
What were you meant for?
What were you meant for?
And it’s seven, eight, nine
You get your shovel back in line
And if you ever make it to ten
You won’t make it again
And if you ever make it to ten
You won’t make it again
And if you ever make it to ten
You won’t make it again

Java多线程消费者问题

有一个简单的场景:一个Queue,包含很多Task,现在的程序要去Queue中取Task来执行
取的时候是多线程去取的,但是一旦有个线程拿到Queue中的某个Task,其他线程就必须退出

拿到Task的线程根据Task里面的内容,将具体工作(因为一个Task中可能存在多个具体的工作)分发给最终的工作执行线程(ProductionLineA或者ProductionLineB)去执行,工作执行线程执行完一个具体的工作后就把信息更新到Task中去,拿到Task的线程会不断检测当前Task里面的具体工作是否都执行完毕,如果完毕,拿到Task的线程退出,Task也会从队列中销毁

通俗的来讲也就是多线程抢任务,抢到一个任务后,把具体的工作分给多个线程去执行,它还可以回来继续抢其他的任务

注意点
1、抢任务的时候可以有多个线程去抢,但是最终一个任务只能有一个线程拿到,也就是执行任务分发的那段代码只能由某个线程执行一次,否则到后面具体的工作可能被执行多次
所以对已经抢到的任务打标记要只能打一次,不能存在A线程判断的时候标记没有被打上,B线程判断也没有,A打标记,B也打标记,这个到后面肯定就乱了,用double check来实现?
2、工作执行线程需要把执行完的情况更新到Task中去,如果第1点中具体工作分正确了,这里应该能保证不会有多个线程执行同样的具体工作,所以把执行完的情况写回Task的时候不用同步保证也行

目前任务标记用的是Thread,这个是不是不合适?
检测是否执行完毕的代码是单线程的,见WatchDog类,目前还缺超时检测,多线程的检测有没有必要?
锁加的恰不恰当?

多线程的代码确实比较难编写和调试

目前我实现了一个代码,看似没有问题,通过测试,分析日志,发现都还正常

			Lock lock = new ReentrantLock();
			lock.tryLock();
			// critical area start
			if (null != task.getExclusive()
					&& task.getExclusive() != Thread.currentThread()) {
				// 如果有人占了,那么自动退出
				// 这里怎么实现比较好?
				continue;
			} else {
				if (null == task.getExclusive()) {
					task.setExclusive(Thread.currentThread());
				}
			}
			// critical area end
			lock.unlock();

关键代码中,目前这一段因该还有修改的空间

运行文件run.sh
日志分析LogAnalyzer.java,目前只能说是大概的分析有没有明显出错的地方,比如执行的数量对不对

日志:

queue has 60 wks
can only be executed 10 times...
can only be executed 10 times...
can only be executed 10 times...
can only be executed 10 times...
can only be executed 10 times...
can only be executed 10 times...
can only be executed 10 times...
can only be executed 10 times...
can only be executed 10 times...
shan$evol in task[2] is executing by ProductionLineB...
shan$love in task[8] is executing by ProductionLineA...
shan$love in task[3] is executing by ProductionLineA...
can only be executed 10 times...
shan$love in task[0] is executing by ProductionLineA...
shan$vole in task[3] is executing by ProductionLineB...
shan$elvo in task[8] is executing by ProductionLineB...
shan$ovel in task[8] is executing by ProductionLineB...
shan$vole in task[8] is executing by ProductionLineB...
shan$love in task[5] is executing by ProductionLineA...
shan$love in task[5] is executing by ProductionLineA...
shan$love in task[4] is executing by ProductionLineA...
shan$love in task[7] is executing by ProductionLineA...
shan$leov in task[8] is executing by ProductionLineB...
shan$elvo in task[4] is executing by ProductionLineB...
shan$ovel in task[4] is executing by ProductionLineB...
shan$vole in task[4] is executing by ProductionLineB...
shan$leov in task[4] is executing by ProductionLineB...
shan$ovel in task[5] is executing by ProductionLineB...
shan$evol in task[5] is executing by ProductionLineB...
shan$elvo in task[5] is executing by ProductionLineB...
shan$vole in task[1] is executing by ProductionLineB...
shan$ovel in task[1] is executing by ProductionLineB...
shan$leov in task[1] is executing by ProductionLineB...
shan$evol in task[1] is executing by ProductionLineB...
shan$vole in task[6] is executing by ProductionLineB...
shan$evol in task[3] is executing by ProductionLineB...
shan$elvo in task[7] is executing by ProductionLineB...
shan$ovel in task[7] is executing by ProductionLineB...
shan$leov in task[7] is executing by ProductionLineB...
shan$love in task[9] is executing by ProductionLineA...
shan$evol in task[7] is executing by ProductionLineB...
shan$ovel in task[6] is executing by ProductionLineB...
task0 is done...
shan$evol in task[6] is executing by ProductionLineB...
shan$vole in task[9] is executing by ProductionLineB...
shan$leov in task[6] is executing by ProductionLineB...
shan$evol in task[9] is executing by ProductionLineB...
shan$ovel in task[5] is executing by ProductionLineB...
shan$leov in task[4] is executing by ProductionLineB...
shan$leov in task[8] is executing by ProductionLineB...
shan$leov in task[1] is executing by ProductionLineB...
task2 is done...
shan$elvo in task[7] is executing by ProductionLineB...
shan$evol in task[5] is executing by ProductionLineB...
shan$ovel in task[7] is executing by ProductionLineB...
shan$elvo in task[5] is executing by ProductionLineB...
shan$evol in task[3] is executing by ProductionLineB...
shan$evol in task[6] is executing by ProductionLineB...
shan$vole in task[1] is executing by ProductionLineB...
shan$ovel in task[1] is executing by ProductionLineB...
shan$vole in task[9] is executing by ProductionLineB...
shan$evol in task[7] is executing by ProductionLineB...
shan$vole in task[6] is executing by ProductionLineB...
shan$evol in task[9] is executing by ProductionLineB...
shan$leov in task[8] is executing by ProductionLineB...
task4 is done...
shan$evol in task[5] is executing by ProductionLineB...
shan$vole in task[1] is executing by ProductionLineB...
shan$ovel in task[1] is executing by ProductionLineB...
task3 is done...
shan$evol in task[7] is executing by ProductionLineB...
task6 is done...
shan$leov in task[8] is executing by ProductionLineB...
task9 is done...
task5 is done...
shan$vole in task[1] is executing by ProductionLineB...
shan$evol in task[7] is executing by ProductionLineB...
task8 is done...
task1 is done...
task7 is done...

完整源码:
multiple-consumers-problem

JVM标记贴

    JVM参数

  • JMX参数
  • -Dcom.sun.management.jmxremote.port=1090
    -Dcom.sun.management.jmxremote.ssl=false
    -Dcom.sun.management.jmxremote.authenticate=false

  • 扩展参数
  • -XX:+PrintGC
    -XX:+PrintGCDetails
    -XX:+PrintGCTimeStamps 和PrintGC以及PrintGCDetails一起使用
    -Xloggc:gc.log

    -XX:+DoEscapeAnalysis
    -XX:+HeapDumpOnOutOfMemoryError

原码,反码,补码备忘

又是一篇炒现饭的帖子,主要是经常忘记,记在这里

首先理解位bit(Binary digit http://en.wikipedia.org/wiki/Bit)

以下都以二进制为例

比如一个8位的存储单元,那么它就可以保存256种不同状态,如果是无符号数的话,那么就是0 ~ 2^8-1,即00000000 ~ 11111111

那么对于有符号的数就是-2^7 ~ 2^7-1,最高位拿来存储符号了,0为正,1为负,即10000001 ~ 01111111

十进制 原码 反码 补码
127 01111111 01111111 01111111
-127 11111111 10000000 10000001
128 010000000 010000000 010000000
-128 10000000
45 00101101 00101101 00101101
-45 10101101 11010010 11010011
1 00000001 00000001 00000001
-1 10000001 11111110 11111111

上表中红色的数是表示溢出的,8位的存储空间无法表示这样的有符号数

计算机中的有符号数都是以补码的形式存放的,可以理解为只有有符号数才有原码,反码,补码
正数的原码,反码,补码表示都一样
负数的原码为其绝对值的原码,但符号位为1;反码就是保持原码的符号位不变,其他位按位取反;补码就是反码加1

引用Wikipedia
“一个数字的二补数就是将该数字作位反相运算(即一补数),再将结果加 1,即为该数字的二补数。”

为什么要引入原码,反码,补码这些概念
参见:http://dev.csdn.net/htmls/17/17680.html

我们最需要关注的是补码,引用作者的原话是

补码的设计目的是:
⑴使符号位能与有效值部分一起参加运算,从而简化运算规则
⑵使减法运算转换为加法运算,进一步简化计算机中运算器的线路设计

引用自http://zh.wikipedia.org/wiki/二補數
在二补数系统中,一个负数就是用其对应正数的二补数来表示。
00000100 4
11111011 按位取反
11111100 二补数(-4)

减法可以用一个数加上另一个数的二补数来表示
5
-4
———-
1

00000101
+11111100
———-
000000001

另外字节(Byte),字(Word),双字,四字,字长这些概念都需要理解

阮一峰有个帖子讲的也很清楚明了

参考

http://zh.wikipedia.org/wiki/原码
http://zh.wikipedia.org/wiki/反码
http://zh.wikipedia.org/wiki/二補數
http://en.wikipedia.org/wiki/One’s_complement
http://en.wikipedia.org/wiki/Two’s_complement

自己动手编译OpenJDK

无聊的时候决定自己动手编译个JDK,曾经想在X86上编译一个Dalvik出来玩玩,但基于那玩意是高手才玩的起的,所以自己那个想法就不了了知了
看了一圈后发现OpenJDK还是比较容易编译的,所以就来炒份现饭
现代JDK这么容易自己编译还是归功于开源和社区

开场白就说这么多,下面是过程记录
下载源码,解压,阅读README和README-builds.html,然后就可以开工了

我是在Linux 2.6.35-28-generic #50-Ubuntu i686 GNU/Linux上编译的,我编译的是openjdk-7-ea-src-b141-05_may_2011这个版本

主要需要以下条件
Basic Linux Check List
1. Install the Bootstrap JDK, set ALT_BOOTDIR.
2. Optional Import JDK, set ALT_JDK_IMPORT_PATH.(可以不要,如果你是完全编译的话)
3. Install or upgrade the FreeType development package.(这个基本有了)
4. Install Ant 1.7.1 or newer, make sure it is in your PATH.

然后在环境变量下做些设置
ALT_BOOTDIR=$SUN_JDK1.6.0_25_HOME(我是用的SUN的JDK6来作为Bootstrap JDK的)
LANG=C(需要设置成这样,否则编译会报错,Linux一般是zh_CN.utf8改下就可以)
ALT_CUPS_HEADERS_PATH=$CUPS_HOME/include(何为CUPS见此)

另外环境变量中不要出现JAVA_HOME,CLASSPATH,有的话就把它们注释掉

记得使修改过的环境变量生效

然后就cd到$openjdk_src下开始执行make sanity检测
成功的话就执行make all ALLOW_DOWNLOADS=true WARNINGS_ARE_ERRORS=开始较为耗时的编译之旅

这两个参数的意思还是比较明白
ALLOW_DOWNLOADS=true就是编译的时候发现缺少某些预知的源码或依赖时,它会自动去下载,前提是必须联网
WARNINGS_ARE_ERRORS=就是禁止编译器把一些警告当错误,如果不加这个就可能会因为类似于这样的cc1plus: warnings being treated as errors提示而终止编译,这不是我们所希望的

但是不要高兴的太早,多多少少可能会遇到点问题,不过找找Google应该都能解决

下面贴出我在编译的时候遇到的问题
1、
/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/hotspot/src/os/linux/vm/os_linux.cpp
cc1plus: warnings being treated as errors
/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/hotspot/src/os/linux/vm/os_linux.cpp: In static member function ‘static bool os::Linux::hugetlbfs_sanity_check(bool, size_t)’:
/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/hotspot/src/os/linux/vm/os_linux.cpp:2853: error: use of assignment suppression and length modifier together in gnu_scanf format
make[6]: *** [os_linux.o] Error 1
make[6]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/build/linux-i586/hotspot/outputdir/linux_i486_compiler2/product’

WARNINGS_ARE_ERRORS=

2、
../../../src/solaris/native/sun/awt/awt.h:38: fatal error: X11/Intrinsic.h: No such file or directory
compilation terminated.
make[5]: *** [/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/build/linux-i586/tmp/sun/sun.awt/awt/obj/BufImgSurfaceData.o] Error 1
make[5]: *** Waiting for unfinished jobs….
make[5]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/jdk/make/sun/awt’
make[4]: *** [library_parallel_compile] Error 2
make[4]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/jdk/make/sun/awt’
make[3]: *** [all] Error 1
make[3]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/jdk/make/sun’
make[2]: *** [all] Error 1
make[2]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/jdk/make’
make[1]: *** [jdk-build] Error 2
make[1]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011′
make: *** [build_product_image] Error 2

sudo apt-get install libxt-dev

3、
../../../src/solaris/native/sun/xawt/XToolkit.c:48: fatal error: X11/extensions/XTest.h: No such file or directory
compilation terminated.
make[5]: *** [/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/build/linux-i586/tmp/sun/sun.awt.X11/xawt/obj/XToolkit.o] Error 1
make[5]: *** Waiting for unfinished jobs….
make[5]: Leaving directory `/home/guohai/dev/src/java/openjdk-7-ea-src-b141-05_may_2011/jdk/make/sun/xawt’
make[4]: *** [library_parallel_compile] Error 2

sudo apt-get install libxtst-dev

从2011-05-12 20:39开始编译,遇到问题,前前后后经历了近一个小时
另外如果编译出错了,并且你解决了错误,貌似你只需要继续执行make all ALLOW_DOWNLOADS=true WARNINGS_ARE_ERRORS=
就可以了,它会接着编译,而不需要从头开始编译
当你看到下面这种提示时
— Build times ———-
Target all_product_build
Start 2011-05-12 21:28:48
End 2011-05-12 21:37:48
00:00:04 corba
00:00:12 hotspot
00:00:03 jaxp
00:00:05 jaxws
00:08:32 jdk
00:00:04 langtools
00:09:00 TOTAL
————————-
应该就可以完工了,cd到$openjdk_src/build/platform/下,看看j2sdk-image和j2re-image中的内容是不是很熟悉?
cd$openjdk_src/build/platform/bin/下执行如下命令就应该可以看到结果,当然版本,硬件架构可能不一样
总之你就自己编译出了一个带自己名字的JDK了

$openjdk_src/build/linux-i586/bin$ ./java -version
openjdk version “1.7.0-internal”
OpenJDK Runtime Environment (build 1.7.0-internal-guohai_2011_05_12_20_39-b00)
OpenJDK Server VM (build 21.0-b11, mixed mode)

$openjdk_src/build/linux-i586/bin$ ./javac -version
javac 1.7.0-internal

有图
compile-my-openjdk

编译成功之后再把那些需要改回来的东西改回来


sudo apt-get install libfreetype6-dev
sudo apt-get install libasound2-dev
sudo apt-get install libcups2-dev

MySQL存储过程备忘

突然要弄MySQL存储过程
好久没有怎么接触过数据库存储过程了,都忘了,记在这里

create table email (id int auto_increment primary key, name text, content text, int size);
create table email_trash (id int auto_increment primary key, name text, content text, int size);

create table attachment (id int auto_increment primary key, email_id int, url text);
create table attachment_trash (id int auto_increment primary key, email_id int, url text);
delimiter //
drop procedure if exists fix_data_trash_magic //
CREATE PROCEDURE `fix_data_trash_magic`()
BEGIN

    DECLARE done INT DEFAULT 0;

    DECLARE _amount INT DEFAULT 0;
    DECLARE _size INT;
    DECLARE _id INT;
    DECLARE _newid LONG;

    DECLARE email_loop CURSOR FOR select id, size from email_trash where name = 'jemmy';

    DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = 1;

    OPEN email_loop;
        REPEAT
        FETCH email_loop INTO _id, _size;
            IF NOT done THEN
                insert into email(name, content, size) select name,content,size from email_trash where id = _id;

                select last_insert_id() into _newid;

                insert into attachment(email_id, url) select _newid,url from attachment_trash where email_id = _id;
                
                SET _amount = _size + _amount;
            END IF;
        UNTIL done END REPEAT;
    CLOSE email_loop;
END
call fix_data_trash_magic()//

另外还有常用的几个命令

mysql> show procedure status;
mysql> show create procedure procedure_name; // SHOW THE CREATING SQL OF THE SP

包含异常处理,保存点的存储过程

DELIMITER ;;
CREATE PROCEDURE ps_with_exception_etc(in_domain varchar(50), in_account varchar(32), in_passwd varchar(32), OUT out_no int, OUT out_msg varchar(50))
BEGIN
    DECLARE s_fullid varchar(100);

    DECLARE EXIT HANDLER FOR SQLSTATE '23000'
    BEGIN
        SET out_no = -1;
        SET @prc_work_error = 'Repeated key';
        ROLLBACK TO sp_01;
    END;

    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        SET out_no = -1;
        SET @prc_work_error = 'Unknown error';
        ROLLBACK TO sp_01;
    END;

    -- 声明之类的都应该放在前面

    SET AUTOCOMMIT = 0;
        SELECT concat(in_account, '@', in_domain) INTO s_fullid;
        START TRANSACTION;
        SAVEPOINT sp_01;
        -- DO OO HERE
        -- DO XX HERE
        COMMIT;
    SET out_no = 1;
END
;;
DELIMITER ;

存储过程中的动态SQL
这里只是演示动态SQL的写法,要是真这么写太浪费了,直接可以写出来的SQL就不需要动态SQL

DELIMITER ;;
CREATE PROCEDURE ps_with_dynamic_sql(in_domain varchar(50), in_account varchar(32), OUT out_no int, OUT out_msg varchar(50))
BEGIN
    DECLARE fullid varchar(100);

    DECLARE DROP_USER_TABLEK varchar(500);
    DECLARE DROP_USER_MAILS varchar(500);

    DECLARE EXIT HANDLER FOR SQLEXCEPTION
    BEGIN
        SET out_no = -1;
        SET @prc_work_error = 'Unknown error';
        ROLLBACK TO sp_01;
    END;

    SET AUTOCOMMIT = 0;
    START TRANSACTION;
    SAVEPOINT sp_01;

        SET @MyQuery = concat('DELETE FROM tablek WHERE uuid = ', in_account);
        PREPARE DROP_USER_TABLEK from @MyQuery;

        SET @MyQuery = concat('DELETE FROM mails WHERE uuid = ', in_account);
        PREPARE DROP_USER_MAILS from @MyQuery;

        EXECUTE DROP_USER_TABLEK;
        EXECUTE DROP_USER_MAILS;
    COMMIT;
    SET out_no = 1;
END
;;
DELIMITER ;

还有个简单的

CREATE PROCEDURE `insert_data`(in item integer)
BEGIN
    DECLARE counter INT;
    SET counter = item;
    WHILE counter >= 1 DO
        // DO YOUR BIZ HERE
        SET counter = counter - 1;
    END WHILE;
END

另外在存储过程中声明变量,游标,处理器是有一定的顺序的,否则会报语法错误

触发器

create trigger insert_after_addr
after insert on addr
for each row
begin
set @aid=new.id;
insert into data(addr_id, type, value) values (@aid, 5, 'hello trigger');
end
show triggers//
drop trigger insert_after_addr//
alter table data modify id int(22) not null auto_increment
CREATE trigger insert_before_tablek
    before
        insert on tablek
    for each row
        begin
            set new.status = 25;
        end

Post Office Protocol – Version 3 阅读杂记

Post Office Protocol – Version 3 http://www.rfc-editor.org/rfc/rfc1939.txt

A POP3 session progresses through a number of states during its
lifetime. Once the TCP connection has been opened and the POP3
server has sent the greeting, the session enters the AUTHORIZATION
state. In this state, the client must identify itself to the POP3
server. Once the client has successfully done this, the server
acquires resources associated with the client’s maildrop, and the
session enters the TRANSACTION state. In this state, the client
requests actions on the part of the POP3 server. When the client has
issued the QUIT command, the session enters the UPDATE state. In
this state, the POP3 server releases any resources acquired during
the TRANSACTION state and says goodbye. The TCP connection is then
closed.

Minimal POP3 Commands:

USER name valid in the AUTHORIZATION state
PASS string
QUIT

STAT valid in the TRANSACTION state
LIST [msg]
RETR msg
DELE msg
NOOP
RSET
QUIT

Optional POP3 Commands:

APOP name digest valid in the AUTHORIZATION state

TOP msg n valid in the TRANSACTION state
UIDL [msg]

POP3 Replies:

+OK
-ERR

Example POP3 Session

S:
C:
S: +OK POP3 server ready <1896.697170952@dbc.mtview.ca.us>
C: APOP mrose c4c9334bac560ecc979e58001b3e22fb
S: +OK mrose’s maildrop has 2 messages (320 octets)
C: STAT
S: +OK 2 320
C: LIST
S: +OK 2 messages (320 octets)
S: 1 120
S: 2 200
S: .
C: RETR 1
S: +OK 120 octets
S: S: .
C: DELE 1
S: +OK message 1 deleted
C: RETR 2
S: +OK 200 octets
S: S: .
C: DELE 2
S: +OK message 2 deleted
C: QUIT
S: +OK dewey POP3 server signing off (maildrop empty)
C:
S:

Responses to certain commands are multi-line. In these cases, which
are clearly indicated below, after sending the first line of the
response and a CRLF, any additional lines are sent, each terminated
by a CRLF pair. When all lines of the response have been sent, a
final line is sent, consisting of a termination octet (decimal code
046, “.”) and a CRLF pair. If any line of the multi-line response
begins with the termination octet, the line is “byte-stuffed” by
pre-pending the termination octet to that line of the response.
Hence a multi-line response is terminated with the five octets
“CRLF.CRLF”. When examining a multi-line response, the client checks
to see if the line begins with the termination octet. If so and if
octets other than CRLF follow, the first octet of the line (the
termination octet) is stripped away. If so and if CRLF immediately
follows the termination character, then the response from the POP
server is ended and the line containing “.CRLF” is not considered
part of the multi-line response.

POP3命令实例
telnet mail-host 110

USER hello@example.com

PASS 111111

STAT
+OK 13 332023

LIST
+OK POP3 clients that break here, they violate STD53.
1 202
2 73113
3 402
4 402
5 80267
6 13380
7 407
8 755
9 115473
10 16932
11 30343
12 169
13 178
.

LIST 2
+OK 2 73113

UIDL 3
+OK 3 uuxvo-13134583174281799

RETR 1 #应该返回第1封邮件的所有内容
+OK
Date: Mon, 15 Aug 2011 14:37:42 +0800 (CST)
From:
To: hello@example.com
Message-ID: <6879346.11.1313450262952.JavaMail.root@knight>
Subject: =?UTF-8?Q?=E6=B5=8B=E8=AF=952011=E5=B9=B48=E6=9C=881?=
=?UTF-8?Q?5=E6=97=A5=E6=98=9F=E6=9C=9F=E4=B8=8014:37?=
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=”—-=_Part_10_343799.1313390262951″

——=_Part_10_343799.1313390262951
Content-Type: text/html;charset=gbk
Content-Transfer-Encoding: base64

PGh0bWw+PGhlYWQ+PG1ldGEgaHR0cC1lcXVpdj0iQ29udGVudC1UeXBlIiBjb250ZW50PSJ0ZXh0
L2h0bWw7IGNoYXJzZXQ9Z2JrIj48dGl0bGU+PC90aXRsZT48L2hlYWQ+PGJvZHk+suLK1DIwMTHE
6jjUwjE1yNXQx8ba0rsxNDozNzxkaXY+suLK1DIwMTHE6jjUwjE1yNXQx8ba0rsxNDozNzwvZGl2
PjwvYm9keT48L2h0bWw+
——=_Part_10_343799.1313390262951–
.

DELE 1
+OK

RSET
+OK

TOP 2 3 #应该返回第2封邮件的邮件头+前3行
+OK
Date: Tue, 16 Aug 2011 09:31:27 +0800 (CST)
From: noreply@example.com
To: hello@example.com
Message-ID: <27682895.1.1313458287431.JavaMail.root@SERVER2>
Subject: =?gbk?B?PLLiytTNy9DFMjAxMcTqONTCMTbI1dDHxtq2/jA5OjI3Ps22td3Kp7Dc?=
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary=”—-=_Part_0_31571602.1313458287431″

——=_Part_0_31571602.1313458287431
Content-Type: text/html;charset=gbk
Content-Transfer-Encoding: base64
.

TOP 1 #应该返回第1封邮件的邮件头

QUIT #断开连接

Milestone刷机神马的

这里是大杂烩,参考了
http://blog.milkpc.com/archives/32
http://and-developers.com/sbf:milestone221

最开始用官方升级软件刷了个2.2,结果重启,死机都来了,如今有下了个UK的底包(Milestone_2.2.1_UK_Package.sbf),据说是很稳定的
刷了之后发热量大,据说2.2都这样?
无法输入中文,如果你是root之后的话,有解决办法

刷SHOLS_U2_05.26.4_SIGNED_UCASHLSMR2EMEAB1B806E.0R_PDS003_USAMS1FRYORTSEA_P013_A009_HWp2a_WIG145030_Service1FF.sbf
完成之后一直在ANDROID那几个大字那里循环,无法开机,WIPE一次之后可以用了
真有人把Milestone刷成石头么?