QAudioDeviceInfo在 Windows上似乎存在内存泄漏(Qt 5.15.18)
Body
我在Windows上使用QAudioDeviceInfo::availableDevices()(Qt版本5.15.18)时,遇到了持续的内存泄漏问题——尚未在其他平台上测试。
Background
需要实现一个实时检测,用以判断某个音频设备是否出现在Windows上。为此,我会定期使用QAudioDeviceInfo::availableDevices() 来列出音频输入设备。然而,我发现进程的内存使用量会无限增长,表明出现了内存泄漏。
最小可复现示例:
#include <QApplication>
#include <QAudioDeviceInfo>
#include <QDebug>
#include <QPushButton>
#include <QTimer>
#include <QVBoxLayout>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget *w = new QWidget();
w->resize(400, 300);
w->show();
QTimer *timer = new QTimer(w);
QObject::connect(timer, &QTimer::timeout, w, [w]() {
qDebug() << "Timer timeout";
// Get list of available audio input devices
QList<QAudioDeviceInfo> devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
// Iterate through devices and print names
for (const QAudioDeviceInfo &device : devices)
{
qDebug() << "Device Name:" << device.deviceName();
}
});
// Trigger device check every 100ms
timer->start(100);
int ret = app.exec();
delete w;
return ret;
}
观察到的行为:
在Windows上运行这段代码时:
- 进程的内存使用量(在任务管理器/资源监视器中可见)会随时间持续增加
- 尽管
QList<QAudioDeviceInfo>设备是一个栈分配的变量(应该在lambda回调退出时被销毁),但内存并未释放
使用Visual Leak Detector (VLD) 进行验证
我已经通过Visual Leak Detector (VLD),这是一个在Windows/MSVC上广泛使用的内存泄漏检测工具,确认了内存泄漏。
- VLD在多次调用QAudioDeviceInfo::availableDevices() 时会报告内存泄漏
- 如果我移除对availableDevices() 的调用,则不会报告泄漏
- 这证实泄漏源自Qt的 QAudioDeviceInfo实现内部
我已检查的内容:
- 我期望每次定时器回调后,栈分配的设备列表及其QAudioDeviceInfo元素能够被正确释放
- 关于Qt 5.15.18的此内存泄漏,尚未发现相关的bug报告
- 该问题在多台Windows机器上可稳定重现
问题:
- 这是Qt 5.15.18的 QAudioDeviceInfo::availableDevices() 在Windows上的已知问题吗?
- 我是不是在代码中遗漏了什么,导致内存泄漏(例如错误的内存管理)?
- 有没有可行的变通方案,可以在不产生内存泄漏的前提下安全地获取音频设备信息?
VLD报告:
---------- Block 182 at 0x0000000039725760: 78 bytes ----------
Leak Hash: 0x68333E06, Count: 1, Total 78 bytes
Call Stack (TID 31932):
ntdll.dll!RtlAllocateHeap()
MMDevAPI.DLL!0x00007FF99F45ED72()
MMDevAPI.DLL!0x00007FF99F452FE2()
wdmaud.drv!0x00007FF9327F4E6C()
wdmaud.drv!0x00007FF9327F3298()
wdmaud.drv!0x00007FF9327F7610()
winmmbase.dll!0x00007FF952AE65DD()
winmmbase.dll!0x00007FF952AEFB7F()
qtaudio_windowsd.dll!0x00007FF8F5C840E8()
qtaudio_windowsd.dll!0x00007FF8F5C83E68()
qtaudio_windowsd.dll!0x00007FF8F5C83BDD()
D:\B\Q\qtmultimedia\src\multimedia\audio\qaudiodevicefactory.cpp (204): Qt5Multimediad.dll!QAudioDeviceFactory::audioDeviceInfo() + 0x27 bytes
D:\B\Q\qtmultimedia\src\multimedia\audio\qaudiodeviceinfo.cpp (68): Qt5Multimediad.dll!QAudioDeviceInfoPrivate::QAudioDeviceInfoPrivate() + 0x23 bytes
D:\B\Q\qtmultimedia\src\multimedia\audio\qaudiodeviceinfo.cpp (443): Qt5Multimediad.dll!QAudioDeviceInfo::QAudioDeviceInfo() + 0x3A bytes
D:\B\Q\qtmultimedia\src\multimedia\audio\qaudiodevicefactory.cpp (135): Qt5Multimediad.dll!QAudioDeviceFactory::availableDevices() + 0x22 bytes
D:\B\Q\qtmultimedia\src\multimedia\audio\qaudiodeviceinfo.cpp (435): Qt5Multimediad.dll!QAudioDeviceInfo::availableDevices() + 0xE bytes
D:\Home\Desktop\mm\src\main.cpp (23): app.exe!<lambda_972f05a238b8c048bbd3e638a6c9cd17>::operator()() + 0xD bytes
D:\Qt\Qt5.15.18-msvc2022_64\include\QtCore\qobjectdefs_impl.h (146): app.exe!QtPrivate::FunctorCall<QtPrivate::IndexesList<>,QtPrivate::List<>,void,<lambda_972f05a238b8c048bbd3e638a6c9cd17> >::call() + 0xA bytes
D:\Qt\Qt5.15.18-msvc2022_64\include\QtCore\qobjectdefs_impl.h (256): app.exe!QtPrivate::Functor<<lambda_972f05a238b8c048bbd3e638a6c9cd17>,0>::call<QtPrivate::List<>,void>() + 0xF bytes
D:\Qt\Qt5.15.18-msvc2022_64\include\QtCore\qobjectdefs_impl.h (443): app.exe!QtPrivate::QFunctorSlotObject<<lambda_972f05a238b8c048bbd3e638a6c9cd17>,0,QtPrivate::List<>,void>::impl() + 0x1B bytes
D:\B\Q\qtbase\src\corelib\kernel\qobjectdefs_impl.h (398): Qt5Cored.dll!QtPrivate::QSlotObjectBase::call() + 0x42 bytes
D:\B\Q\qtbase\src\corelib\kernel\qobject.cpp (3928): Qt5Cored.dll!doActivate<0>() + 0x2F bytes
D:\B\Q\qtbase\src\corelib\kernel\qobject.cpp (3988): Qt5Cored.dll!QMetaObject::activate() + 0x13 bytes
D:\B\Q\build-msvc\qtbase\src\corelib\.moc\debug\moc_qtimer.cpp (205): Qt5Cored.dll!QTimer::timeout() + 0x19 bytes
D:\B\Q\qtbase\src\corelib\kernel\qtimer.cpp (259): Qt5Cored.dll!QTimer::timerEvent() + 0xF bytes
D:\B\Q\qtbase\src\corelib\kernel\qobject.cpp (1324): Qt5Cored.dll!QObject::event() + 0x2E bytes
D:\B\Q\qtbase\src\widgets\kernel\qapplication.cpp (3640): Qt5Widgetsd.dll!QApplicationPrivate::notify_helper() + 0x1F bytes
D:\B\Q\qtbase\src\widgets\kernel\qapplication.cpp (2980): Qt5Widgetsd.dll!QApplication::notify() + 0x1D bytes
D:\B\Q\qtbase\src\corelib\kernel\qcoreapplication.cpp (1064): Qt5Cored.dll!QCoreApplication::notifyInternal2() + 0x2E bytes
D:\B\Q\qtbase\src\corelib\kernel\qcoreapplication.cpp (1463): Qt5Cored.dll!QCoreApplication::sendEvent()
D:\B\Q\qtbase\src\corelib\kernel\qeventdispatcher_win.cpp (432): Qt5Cored.dll!QEventDispatcherWin32Private::sendTimerEvent() + 0x13 bytes
D:\B\Q\qtbase\src\corelib\kernel\qeventdispatcher_win.cpp (256): Qt5Cored.dll!qt_internal_proc() + 0x11 bytes
USER32.dll!CallWindowProcW() + 0x6A6 bytes
USER32.dll!IsWindowUnicode() + 0x3CD bytes
D:\B\Q\qtbase\src\corelib\kernel\qeventdispatcher_win.cpp (597): Qt5Cored.dll!QEventDispatcherWin32::processEvents() + 0xB bytes
qwindowsd.dll!0x00007FF89FB00024()
D:\B\Q\qtbase\src\corelib\kernel\qeventloop.cpp (143): Qt5Cored.dll!QEventLoop::processEvents()
D:\B\Q\qtbase\src\corelib\kernel\qeventloop.cpp (235): Qt5Cored.dll!QEventLoop::exec() + 0x41 bytes
D:\B\Q\qtbase\src\corelib\kernel\qcoreapplication.cpp (1375): Qt5Cored.dll!QCoreApplication::exec() + 0x22 bytes
D:\B\Q\qtbase\src\gui\kernel\qguiapplication.cpp (1871): Qt5Guid.dll!QGuiApplication::exec()
D:\B\Q\qtbase\src\widgets\kernel\qapplication.cpp (2833): Qt5Widgetsd.dll!QApplication::exec()
D:\Home\Desktop\mm\src\main.cpp (31): app.exe!main() + 0x6 bytes
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl (79): app.exe!invoke_main()
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl (288): app.exe!__scrt_common_main_seh() + 0x5 bytes
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl (331): app.exe!__scrt_common_main()
D:\a\_work\1\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp (17): app.exe!mainCRTStartup()
KERNEL32.DLL!BaseThreadInitThunk() + 0x17 bytes
ntdll.dll!RtlUserThreadStart() + 0x2C bytes
Data:
7B 00 32 00 38 00 66 00 62 00 62 00 35 00 34 00 {.2.8.f. b.b.5.4.
31 00 2D 00 66 00 63 00 65 00 65 00 2D 00 34 00 1.-.f.c. e.e.-.4.
30 00 62 00 36 00 2D 00 62 00 34 00 33 00 34 00 0.b.6.-. b.4.3.4.
2D 00 32 00 64 00 31 00 66 00 38 00 30 00 38 00 -.2.d.1. f.8.0.8.
36 00 62 00 64 00 65 00 34 00 7D 00 00 00 6.b.d.e. 4.}.....
Visual Leak Detector detected 37 memory leaks (2232 bytes).
Largest number used: 11387 bytes.
Total allocations: 50233 bytes.
Visual Leak Detector is now exiting.
解决方案
@Friedrich感谢你的提示!我已查看Qt的缺陷跟踪系统,找到了相关的问题QTBUG-62663。 https://qt-project.atlassian.net/browse/QTBUG-62663
看起来这个内存泄漏与非ASCII设备名称的字符串编码转换有关,例如中文、日文、俄文等。
在英文Windows系统上可能不会出现此问题,因为设备名称仅使用ASCII字符。很可能Qt开发人员只在自己的英文系统上进行了测试,测试时也没有覆盖非英文的操作系统语言。
此外,这个问题在多年前就有报道,但在最新的Qt 5.15.x版本中仍未修复,这也是我在Qt 5.15.18中遇到它的原因。
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。