甲午年,至关重要的使命

接下来的一年于我而言的重要,直接可以用人生选择来形容。我已经将创业作为了自己的发展方向和主业。接下来的半年,算是正式创业的开端。从此,作为一个职业创业者,今年的使命可以说「亚历山大」。
更核心的时期,应该是前半年,也就是我毕业之前的时间节点。在这段时间,我的业绩能做到如何,企业能否起步,将是相当重要的一个转折点。
加油。

蔡元培不肯再任北大校长的宣言

(1919年6月15日)

(一)我绝对不能再作那政府任命的校长:为了北京大学校长是简任职,是半官僚性质,便生出那许多官僚的关系,那里用呈,那里用咨,天天有一大堆无聊的照例的公牍。要是稍微破点例,就要呈请教育部,候他批准。什么大学文、理科叫做本科的问题,文、理合办的问题,选科制的问题,甚至小到法科暂省学长的问题,附设中学的问题,都要经那拘文牵义的部员来斟酌。甚而部里还常常派了什么一知半解的部员来视察,他报告了,还要发几个训令来训饬几句。我是个痛恶官僚的人,能甘心仰这些官僚的鼻息么?我将进北京大学的时候,没有想到这一层,所以两年有半,天天受这个苦痛。现在苦痛受足了,好容易脱离了,难道还肯投入去么?

(二)我绝对不能再做不自由的大学校长:思想自由,是世界大学的通例。得意志帝政时代,是世界著名开明专制的国,他的大学何等自由。那美、法等国,更不必说了。北京大学,向来受旧思想的拘束,是很不自由的。我进去了,想稍稍开点风气,请了几个比较的有点新思想的人,提倡点新的学理,发布点新的印刷品,用世界的新思想来比较,用我的理想来批评,还算是半新的。在新的一方面偶有点儿沾沾自喜的,我还觉得好笑。哪知道旧的一方面,看了这点半新的,就算"洪水猛兽"一样了。又不能用正当的辩论法来干涉了,国务院来干涉了,甚而什么参议院也来干涉了,世界哪有这种不自由的大学么?还要我去充这种大学的校长么?

(三)我绝对不能再到北京的学校任校长:北京是个臭虫窠。无论何等高尚的人物,无论何等高尚的事业,一到北京,便都染了点臭虫的气味。我已经染了两年有半了,好容易逃到故乡的西湖、鉴湖,把那个臭气味淘洗干净了。难道还要我再作逐臭之夫,再去尝尝这气味么?

我想有人见了我这一段的话,一定要把'我不入地狱,谁入地狱'的话来劝勉我。但是我现在实在没有到佛说这句话的时候的程度,所以只好谨谢不敏了。

附:爱蔡孑民者启

右宣言闻尚是蔡君初出京时所草,到上海后,本拟即行宣布,后因北京挽留之电,有友人劝其婉复,免致以个人去留问题与学生所争政治问题,永结不解之缘,故有以条件的允任维持之电,后来又有卧病不行之电,均未将真意说出。闻其意,无论如何,决不回校也。鄙人抄得此宣言书,觉与北京各报所载启事,及津浦车站告友之言,均相符合,必是祭君本意。个人意志自由,本不可以多数压制之,且为社会上留此一个干净人,使不与政治问题发生关系,亦是好事。故特为宣布,以备挽留蔡君者之参考焉。爱蔡孑民者启

据蔡元培手稿

选自《蔡元培全集》第3卷,中华书局1984年版

另附《蔡元培先生任北京大学校长之就职演说》

五年前,严几道先生为本校校长时,余方服务教育部,开学日曾有所贡献于学校。诸君多自预科毕业而来,想必闻知。士别三日,刮目相见,况时阅数载,诸君较昔当为长足之进步矣。予今长斯校,请以三事为诸君告:

