所以我找到了很多代码here来完成我需要做的事情。但是,我确实通过Jconsole
注意到它只是记录和记录数据而不是释放数据。显然没有line.close()
电话。这是一个问题,以确定数据是否良好传递(必须足够响亮)线必须不断记录,因此关闭它将不允许进行该确定。有没有办法释放line.read()
没有line.close()
占用的所有内存?我注意到我能够在内存选项卡中从Jconsole
执行GC,但它会在约System.gc()
左右调用。我知道的是你不应该调用的内容,但它似乎能够清除缓冲区正在使用的内存,因此line.close()
似乎不是唯一的方法。
以下是链接中的代码,除了我撕掉了GUI元素,并添加了我认为应该执行GC的位置。
import javax.swing.SwingUtilities;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.TargetDataLine;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.LineUnavailableException;
import javax.sound.sampled.Control.Type;
import javax.sound.sampled.FloatControl;
import javax.sound.sampled.DataLine.Info;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.SourceDataLine;
public class LevelMeter {
private float amp = 0f;
private float peak = 0f;
public void setAmplitude(float amp) {
this.amp = Math.abs(amp);
}
public void setPeak(float peak) {
this.peak = Math.abs(peak);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
LevelMeter meter = new LevelMeter();
new Thread(new Recorder(meter)).start();
}
});
}
static class Recorder implements Runnable {
final LevelMeter meter;
Recorder(final LevelMeter meter) {
this.meter = meter;
}
@Override
public void run() {
AudioFormat fmt = new AudioFormat(44100f, 16, 1, true, false);
final DataLine.Info info = new DataLine.Info(SourceDataLine.class, fmt, 1);
final SourceDataLine soundLine = (SourceDataLine) AudioSystem.getLine(info);
final int bufferByteSize = 2048;
TargetDataLine line;
try {
line = AudioSystem.getTargetDataLine(fmt);
line.open(fmt, bufferByteSize);
soundLine.open(fmt);
} catch(LineUnavailableException e) {
System.err.println(e);
return;
}
byte[] buf = new byte[bufferByteSize];
float[] samples = new float[bufferByteSize / 2];
float lastPeak = 0f;
line.start();
for(int b; (b = line.read(buf, 0, buf.length)) > -1;) {
// convert bytes to samples here
for(int i = 0, s = 0; i < b;) {
int sample = 0;
sample |= buf[i++] & 0xFF; // (reverse these two lines
sample |= buf[i++] << 8; // if the format is big endian)
// normalize to range of +/-1.0f
samples[s++] = sample / 32768f;
}
float rms = 0f;
float peak = 0f;
for(float sample : samples) {
float abs = Math.abs(sample);
if(abs > peak) {
peak = abs;
}
rms += sample * sample;
}
rms = (float)Math.sqrt(rms / samples.length);
if(lastPeak > peak) {
peak = lastPeak * 0.875f;
}
if(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory() > 7000000){
System.gc();
System.gc();
}
if(peak > 0.20){
sourceLine.start();
}
lastPeak = peak;
System.out.println(peak);
setMeterOnEDT(rms, peak);
}
}
void setMeterOnEDT(final float rms, final float peak) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
meter.setAmplitude(rms);
meter.setPeak(peak);
}
});
}
}
}
编辑:我没有意识到这是因为我没有关闭线路是内存未被释放的原因。问题是我无法关闭线路,因为我只需要抓住表明有人正在对着话筒说话的字节。如果我关闭线路然后它有效地静音,或者至少这是我的理解。但是,我已经设置了一种脏的方法来在我上面的更新代码中不时擦除缓冲区。我目前正在尝试获取字节并在它们超过某个阈值时播放它们。我可以从缓冲区中写出另一行正在读取的内容吗?或者我是否必须设置第二个缓冲区来写出来?
答案 0 :(得分:0)
您看到的内存增加可能是因为垃圾收集器到目前为止尚未在程序的时间范围内运行。我怀疑如果你的程序运行时间足够长,新一代的空间不足,你会发现大部分内存都可以再次使用。
这似乎由TargetDataLine类的Javadoc支持。这个类是缓冲,这意味着它只消耗固定数量的内存,直到该行关闭才能回收,一旦你的程序不再需要从该行读取,你应该这样做线。所以我怀疑堆大小的增加只是来自读取该行时的瞬态对象。