wch*_*gin 7 java garbage-collection weak-references out-of-memory javasound
我有一个SoundManager简单的声音管理课程.实质上:
public class SoundManager {
public static class Sound {
private Clip clip; // for internal use
public void stop() {...}
public void start() {...}
public void volume(float) {...}
// etc.
}
public Sound get(String filename) {
// Gets a Sound for the given clip
}
// moar stuff
}
Run Code Online (Sandbox Code Playgroud)
其中大部分用途如下:
sounds.get("zap.wav").start();
Run Code Online (Sandbox Code Playgroud)
据我所知,这应该不是保持在内存中新创建的声音的引用,它应该被垃圾收集相当迅速.但是,使用一个简短的声音文件(108 KB,高达00:00:00秒,实际上大约0.8秒),我得到一个之前我只能进行大约2100次调用OutOfMemoryError:
#Java Runtime Environment没有足够的内存来继续.
#本机内存分配(malloc)无法在C:\ BUILD_AREA\jdk6_34\hotspot\src\share\_vm\prims\_jni.cpp中为jbyte分配3874172个字节
#包含更多信息的错误报告文件保存为:
#[path ]
我尝试private static final Vector<WeakReference<Sound>>在SoundManager.Sound类中实现a ,将以下内容添加到构造函数中:
// Add to the sound list.
allSounds.add(new WeakReference<SoundManager.Sound>(this));
System.out.println(allSounds.size());
Run Code Online (Sandbox Code Playgroud)
这也允许我在程序结束时迭代并停止所有声音(在applet中,这并不总是自动完成).
但是,在相同的OutOfMemoryError情况发生之前,我仍然只能再调用10次以上.
如果重要的是,对于每个文件名,我将文件内容缓存为a byte[],但每个文件只执行一次,因此不应累积.
那么为什么要保留这些引用,如何在不增加堆大小的情况下停止它?
编辑:第32行包含"包含更多信息的错误报告":
Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)
J com.sun.media.sound.DirectAudioDevice.nWrite(J[BIIIFF)I
J com.sun.media.sound.DirectAudioDevice$DirectDL.write([BII)I
j com.sun.media.sound.DirectAudioDevice$DirectClip.run()V+163
j java.lang.Thread.run()V+11
v ~StubRoutines::call_stub
Run Code Online (Sandbox Code Playgroud)
这是否意味着这个问题完全不受我的控制?javasound需要时间"冷静下来"吗?出于调试目的,我以300 /秒的速度喷出这些声音.
编辑有关我使用JavaSound的更多信息.
我第一次打电话sounds.get("zap.wav"),它看到之前没有加载"zap.wav".它将文件写入a byte[]并存储它.然后它就像之前被缓存一样进行.
第一次和所有后续时间(在缓存之后),该方法将byte[]存储在内存中,创建一个新的ByteArrayInputStream,并AudioSystem.getAudioInputStream(bais)在所述流上使用.难道这些流可以记忆吗?我认为当收集Sound(以及因此Clip)时,流也将被关闭.
使用get每个请求的方法编辑.这是public Sound get(String name).
byteCache 是一个 HashMap<String, byte[]>clazz 是一个 Class<?>byteCache是一个HashMap<String, byte[]>,clazz是一个Class<?>
try {
// Create a clip.
Clip clip = AudioSystem.getClip();
// Find the full name.
final String fullPath = prefix + name;
// See what we have already.
byte[] theseBytes = byteCache.get(fullPath);
// Have we found the bytes yet?
if (theseBytes == null) {
// Nope. Read it in.
InputStream is = clazz.getResourceAsStream(fullPath);
// Credit for this goes to Evgeniy Dorofeev:
// http://stackoverflow.com/a/15725969/732016
// Output to a temporary stream.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
// Loop.
for (int b; (b = is.read()) != -1;) {
// Write it.
baos.write(b);
}
// Close the input stream now.
is.close();
// Create a byte array.
theseBytes = baos.toByteArray();
// Put in map for later reference.
byteCache.put(fullPath, theseBytes);
}
// Get a BAIS.
ByteArrayInputStream bais = new ByteArrayInputStream(theseBytes);
// Convert to an audio stream.
AudioInputStream ais = AudioSystem.getAudioInputStream(bais);
// Open the clip.
clip.open(ais);
// Create a new Sound and return it.
return new Sound(clip);
} catch (Exception e) {
// If they're watching, let them know.
e.printStackTrace();
// Nothing to do here.
return null;
}
Run Code Online (Sandbox Code Playgroud)
堆分析后编辑.
在撞车之前大约5秒钟.这是在说:

问题可疑#1:
"com.sun.media.sound.DirectAudioDevice $ DirectClip"的2,062个实例,由""加载占用230,207,264(93.19%)个字节.
关键词com.sun.media.sound.DirectAudioDevice $ DirectClip
这些Clip对象被对象强烈引用,Sound但Sound对象仅在a中被弱引用Vector<WeakReference<Sound>>.
我还可以看到每个Clip对象都包含一个副本byte[].
编辑每菲尔的评论:
我改变了这个:
// Convert to an audio stream.
AudioInputStream ais = AudioSystem.getAudioInputStream(bais);
// Open the clip.
clip.open(ais);
Run Code Online (Sandbox Code Playgroud)
对此:
// Convert to an audio stream.
AudioInputStream ais = AudioSystem.getAudioInputStream(bais);
// Close the stream to prevent a memory leak.
ais.close();
// Open the clip.
clip.open(ais);
clip.close();
Run Code Online (Sandbox Code Playgroud)
这可以修复错误但从不播放任何声音.
如果我省略clip.close()错误仍然发生.如果我仍然发生错误ais.close()后移动到clip.open.
我也尝试LineListener在剪辑创建时添加一个:
@Override
public void update(LineEvent le) {
if (le.getType() == LineEvent.Type.STOP) {
if (le.getLine() instanceof Clip) {
System.out.println("draining");
((Clip)le.getLine()).drain();
}
}
}
Run Code Online (Sandbox Code Playgroud)
每次剪辑完成或停止时(即,开始发生后30次/秒),我会收到"消耗"消息,但仍会得到相同的错误.更换drain有flush没有影响无论是.使用close会使线路在以后不可打开(即使在收听START和呼叫时open也是如此start).
我怀疑问题是您没有明确关闭音频流。您不应该依赖垃圾收集器来关闭它们。
分配似乎在本机分配中失败,而不是在正常的 Java 分配中失败,并且我怀疑“GC 在抛出 OOME 之前运行”的正常行为适用于这种情况。
无论哪种方式,最佳实践是显式关闭流(使用或带有资源的finallyJava 7 )。try这适用于涉及外部资源或堆外内存缓冲区的任何类型的流。
| 归档时间: |
|
| 查看次数: |
495 次 |
| 最近记录: |