2

リークをかなり追跡しましたが、自分で理解 (修正) できないようです。最初にANTSメモリプロファイラーを使用して、コードが実際にメモリをスタックしていることを確認しました。最初は 25 MB を使用していましたが、1 時間ほどで 100 MB を超えました。私がこれをコーディングしている私の友人は、実際にこの欠陥のあるプログラムを使用していて、18 GB の RAM をすべて使い果たしてしまい、メモリ不足の例外が発生しました。

リーク部分はプログラムにとって重要ではありませんが、 RefreshSessions() メソッドがなければほとんど役に立ちません。

私は、Code Project からプロジェクトVista Core Audio API Master Volume Controlを拡張してきました。

漏れそうな部分です。未使用でテストしましたが、漏れません。

更新しました:

public void RefreshSessions()
    {
        Marshal.ThrowExceptionForHR(_AudioSessionManager.GetSessionEnumerator(out _SessionEnum));
        _Sessions.Refresh(_SessionEnum);
    }

(ここからクラスコードを削除しました)

私はあまりコーディングしていないので、何か見落としているかもしれませんが、さらに詳細が必要な場合は、実際にソースをダウンロードするか、私の最善の能力に答えることができます.

(ここで不要なコードを削除)

リークは、次の単純なコンソール アプリでテストされました。

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            MMDeviceEnumerator DevEnum = new MMDeviceEnumerator();
            MMDevice device = DevEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);

            Console.ReadKey();
            int i = 0;
            while (i < 10000)
            {
                device.AudioSessionManager.RefreshSessions();
                i++;
            }
            Console.ReadKey();
        }
   }
}

更新 2

私はそれを修正したと思います。もう少し長いテストを実行する必要がありますが、少なくともメモリ使用量は安定しているようです。このアイデアは、c++ でリークの修正を見つけたダイヤラーから生まれました。

public void RefreshSessions()
        {
            _Sessions.Release(); //added this
            IAudioSessionEnumerator _SessionEnum;
            Marshal.ThrowExceptionForHR(_AudioSessionManager.GetSessionEnumerator(out _SessionEnum));
            _Sessions.Refresh(_SessionEnum);
        }

これは の部分ですSessionCollection:

public void Release()
        {
            Marshal.ReleaseComObject(_AudioSessionEnumerator);
        }

これは、提案されたコードダイヤラーとは正確には異なりますが(とにかく使用することになりました)、それでも. 彼が言ったように、これはこれを達成するための最良の方法ではないかもしれませんが、私のアプリに悪影響を及ぼさないと思われるので、私はそれを採用します.

4

3 に答える 3

2

別の編集

public void RefreshSessions()
{
    if (_SessionEnum != null)
    {
        Marshal.ReleaseComObject(_SessionEnum);
    }
    Marshal.ThrowExceptionForHR(_AudioSessionManager.GetSessionEnumerator(out _SessionEnum));
}

上記のコードは、SessionEnum を明示的に解放し、C# のリークも修正しました。ただし、これはおそらくより良い方法で処理する必要があります。

編集:

次の C++ プログラムは、ループ テスト プログラムで実行したものと同等です。for ループの最後のRelease呼び出しにより、リークが修正されます。今日は行く必要があります。少し遊んで、自分で修正してみてください。または、他の誰かが、Release上記の C# プログラムのある時点で CLR ガベージ コレクターが を自動的に呼び出さない理由を見つけて説明できるかもしれません。

#include <stdio.h>
#include <tchar.h>
#include <audiopolicy.h>
#include <mmdeviceapi.h>

#define SAFE_RELEASE(p) { if ( (p) ) { (p)->Release(); (p) = 0; } }
#define CHECK_HR(hr) if (FAILED(hr)) { goto done; }

const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
const IID IID_IAudioSessionManager2 = __uuidof(IAudioSessionManager2);

int _tmain(int argc, _TCHAR* argv[])
{
    HRESULT hr;

    CoInitialize(0);

    IMMDeviceEnumerator *deviceEnum = 0;
    CHECK_HR(hr = CoCreateInstance(
        CLSID_MMDeviceEnumerator, NULL,
        CLSCTX_ALL, IID_IMMDeviceEnumerator,
        (void**)&deviceEnum));;

    IMMDevice *endpoint = 0;
    CHECK_HR(deviceEnum->GetDefaultAudioEndpoint(eRender, eMultimedia, &endpoint));

    getchar();

    // lazy initialization as found in MMDevice.AudioSessionManager..get
    IAudioSessionManager2 *m = 0;
    CHECK_HR(endpoint->Activate(IID_IAudioSessionManager2, CLSCTX_ALL, 0, (void **)&m));

    for (int i = 0; i < 100000; i++)
    {
        IAudioSessionEnumerator *sessEnum = 0;
        m->GetSessionEnumerator(&sessEnum);
        sessEnum->Release(); // leak
    }

    getchar();

    printf("success");
    return 0;

done:
    printf("failure");
    return 1;
}

私の推測:

_AudioSessionManager.GetSessionEnumerator(out _SessionEnum)列挙子を生成します。コンストラクターを呼び出すとSessionCollection(_SessionEnum)、その後_SessionEnum列挙されます。各列挙ステップでは、実際のアンマネージドオブジェクトが取得されます。

値型の場合、実際にはセッション コレクションにコピーされます (List(IEnumerable e)コンストラクターが各要素をコピーすることに注意してください) コピーはガベージ コレクションされますが、元のオブジェクトはアンマネージ コードから割り当てられ、リークが発生します。この場合は、コレクション コンストラクターを呼び出した直後に、unmanage メモリ解放関数を使用してメモリを解放する必要があります。

参照型の場合、メモリ内の実際のオブジェクトはアンマネージ コード内から割り当てられたため、ガベージ コレクションされないため、解放されません。この場合、アンマネージ ライブラリ関数が不要になったオブジェクトのメモリを解放する必要があります。

于 2013-03-28T21:35:08.753 に答える
1

アンマネージ コードがある場合、_Sessions メモリはいつ解放されますか? プライベート フィールドを再割り当てするだけでは、メモリが解放されることはありません。

例を次に示します: http://social.msdn.microsoft.com/forums/en-US/clr/thread/b2162d42-0d7a-4513-b02c-afd6cdd854bd

メモリを解放するには、dll のメソッドを使用する必要があります (C++ では delete[])。

于 2013-03-28T21:30:26.580 に答える