質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

SQLite

SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

Q&A

解決済

3回答

3135閲覧

C#でSQLiteを使用するとメモリリークする

hoge_beginner

総合スコア16

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

SQLite

SQLiteはリレーショナルデータベース管理システムの1つで、サーバーではなくライブラリとして使用されている。

0グッド

1クリップ

投稿2018/12/20 09:19

編集2018/12/21 05:28

発生している問題

C#でSQLiteデータベースを使用しています。
タイマーを使用して一定間隔毎にデータベースの読み取りを行おうとしているのですが、データベースへのアクセスをするたびにメモリ(プライベートワーキングセット)が増加しておりメモリリークしてしまいます。
下記のような簡単なサンプルを作ったところSQLiteConnectionStringBuilderがガベージコレクションに全く回収されないようです。
タイマーで一定周期でDBを読み取るような場合にSQLiteConnectionStringBuilderをガベージコレクションに回収させるようなことはできないのでしょうか?
できない場合、どのように実装するのがベストプラクティスなのか教えていただけないでしょうか?

該当のソースコード

C#

1static void Main(string[] args) 2{ 3 Timer timer = new Timer(10000); 4 timer.Elapsed += (sender, e) => 5 { 6 SQLiteConnectionStringBuilder dbConnectionString = new SQLiteConnectionStringBuilder 7 { 8 SyncMode = SynchronizationModes.Off, 9 JournalMode = SQLiteJournalModeEnum.Memory, 10 DefaultTimeout = 60, 11 BusyTimeout = 10, 12 ReadOnly = true 13 }; 14 15 string test = null; 16 using (SQLiteConnection dbConnection = new SQLiteConnection(dbConnectionString.ToString())) 17 { 18 dbConnection.Open(); 19 using (DataContext dbContext = new DataContext(dbConnection)) 20 { 21 dbContext.ObjectTrackingEnabled = false; 22 /* ここでLINQでSQLを構築しデータアクセス */ 23 var items = from test in dbContext.GetTable<TestTable>() 24 where test.Id == 1 25 select test; 26 foreach (var item in items) 27 { 28 test = String.Copy(item.TestName); 29 break; 30 } 31 } 32 } 33 dbConnectionString = null; 34 }; 35 timer.Start(); 36 Console.ReadKey(); 37} 38 39[Table(Name = "T_TEST")] 40public class TestTable 41{ 42 [Column(Name = "ID", DbType = "BIGINT", CanBeNull = false, IsPrimaryKey = true)] 43 public Int64 Id { get; set; } 44 45 [Column(Name = "TestName", DbType = "NVARCHAR", CanBeNull = false)] 46 public string TestName { get; set; } 47 48 public TestTable() 49 { 50 this.Id = -1; 51 this.TestName = ""; 52 } 53} 54 55

環境:VisualStudio 2013 .Net Framework 4.5

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ozwk

2018/12/21 08:13

1時間あたり何MB増えて、計測は何時間しましたか?
guest

回答3

0

提示されたソースを実行してみましたが、実行当初は使用メモリは増加しましたが、ある程度の回数実行されると増加は見られません。
メモリリークと言えるような動作の確認が出来ませんでした。
(タイマー間隔を10msにして回しました)

他の部分でリークしているとは考えられないのでしょうか?

投稿2018/12/21 02:59

YAmaGNZ

総合スコア10242

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoge_beginner

2018/12/21 05:30

回答ありがとうございます。 DBへのSELECTの発行がなくてもメモリが増加していくのですが、SELECTで取得した文字列を別の変数に代入するとさらにメモリが増加していきます。 そもそもLINQの使い方が誤っているのでしょうか?
ozwk

2018/12/21 05:52 編集

こちらも試しましたが 10msごと実行で15秒経過ぐらいで16MB->22MBになり、そこでGCが入りメモリ増加が止まりました。 10秒ごとなら単純計算で4時間以上かかります。
YAmaGNZ

2018/12/21 06:10 編集

編集されたLINQを使用するソースでも確認してみましたが、リークの確認はできませんでした。 動作させていて10MB弱の増加は確認できましたが、それ以上の増加はありませんでした。 (ログを表示しているので、処理だけを行った場合よりメモリの上昇は多いと思います)
hoge_beginner

2018/12/25 00:36

SQLiteConnectionStringBuilderをシングルトンパターンを適用してタイマーのハンドラでGC.Collect()を呼び出してもメモリ使用量が増加し続けていましたが、別のアプリで大量のメモリを使用するようにしたら(今回は仮想環境を起動)メモリが解放されました。 皆さんの環境と私の環境でガベージコレクタの動きが違い不可解ではありますが、複数のPCで動かしてしばらく様子見したいと思います。 色々とアドバイスありがとうございました。
guest