一曰抱定宗旨。诸君来此求学,必有一定宗旨,欲求宗旨之正大与否,必先知大学之性质。今人肄业专门学校,学成任事,此固势所必然。而在大学则不然,大学者,研究高深学问者也。外人每指摘本校之腐败,以求学于此者,皆有做官发财思想,故毕业预科者,多入法科,入文科者甚少,入理科者尤少,盖以法科为干禄之终南捷径也。因做官心热,对于教员,则不问其学问之浅深,惟问其官阶之大小。官阶大者,特别欢迎,盖为将来毕业有人提携也。现在我国精于政法者,多入政界,专任教授者甚少,故聘请教员,不得不聘请兼职之人,亦属不得已之举。究之外人指摘之当否,姑不具论,然弭谤莫如自修,人讥我腐败,问心无愧,于我何惧?果欲达其做官发财之目的,则北京不少专门学校,入法科者尽可肄业于法律学堂,入商科者亦可投考商业学校,又何必来此大学?所以诸君须抱定宗旨,为求学而来,入法科者,非为做官;入商科者,非为致富。宗旨既定,自趋正轨,诸君肄业于此,或三年,或四年,时间不为不多,苟能爱惜分阴,孜孜求学,则求造诣,容有底止。若徒志在做官发财,宗旨既乖,趋向自异。平时则放荡冶游,考试则熟读讲义,不问学问之有无,惟争分数之多寡;试验既终,书籍束之高阁,毫不过问,敷衍三、四年,潦草塞责,文凭到手,即可借此活动于社会,岂非与求学初衷大相背驰乎?光阴虚度,学问毫无,是自误也。且辛亥之役,吾人之所以革命,因清廷官吏之腐败。即在今日,吾人对于当轴多不满意,亦以其道德沦丧。今诸君苟不于此时植其基,勤其学,则将来万一因生计所迫,出而仕事,但任讲席,则必贻误学生;置身政界,则必贻误国家。是误人也。误已误人,又岂本心所愿乎?故宗旨不可以不正大。此余所希望于诸君者一也。

二曰砥砺德行。方今风俗日偷,道德沦丧,北京社会,尤为恶劣,败德毁行之事,触目皆是,非根基深固,鲜不为流俗所染。诸君肄业大学,当能束身自爱。然国家之兴替,视风俗之厚薄。流俗如此,前途何堪设想。故必有卓绝之士,以?身作则,力矫颓俗,诸君为大学学生,地位甚高,肩此重任,责无旁贷,故诸君不惟思所以感已,更必有以励人。苟德之不修,学之不讲,同乎流俗,合乎污世,已且为人轻侮,更何足以感人。然诸君终日伏首案前,芸芸攻苦,毫无娱乐之事,必感身体上之苦痛。为诸君计,莫如以正当之娱乐,易不正当之娱乐,庶几道德无亏,而于身体有益。诸君入分科时,曾填写愿书,遵守本校规则,苟中道而违之,岂非与原始之意相反乎?故品行不可以不谨严。此余所希望于诸君者二也。

三曰敬爱师友。教员之教授,职员之任务,皆以图诸君求学便利,诸君能无动于衷乎?自应以诚相待,敬礼有加。至于同学共处一室,尤应互相亲爱,庶可收切磋之效。不惟开诚布公,更宜道义相勗,盖同处此校,毁誉共之。同学中苟道德有亏,行有不正,为社会所訾詈,已虽规行矩步,亦莫能辨,此所以必互相劝勉也。余在德国,每至店肆购买物品,店主殷勤款待,付价接物,互相称谢,此虽小节,然亦交际所必需,常人如此,况堂堂大学生乎?对于师友之敬爱,此余所希望于诸君者三也。

余到校视事仅数日,校事多未详悉,兹所计划者二事:一曰改良讲义。诸君既研究高深学问,自与中学、高等不同,不惟恃教员讲授,尤赖一已潜修。以后所印讲义,只列纲要,细微末节,以及精旨奥义,或讲师口授,或自行参考,以期学有心得,能裨实用。二曰添购书籍。本校图书馆书籍虽多,新出者甚少,苟不广为购办,必不足供学生之参考。刻拟筹集款项,多购新书,将来典籍满架,自可旁稽博采,无虞缺乏矣。今日所与诸君陈说者只此,以后会晤日长,随时再为商榷可也。

