许多人在Mono + Linux中询问为什么在打开SQLite数据库时有时会发生“无法打开数据库文件”。
几天后,我们发现一个隐藏的问题让我疯了。考虑以下代码(请不要评论风格,因为这不是重点!)
System.Data.SQLite.SQLiteConnectionStringBuilder sqcsb;
sqcsb = new System.Data.SQLite.SQLiteConnectionStringBuilder();
sqcsb.DataSource = "file.db";
sqcsb.SyncMode = System.Data.SQLite.SynchronizationModes.Full;
sqcsb.PageSize = 4096;
System.Data.SQLite.SQLiteConnection conn;
System.Data.SQLite.SQLiteCommand cmd;
System.Data.SQLite.SQLiteParameter param;
string sql = "update Smth set tt = @TT";
int i = 0;
while (true)
{
GC.Collect();
System.Diagnostics.Process proc = System.Diagnostics.Process.GetCurrentProcess();
Console.WriteLine("Memory:{0:0,0},Private: {1:0,0},Virtual: {2:0,0} Working: {3:0,0}, Paged: {4:0,0}",
GC.GetTotalMemory(true),
proc.PrivateMemorySize64,
proc.VirtualMemorySize64,
proc.WorkingSet64,
proc.PagedMemorySize64);
Console.WriteLine( "Testing DB..." + i++);
conn = new System.Data.SQLite.SQLiteConnection(sqcsb.ConnectionString);
conn.Open();
cmd = new System.Data.SQLite.SQLiteCommand(conn);
cmd.CommandText = sql;
param = new System.Data.SQLite.SQLiteParameter("@TT", DbType.String);
param.Value = "0";
cmd.Parameters.Add(param);
conn.Close();
conn.Dispose();
cmd.Dispose();
}
那么上面的代码有什么问题?
答案是我们在cmd.Dispose()之前调用conn.Dispose(),它在WINDOWS上运行没有任何问题,而在Mono和Linux上,它在上述代码的大约1000个循环中失败,带有Sqlite异常无法打开数据库文件
在此处开始处理输出:
Memory:671,744,Private: 5,091,328,Virtual: 19,202,048 Working: 9,617,408, Paged: 00
Testing DB...0
Memory:770,048,Private: 5,763,072,Virtual: 23,617,536 Working: 11,341,824, Paged: 00
Testing DB...1
Memory:770,048,Private: 5,763,072,Virtual: 23,617,536 Working: 11,341,824, Paged: 00
Testing DB...2
这是最后一个WriteLine输出:
Memory:778,240,Private: 125,104,128,Virtual: 142,958,592 Working: 130,654,208, Paged: 00
Testing DB...1019
Unhandled Exception: System.Data.SQLite.SQLiteException: unable to open database file
at System.Data.SQLite.SQLite3.Open (System.String strFilename, SQLiteConnectionFlags connectionFlags, SQLiteOpenFlagsEnum openFlags, Int32 maxPoolSize, Boolean usePool) [0x00000] in <filename unknown>:0
at System.Data.SQLite.SQLiteConnection.Open () [0x00000] in <filename unknown>:0
在崩溃的最后,你可以看到进程的PrivateMemory和VirtualMemory非常高。
它们在每次打开到db的连接时都会增加,这意味着它可能不会从内存中释放出来!
所以找到困难的方法..解决问题的方法是......改变这一点:
conn.Dispose();
cmd.Dispose();
为:
cmd.Dispose();
conn.Dispose();
我知道上面的代码不是本书的最佳实践,但仍然有很多用户不知道为什么他们应该按顺序处理所有内容,因为它会导致上述问题!
所以我认为发生的情况是,当处理与db的连接时,GC仍会在命令中检测到对连接的引用,这就是它推迟集合的原因,但是在处理命令之后它应该处理连接但它不会,至少我是这么看的:DI可能是错的。
所以请那些有GC和Mono经验的人为什么在MONO上这是一个问题而在Windows上我们使用dispose并不会增加任何问题。
谢谢你,最诚挚的问候!