1

发现和有趣的问题:D

许多人询问为什么在 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 方式,内存都不会增加,也不会导致任何问题。

感谢你并致以真诚的问候!

4

0 回答 0