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

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

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

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

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

2697閲覧

Felicaの連続読み込みでメモリリークの疑い?

yotuika

総合スコア0

C#

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

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2021/07/22 05:19

恐れ入ります。C#,C++の初心者です。

Felica(PASORI)でカードの読み込みを行いたいのですが、
基本的に24時間の連続稼働でカードをかざした時にカード情報を読み込む
カードがかざされていない場合は処理なし、ですがアプリ自体は稼働し続けると
いった機能を C# で実現しようとしています。(3秒に1回にアプリを稼働させています)
ただ、どうもメモリリーク?を起こしているかもしれません。
1週間ほど連続稼働させましたが1日に1.5メガほどメモリが増加し続けています。
(windowsのパフォーマンスモニタのPrivateByteに該当のアプリを監視して確認しました)

Felicaの読み込みにはBSDライセンスの「felicalib.dll」をそのまま使用してみました。
該当のdllはC++で作成されており.NET非対応アセンブリの為、
C#側のプロジェクトの参照に追加せず、
同一ディレクトリに格納し、C#側のラッパより直接アクセスしております。
(C#のラッパもdllと一緒に配布されておりほぼそのまま使用しております)

メモリリークの疑い?があったので、DISPOSEの一部を追加してみるなどしましたが、
完全な改善には至らず、そもそもリークを起こすような記述であるのか判断がつきませんでした。

そこで、、そもそもメモリリークを起こすような可能性のあるコードであるのかどうか
御教授頂ければ幸いです。
(私自身のコードのまずい、あるいは24時間連続稼働させるような使い方をするなら別の考慮が必要等なんでも結構です!)

環境は.NetFramework4.6で稼働させております。

■C#側(Main)一部抜粋

C#

1 int iRet = 0; 2 3 // 4 //Make FelicaObject 5 // 6 using(Felica f = new Felica()) 7 { 8 // 9 //Felica Pooling 10 // 11 iRet = f.Poolling(SYS_CD) //システムコード(既定値)指定 12 13 switch(iRet) 14 { 15 case 0: //カードありの為、カード読み込み 16 byte[] bFelicaBlk = new byte[16]; 17 bFelicaBlk = f.ReadWithoutEncryption(SERVICE_CD,BLK_ADDRESS); //サービスコード、ブロックアドレス(既定値) 18 break; 19 case 1: //カードなしの為、処理なし 20 break; 21 } 22 } 23

■C#側(ラッパ)

C#

1using System; 2using System.Collections.Generic; 3using System.Text; 4using System.Runtime.InteropServices; 5 6namespace FelicaLib 7{ 8 public class Felica : IDisposable 9 { 10 //⇒★★★オリジナルの下記DllImportのコードに(CallingConvention = CallingConvention.Cdecl))を追加しました!! 11 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 12 private extern static IntPtr pasori_open(String dummy); 13 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 14 private extern static void pasori_close(IntPtr p); 15 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 16 private extern static int pasori_init(IntPtr p); 17 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 18 private extern static IntPtr felica_polling(IntPtr p, ushort systemcode, byte rfu, byte time_slot); 19 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 20 private extern static void felica_free(IntPtr f); 21 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 22 private extern static void felica_getidm(IntPtr f, byte[] data); 23 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 24 private extern static void felica_getpmm(IntPtr f, byte[] data); 25 [DllImport("felicalib.dll"), CallingConvention = CallingConvention.Cdecl)] 26 private extern static int felica_read_without_encryption02(IntPtr f, int servicecode, int mode, byte addr, byte[] data); 27 28 private IntPtr pasorip = IntPtr.Zero; 29 private IntPtr felicap = IntPtr.Zero; 30 31 public Felica() 32 { 33 pasorip = pasori_open(null); 34 if (pasorip == IntPtr.Zero) 35 { 36 throw new Exception("felicalib.dll を開けません"); 37 } 38 if (pasori_init(pasorip) != 0) 39 { 40 throw new Exception("PaSoRi に接続できません"); 41 } 42 } 43 44 public void Dispose() 45 { 46 if (pasorip != IntPtr.Zero) 47 { 48 felica_free(felicap); //⇒★★★オリジナルのコードに追加しました! 49 felicap = IntPtr.Zero; //⇒★★★オリジナルのコードに追加しました! 50 pasori_close(pasorip); 51 pasorip = IntPtr.Zero; 52 GC.SuppressFinalize(this); //⇒★★★オリジナルのコードに追加しました! 53 } 54 } 55 56 ~Felica() 57 { 58 Dispose(); 59 } 60 61 public void Polling(int systemcode) 62 { 63 felica_free(felicap); 64 65 felicap = felica_polling(pasorip, (ushort)systemcode, 0, 0); 66 if (felicap == IntPtr.Zero) 67 { 68 return 1; //⇒★★★オリジナルのコードに追加しました!(カードがない場合はreturn 1) 69 } 70 return 0; //⇒★★★オリジナルのコードに追加しました!(カードがない場合はreturn 0) 71 } 72 73 public byte[] ReadWithoutEncryption(int servicecode, int addr) 74 { 75 if (felicap == IntPtr.Zero) 76 { 77 throw new Exception("no polling executed."); 78 } 79 80 byte[] data = new byte[16]; 81 int ret = felica_read_without_encryption02(felicap, servicecode, 0, (byte)addr, data); 82 if (ret != 0) 83 { 84 return null; 85 } 86 return data; 87 } 88 } 89} 90

C++側(dll)(内容はオリジナルより変更していません)※コードが長いので使用部分のみを転記

C++

1#include "felicalib.h" 2#include "felicaint.h" 3 4#include <tchar.h> 5#include <shlobj.h> 6#include <stdio.h> 7 8/** 9 @brief PaSoRi をオープンする 10 @param[in] dummy ダミー (libpasori との互換性のため) 11 @retval pasori ハンドル 12 13 felica.dll をロード、初期化する 14*/ 15pasori *pasori_open(char *dummy) 16{ 17 pasori *p; 18 TCHAR cp[_MAX_PATH], dllpath[_MAX_PATH]; 19 20 /* get felica.dll path */ 21 SHGetSpecialFolderPath(NULL, cp, CSIDL_PROGRAM_FILES_COMMON, FALSE); 22 _stprintf(dllpath, _T("%s\Sony Shared\FeliCaLibrary\felica.dll"), cp); 23 24 /* open felica.dll */ 25 p = (pasori *)malloc(sizeof(pasori)); 26 p->hInstDLL = LoadLibrary(dllpath); 27 if (p->hInstDLL == NULL) { 28 free(p); 29 return NULL; 30 } 31 32 /* resolve function pointers */ 33#define resolve_entry(f) p->f = (f ## _t)GetProcAddress(p->hInstDLL, #f) 34 resolve_entry(initialize_library); 35 resolve_entry(dispose_library); 36 resolve_entry(open_reader_writer_auto); 37 resolve_entry(close_reader_writer); 38 resolve_entry(polling_and_get_card_information); 39 resolve_entry(polling_and_request_system_code); 40 resolve_entry(polling_and_search_service_code); 41 resolve_entry(read_block_without_encryption); 42 resolve_entry(write_block_without_encryption); 43 44 if (!p->initialize_library()) { 45 free(p); 46 return NULL; 47 } 48 return p; 49} 50 51/** 52 @brief PaSoRi ハンドルをクローズする 53 @param[in] p pasoriハンドル 54 55 felica.dll を解放する 56*/ 57void pasori_close(pasori *p) 58{ 59 p->dispose_library(); 60 FreeLibrary(p->hInstDLL); 61} 62 63/** 64 @brief PaSoRi を初期化する 65 @param[in] p pasoriハンドル 66 @return エラーコード 67*/ 68int pasori_init(pasori *p) 69{ 70 if (!p->open_reader_writer_auto()) { 71 return -1; 72 } 73 return 0; 74} 75 76static felica *alloc_felica(pasori *p, uint16 systemcode) 77{ 78 felica *f = (felica *)malloc(sizeof(felica)); 79 memset(f, 0, sizeof(felica)); 80 f->p = p; 81 f->systemcode = H2NS(systemcode); 82 83 return f; 84} 85 86/** 87 @brief FeliCa をポーリングする 88 @param[in] p pasoriハンドル 89 @param[in] systemcode システムコード 90 @param[in] RFU RFU (使用しない) 91 @param[in] timeslot タイムスロット 92 @return felicaハンドル 93*/ 94felica *felica_polling(pasori *p, uint16 systemcode, uint8 RFU, uint8 timeslot) 95{ 96 felica *f; 97 POLLING polling; 98 uint16 sc; 99 uint8 number_of_cards = 0; 100 CARD_INFO card_info; 101 102 f = alloc_felica(p, systemcode); 103 104 sc = H2NS(systemcode); 105 polling.system_code = (uint8 *)&sc; 106 polling.time_slot = timeslot; 107 108 card_info.card_idm = f->IDm; 109 card_info.card_pmm = f->PMm; 110 111 if (!p->polling_and_get_card_information(&polling, &number_of_cards, &card_info) || 112 number_of_cards == 0) { 113 free(f); 114 return NULL; 115 } 116 117 return f; 118} 119 120/** 121 @brief 暗号化されていないブロックを読み込む 122 @param[in] f felicaハンドル 123 @param[in] servicecode サービスコード 124 @param[in] mode モード(使用しない) 125 @param[in] addr ブロック番号 126 @param[out] data データ(16バイト) 127 @return エラーコード 128 129 サービスコード、ブロック番号を指定してブロックを読み込む。 130 システムコードは felica_polling で指定したものが使用される。 131*/ 132int felica_read_without_encryption02(felica *f, int servicecode, int mode, uint8 addr, uint8 *data) 133{ 134 INSTR_READ_BLOCK irb; 135 OUTSTR_READ_BLOCK orb; 136 137 uint8 service_code_list[2]; 138 uint8 block_list[2]; 139 uint8 status_flag1, status_flag2; 140 uint8 result_number_of_blocks = 0; 141 142 service_code_list[0] = servicecode & 0xff; 143 service_code_list[1] = servicecode >> 8; 144 block_list[0] = 0x80; 145 block_list[1] = addr; 146 147 irb.card_idm = f->IDm; 148 irb.number_of_services = 1; 149 irb.service_code_list = service_code_list; 150 irb.number_of_blocks = 1; 151 irb.block_list = block_list; 152 153 orb.status_flag_1 = &status_flag1; 154 orb.status_flag_2 = &status_flag2; 155 orb.result_number_of_blocks = &result_number_of_blocks; 156 orb.block_data = data; 157 158 if (!f->p->read_block_without_encryption(&irb, &orb)) { 159 return -1; 160 } 161 if (status_flag1 != 0) { 162 return -1; 163 } 164 return 0; 165} 166 167/** 168 @brief felica ハンドル解放 169 @param[in] f felica ハンドル 170*/ 171void felica_free(felica *f) 172{ 173 free(f); 174} 175

メモリリークの疑いがあったため、C#側(ラッパ)のDISPOSEに一部処理を追加しました。
今回の現象がコード上メモリリークの疑いがない場合は別の要因があるとの想定で
調査しようと思います。

初心者の質問内容で大変恐縮ですが、宜しくお願い致します。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2021/07/22 09:42 編集

一日といっても、実際どの程度コールされてるか分からないので、テストプログラム作って負荷テストやってみればいいんじゃないですか?特定の処理繰り返し呼んでリニアにメモリが増え続けるならリークしてるって事になるでしょう。何をしたらリークするか特定して、対処を考えてください。
dodox86

2021/07/22 09:50

見た感じですが、怪しいところをひとつ。pasori_open()内でmalloc()で確保したpasori*型のメモリブロックですが、free()されている箇所が見当たらないようです。
yotuika

2021/07/23 08:40

radianさん ご丁寧に有難うございます! コール数をカウントしたところ1日に3万回弱、1週間で20万回ほど稼働させてみた結果です。 ライブラリに特にこだわりはありませんので、もう少し調べて改善しないようであれば、 試してみます!有難うございました
yotuika

2021/07/23 08:44 編集

dodox86さん 確かにほんとですね! dll内部の処理であるので、dllを再作成してみようと思います!! なんかこれが原因のような気がしてきました。 有難うございます!!
guest

回答1

0

自己解決

dll内部処理であるpasori_open()にて確保されたメモリブロックに対しfreeを追加し
dll自体を新しく作り直したところ、当該事象であったメモリリークが解消致しました。

アドバイスを頂いたお二方にはとても感謝しております。
配布用のライブラリだからと過信せず、自分の使用用途と照らし合わせて
内容を精査する必要があることを学びました。

radianさんの言うように一つの方法に固執せずいろんな方法を試してみてその中でベストなものを選択する進め方も非常に勉強になりましたし、
dodox86さんのようにざっと見た感じで不具合の原因を特定できてしまうような
お二人のような技術者を目指して今後も頑張ります。

有難うございました。

投稿2021/07/29 15:20

編集2021/07/29 15:24
yotuika

総合スコア0

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問