我试图在我的程序中使用线程播放声音效果,我搜索了网络,据我所知,当一个线程到达运行功能的末尾时,GC将自由收集。
然而,当我一个接一个地多次调用该函数时,任务管理器显示内存使用量大幅增加而且它从未退回,我等了2分钟就没有效果。
以下是我用来播放音效的代码:
public static void playSfx(final String path) {
new Thread(new Runnable() {
public void run() {
try {
AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(path));
final int BUFFER_SIZE = 128000;
SourceDataLine sourceLine = null;
AudioFormat audioFormat = audioInputStream.getFormat();
DataLine.Info info = new DataLine.Info(SourceDataLine.class, audioFormat);
sourceLine = (SourceDataLine) AudioSystem.getLine(info);
sourceLine.open(audioFormat);
if (sourceLine == null)
return;
sourceLine.start();
int nBytesRead = 0;
byte[] abData = new byte[BUFFER_SIZE];
while (nBytesRead != -1) {
try {
nBytesRead = audioInputStream.read(abData, 0, abData.length);
} catch (IOException e) {
e.printStackTrace();
}
if (nBytesRead >= 0) {
sourceLine.write(abData, 0, nBytesRead);
}
}
sourceLine.drain();
sourceLine.close();
audioInputStream.close();
} catch (IOException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}).start();
}
我该怎样做才能收回丢失的记忆?
答案 0 :(得分:0)
我无法重现这一点。对我而言,这将达到大约60k内存使用率的峰值 考虑到有问题的语言是完全可以的imo。你有什么小事 可靠地再现问题的代码?
public static void main(String[] args) {
for(int i = 0; i < 1000; i++) {
playSfx("somesortsound.wav");
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
}
此外,您对内存使用的期望是什么?预测GC 行为并不容易,而且你不能保证看到内存被释放只是因为 你等一会儿。特别是如果你没有分配很多。
我也会在你的阅读循环中摆脱try catch
。你不处理任何事情
无论如何都有例外,你也可以在循环外捕获它。如果
stream导致IO异常你想要中止循环,对吧?
答案 1 :(得分:0)
使用兼容文件从以下类运行该方法,没有任何异常;我没有看到这种行为。
public class PlaySound {
public static void main(String[] args) throws Exception {
String filePath = "C:"+File.separator+"bach.wav";
playSfx(filePath);
playSfx(filePath);
playSfx(filePath);
while(true){
Thread.sleep(1000);
}
}
}
可能是内存泄漏。您没有使用finally块(或“try-with-resources”)管理资源,将此与return语句耦合可能会导致AudioInputStream驱动程序的内部维护对文件的引用。
我会使用try-with-resources块。
try(AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(path));){
//your code
}
另外,我不会使用任务管理器来可靠地确定垃圾收集器的行为,有许多组件可以确定内存中Java进程的大小。
了解正在发生的事情;使用VisualVM。 (它位于jdk的bin目录中)。您可以看到垃圾收集器处于活动状态的时间,以及堆的大小与其当前最大大小的比较。您还可以“请求”(不保证)垃圾收集,这将指示“幸存者”所需的内存量以及可以释放的数量。
不可否认,2分钟是很长一段时间,但值得一提的是,垃圾收集通常是因为未能将内存分配给特定代,但这取决于所使用的特定GC算法。换句话说,如果它没有达到上限,则有可能它不会执行垃圾收集。
This article可能值得阅读/浏览;它详细介绍了各种不同的垃圾收集器以及它们如何运行。