蔡元培
1917年4月

Mono 环境下跟踪和优化 .NET 程序内存分配

.NET 程序在 Mono 环境之下的内存分配和在 Microsoft .NET Framework 环境是不太相同的,虽然它们的兼容性很高,但仍有区别。尤其是在 GC 方面,Mono 的 GC 实现似乎仍有不足(在 3.0.6 版本下),据我观察,有一些大对象(估计是二代对象,譬如很大的 byte[] 数组)的释放仍然有些问题。下面就以 TCP 服务器端程序为例。

场景

假设在服务器端有这样的语句,用来备份 SQLite 数据库文件:

private void BackupDB(string dest)
{
    byte[] content = getAllContent(this.DBFile);
    File.WriteAllBytes(dest, Zip.Compress(content));
    content = null;
}
 
private byte[] getAllContent(string path)
{
    try
    {
        byte[] b;
        using (FileStream fs = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            b = new byte[fs.Length];
            fs.Read(b, 0, b.Length);
        }
        return b;
    }
    catch
    {
        throw new Exception("数据库正在读写,请稍后再备份。");
    }
}

备份数据库需要读取整个数据库。这段代码直接将整个数据库的本地文件镜像读出,并放在了一个 Byte[] 数组之中,作为 getAllContent 函数的返回值。在读取之后, getAllContent 方法的 b 中存储了大量的字节。另外,在 BackupDB 函数中,使用 Zip.Compress 函数压缩数据库后,也会返回一个较大的 byte[] 数组。

经过测试,这段代码在 Windows 的 .NET Framework 平台下工作没有问题。因为这些大对象可以几乎在使用后即刻被销毁。但是,在 Mono 下,即使在代码的最后显式调用 GC.Collect() 方法进行全代垃圾回收,也无法减少内存。

使用 Profiler 跟踪内存分配和垃圾回收

虽然我们可能不知道原因出在哪里,但是我们可以通过 Mono 自带的工具来跟踪内存分配。这里用到的工具是 Profiler,这个工具只支持 Mono 2.9+,如果你使用的发行版过于稳定,可能需要安装测试版本的 Mono 或手工编译安装 Mono 才可以使用这个工具。

Profiler 特别像在 Visual Studio 中的「性能分析」工具。它们的原理也大同小异。它们都是通过运行时采样和日志,来跟踪程序运行的状态。

具体来说,Profiler 会在托管程序运行时记录这些内容:

  • 方法进入和退出
  • 对象分配
  • 垃圾回收
  • JIT 编译
  • 元数据加载
  • 锁上下文
  • 异常

另外,它还提供了堆快照的功能,可以在你的程序进行过一次垃圾回收之后,记录下目前托管堆的情况。

使用 Profile 日志

要使用 Profiler 进行分析,首先要以附加参数启动 Mono 代码:

mono --profile=log program.exe

在程序运行结束后,在当前目录下会生成一个 output.mlpd 文件。这个文件稍后可以被用来做性能分析。在程序运行结束之后,输入命令

mprof-report output.mlpd

会生成分析报告。

你也可以使用一些参数,常用参数有

mono --profile=log:report program.exe

这样可以不保留日志,而是直接产生分析报告,有时候这可能很有用,因为日志文件实在是太大了

mono --profile=log:noalloc program.exe

noalloc 会导致日志中不记录分配情况。

mono --profile=log:nocalls program.exe

nocalls 不记录调用方法。如果不使用 nocalls,要预留足够大的硬盘空间。在我的实验中,几分钟产生了 500MB 甚至 2GB 的数据(即使取消一些日志,文件仍然很大)。

mono --gc=sgen --profile=log:heapshot program.exe

这可以记录托管堆的快照。

mono --profile=log:sample program.exe