0

ベストアンサー

ガベージコレクションはメモリが足りなくなった時などに起こります。
気にしすぎでしょう。
どうしても回収させたいなら GC.Collect() でできますが、パフォーマンスは別に良くならないと思います。

投稿2018/12/21 02:12

Zuishin

総合スコア28660

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoge_beginner

2018/12/21 03:03

回答ありがとうございます。 このコードを1時間動かしたところメモリが増加し続けてまったくメモリが解放され無い状況ですが、正常な動作なのでしょうか? タイマーハンドラの最後で明示的にGC.Collect()を呼び出してもメモリが増加し続けており誰かが参照し続けてガベージコレクタが回収できないような状態になっているように見えます。
Zuishin

2018/12/21 03:54

この部分に問題はないように思います。メモリの増加をどうやって確認されましたか?
hoge_beginner

2018/12/21 05:20

メモリ増加の確認はタスクマネージャのプライベートワーキングセットの変化を確認しています。 LINQでのアクセス部分をコメントアウトしていても微小な増加をしている状況です。
Zuishin

2018/12/21 05:50 編集

よく見たらこれ LINQ To SQL ではありませんか? コメントでも書かれていましたが、見落としていました。 これは使ってはいけません。
Zuishin

2018/12/21 05:53

Qiita の記事はコメントまで読んでから参考にしてください。あそこの「いいね」は信用できません。
hoge_beginner

2018/12/21 06:11

LINQ To SQLの使用方法が誤っているということでしょうか? それとも使用してはいけないということでしょうか? ObjectTrackingEnabledがtrueだとメモリリークするという記述は見たのですが、LINQを使ってはいけないという記述は見たことがないです。 使用してはいけない場合、その理由を教えていただけないでしょうか?
Zuishin

2018/12/21 06:32

SQLite に対応していません。
hoge_beginner

2018/12/21 07:04

nugetで公開されているSystem.Data.SQLite.Linqパッケージの追加でSQL文の使用とDBへのアクセスはできるのですが、それは正式には対応していないということなのでしょうか? パッケージの説明にも特に非対応という記述は見当たりません。
Zuishin

2018/12/21 07:10

Entity Framework に変更して同じ問題が起こるかどうか確かめてみてください。
hoge_beginner

2018/12/25 00:33

SQLiteConnectionStringBuilderをシングルトンパターンを適用してタイマーのハンドラでGC.Collect()を呼び出してもメモリ使用量が増加し続けていましたが、別のアプリで大量のメモリを使用するようにしたら(今回は仮想環境を起動)メモリが解放されました。 GC.Collect()で即座に回収されなかった理由が分かりませんが、しばらくこのままで運用したいと思います。 いろいろとアドバイスありがとうございました。
guest

0

SQLiteConnectionStringBuilderは接続文字列を生成するだけのクラスなので、
もしリークしたところで大したメモリ消費量ではないのではないでしょうか?

それよりも、質問欄のコードには載っていませんが、
SQLiteConnectionのインスタンスや、BeginTransaction()の戻り値などがDisposeし忘れていないか、(using構文なりを使っているか)確認してみてください。


追記:
SQLiteConnectionStringBuilderのリークが気になるのでしたら、毎回生成しないようにしてみては?

csharp

1 static void Main(string[] args) 2 { 3 4 var dbConnectionString = new SQLiteConnectionStringBuilder 5 { 6 SyncMode = SynchronizationModes.Off, 7 JournalMode = SQLiteJournalModeEnum.Memory, 8 DefaultTimeout = 60, 9 BusyTimeout = 10, 10 ReadOnly = true 11 }; 12 var connectionString = dbConnectionString.ToString(); // ←タイマーの外で文字列化 13 14 var timer = new Timer(10000); 15 timer.Elapsed += (sender, e) => 16 { 17 using (SQLiteConnection dbConnection = new SQLiteConnection(connectionString)) // ←タイマーの中では事前に生成した文字列を再利用 18 {

投稿2018/12/21 00:46

編集2018/12/21 05:10
takabosoft

総合スコア8356

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoge_beginner

2018/12/21 02:08

回答ありがとうございます。 1回のリーク量は微小でも長時間の運用でMB単位のリークになっているので無視できない状況です。 DBのアクセスにはLinq to SQLを使用していてusingで括っています。
Zuishin

2018/12/21 02:14

SQLiteConnection はちゃんと破棄されています。 BeginTransaction は使われていません。
takabosoft

2018/12/21 05:12

そこまで気にするのであれば別案を用意しました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問