CPU 采样,和 .NET 自带的特别像,通过一般不超过 10% 的性能损耗,在运行时动态采样,以便获取关于 CPU 时间消耗在什么运算中的第一手资料。

分析日志

现在我们开始分析日志,输入命令

mprof-report output.mlpd

即可看到结果。

详细的 Mono Profiler 使用方法请参考它的官方文档

两处优化

使用 Stream 代替 byte[]

如果可能,在操作大量 byte[] 数据的地方,考虑使用 Stream 对象。比如,如果你使用 FileStream 操作大文件,你将不需要将文件性一次读入内存。进行此项优化成功节约了 20% 的内存。但是要注意:

  • 确保 Stream 的回收,尽可能使用 using 关键字来管理 Stream 的释放
  • 如果你使用的是 MemoryStream,其内部实现其实是一个动态变长的 byte[] 数组,当它增长到最大的时候,文件仍然是完全放在了内存之中。
  • 最重要的是,你对数据的处理必须是流式的,如果你只是把 FileStream 分块读入到一个 byte[]DataTableMemoryStream 等中并作为参数进行某种操作(比如压缩,如上述代码中的 Zip.Compress(byte[] 对象)),那么你仍然无法节约内存。你必须确保你对流的操作是分块流式进行的。

压缩是典型的非流式操作,因为压缩算法几乎都是基于上下文的,而不只是当前位置。

使用外部命令代替大内存操作

编写另外一个可执行文件,这个文件不能是 dll,so 等模块,保证其内存空间的独立,然后将耗费内存的操作放在这个程序中,将数据的位置作为参数。好处是这个程序处完数据会退出,所有资源得到了充分的释放。

为什么不使用 GC.Collect()

  • GC.Collect() 进行全代垃圾回收,暂停所有线程的运行,而且资源消耗大,对于对速度要求高的程序(比如服务器端程序)不太合适
  • GC 未必可以正确的回收所有垃圾(如本文代码在 Mono 环境下就无法正常回收)
  • 外置程序可以在多核计算机中和原服务器中并行执行,因为数据处理很多时候是高 IO 操作,不会卡住服务器程序,逻辑简单性能又高

通过弱引用(WeekReference)管理缓存

这并不是标准库推荐的做法,但是如果你想简单的防止程序缓存占用过多的资源,可以使用它。详细介绍可以移步 MSDN。

VirtualBox Code CO_E_SERVER_EXEC_FAILURE Server execution failed 解决办法

问题描述

如果你在使用 VirtualBox 的命令行 VBoxManage 创建虚拟磁盘指向物理磁盘,那么你可能会使用类似这样的语法

VBoxManage internalcommands createrawvmdk
-filename "C:\Users\my\usb30.vmdk" -rawdisk "\\.\PhysicalDrive1"

其中,filename 代表的是虚拟磁盘名称,rawdisk 代表的是对应的物理磁盘,PhysicalDrive1 代表的是第二块物理磁盘(通常是移动硬盘),请不要搞错了,直接复制来用。

此时(在 Windows 平台下),你可能会得到这样的错误输出。

VBoxManage.exe: error: Code CO_E_SERVER_EXEC_FAILURE (0x80080005) - Server execution failed (extended info not available)

解决方法

解决方法只是把每个参数加上引号,很无语,因为其实并没有空格等字符,但仍然需要加上引号,而且错误信号驴头不对马嘴。

例如:

VBoxManage internalcommands createrawvmdk
-filename "C:\Users\my\usb30.vmdk" -rawdisk "\.\PhysicalDrive1"

输出:

RAW host disk access VMDK file C:\Users\my\usb30.vmdk created successfully.

另,如果你执行 VBoxManage 遇到 Access Denied 等错误,请用管理员权限打开 cmd。

参考资料

在 Debian Wheely (6.0) 下安装 testing mono 软件包

在 /etc/apt/sources.list.d/下建立一个 testing.list 文件,内容为

deb http://ftp.cn.debian.org/debian testing main

然后执行

apt-get update

apt-get -t testing install 包名

即可。

1 2 3 4 5 64