I'm trying to play a sound effect in my program using threads, I searched the web and as I understand when a thread reaches the end of the run function it will become free for the GC to collect.
However when I call for the function many times one after another the task manager shows a high increase at memory usage and it never went back down, I waited for 2 minutes for the GC but there was no effect.
Here is the code that I use for playing sound effect:
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();
}
What should I do to reclaim the lost memory?
I can't reproduce this. For me this will peak at about 60k memory usage, which
considering the language in question is perfectly ok imo. Do you have any small
code which reliably reproduces the problem?
public static void main(String[] args) {
for(int i = 0; i < 1000; i++) {
playSfx("somesortsound.wav");
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
}
In addition, what are your expectations when it comes to memory usage? Predicting GC
behaviour is not easy, and you are not guaranteed to see memory freed just because
you wait a while. Especially so if you are not allocating a lot.
I would also get rid of the try catch in your read loop. You don't handle any
exception there anyway, and you catch it outside of the loop as well. If the
stream causes an IO exception chances are you want to abort the loop anyway, right?
Running that method from the following class with compatible files and without any exceptions; I did not see that behaviour.
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);
}
}
}
It could be a memory leak. You are not managing the resources with a finally block (or 'try-with-resources'), coupling this with return statements could result in the internals of the AudioInputStream driver maintaining a reference to the file.
I would use a try-with-resources block.
try(AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(path));){
//your code
}
Also, I wouldn't use the task manager to reliably determine the garbage collectors behaviour, there are many components that determine the size of a Java process in memory.
To gain an understanding of what's going on; use VisualVM. (This is located in the bin directory of your jdk). You can see when the garbage collector is active, as well as the size of the heap compared to its current maximum size. You can also 'Request' (does not guarantee) garbage collection, which will give you an indication of the amount of memory required for 'survivors', and the amount that can be freed.
Admittedly 2 minutes is a long time, but it is worth mentioning garbage collection usually occurs as a failure to allocate memory to a particular generation, but this is dependant on the particular GC algorithm being used. In other words, if it didn't reach an upper limit then it is possible it wouldn't have performed a garbage collection.
This article is potentially worth a read/glance; it details various different garbage collectors and how they are expected to function.
Related
I'm making this status/menu bar app which displays the currently playing song in the status bar for Mac OS X. To get the player status from Spotify I have to create and execute an AppleScript and get the output from this. The result is then drawn using drawString() from Graphics2D, which is set onto a BufferedImage which is then set as the tray icon.
The whole code is 4 classes and easy to follow, available here: https://github.com/ZinoKader/Menify
Now onto the problem
My runnable seems to eat up memory like nothing I've seen before. Every second the application uses 2-3MB more RAM, and reaches for gigabytes if I leave it be. What I have tried so far is to flush and dispose of all my images and Graphics2D resources, flush and close every inputstream, outputstream and destroy the Process object I create in AppleScripthHelper.
Even something like this, just calling a static method starts piling up RAM really quickly.
final Runnable refreshPlayingText = () -> {
AppleScriptHelper.evalAppleScript(ScriptConstants.SPOTIFY_META_DATA_SCRIPT);
}
//update every 50ms
mainExecutor.scheduleAtFixedRate(refreshPlayingText, 0, 50, TimeUnit.MILLISECONDS);
and AppleScriptHelper
class AppleScriptHelper {
private static final int EOF = -1;
static String evalAppleScript(String code) {
String[] args = { "osascript", "-e", code };
try {
Process process = Runtime.getRuntime().exec(args);
process.waitFor();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] bigByteArray = new byte[4096];
InputStream is = process.getInputStream();
copyLargeStream(is, baos, bigByteArray); //write to outputstream
String result = baos.toString().trim();
is.close();
baos.flush();
baos.close();
process.destroyForcibly();
return result;
} catch (IOException | InterruptedException e) {
Log.debug(e);
return null;
}
}
private static void copyLargeStream(InputStream input, OutputStream output, byte[] buffer) throws IOException {
int n;
while (EOF != (n = input.read(buffer))) {
output.write(buffer, 0, n);
}
input.close();
output.close();
}
}
So the question is, what is eating up all of that RAM? Why is seemingly nothing being garbage collected?
What you're facing is not a memory leak!
According to the Java™ Tutorials for Processes and Threads (https://docs.oracle.com/javase/tutorial/essential/concurrency/procthread.html),
A process generally has a complete, private set of basic run-time resources; in particular, each process has its own memory space.
You're creating a new process every 50ms which is most likely what is taking a toll on your available memory.
Creating too many processes will result in thrashing and you will notice reduced CPU performance. Depending on what the process does, there is most likely a more efficient way to achieve your goal without creating 20 processes per second.
When I am playing the audio on my Java desktop application, the sound begins to crackle and fuzz out. I don't know why, any suggestions? I am working on a Pokemon fan game.
static AudioInputStream audio = null;
public static boolean change = false;
static Clip clip = null;
public static void music() {
try {
change = false;
if(!Main.choosegame) {
if(!Main.startup) {
if(Movement.POKEMONBATTLE) {
audio = AudioSystem.getAudioInputStream(new File("Res/music/pokemon battle.wav"));
} else {
audio = AudioSystem.getAudioInputStream(new File("Res/music/route.wav"));
}
} else {
audio = AudioSystem.getAudioInputStream(new File("Res/music/Oak's Speech.wav"));
}
} else {
audio = AudioSystem.getAudioInputStream(new File("Res/music/Title Screen.wav"));
}
clip = AudioSystem.getClip();
clip.open(audio);
clip.start();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
while(clip.isActive() && Main.Running && !change){
}
clip.stop();
audio.close();
Thread.sleep(100);
} catch(UnsupportedAudioFileException uae) {
System.out.println(uae);
} catch(IOException ioe) {
System.out.println(ioe);
} catch(LineUnavailableException lua) {
System.out.println(lua);
} catch (InterruptedException e) {
e.printStackTrace();
} catch(OutOfMemoryError e12) {
clip.stop();
change = true;
try {
audio.close();
} catch (IOException e1) {
e1.printStackTrace();
}
System.out.println("OUT OF MEMORY IN MUSIC");
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void run() {
while(Main.Running) {
music();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Curious stuff. Given that you have found a solution, maybe I shouldn't be adding my two cents. But a few things seem puzzling and not quite matching the audio world as I know it.
Usually crackle and distortion are the result of PCM data points exceeding their bounds. For example, if you have wav files with data that ranges from -32768 to 32767 (16-bit encoding represented via signed shorts), and the values go outside of that range, then distortion of various sorts can occur.
This might occur in your case if more than one wav file is played at a time, and the wavs are already at a very high volume. When their data is summed together for simultaneous playback, the 16-bit range could be exceeded.
If the addition of pauses has the main effect of preventing the wavs from playing at the same time, this could thus also lessen the amount of distortion.
There are some situations where it takes an audio thread a bit of time to finish and respond to a state change. But I can't think of any where crackle or fuzz would be the result. (But that doesn't mean there are no such situations.)
Simply bypassing a number of samples, via skip(), should (theoretically) only help if the same crackle and fuzz are on the original wav files, and you are skipping past the distorted section. However this should result in a click if starting from an already audible volume level.
By the way, you would probably do better to run the files as SourceDataLines than as Clips. Clips are only meant for situations where you are going to replay the sounds many times and can afford to hold the data in memory. As coded, every time you play a sound, you are first loading the entire sound into memory, and then playing it. A Clip does not play until all the data has been loaded into memory. With a SourceDataLine, the playback code reads data as it plays, consuming much less memory.
If you can afford the memory, load the Clip only once into its own variable. After playing a Clip, one can set its cursor back to the start of the Clip and later replay the data without having to reload from the file (as you are continually doing).
Is the crackling always at the beginning? If so, I found some code that skips the first bytes to avoid that:
// Skip some bytes at the beginning to prevent crackling noise.
audio.skip(30);
Source: http://veritas.eecs.berkeley.edu/apcsa-ret/program/projects/lesson13/Sound/SampleRateConverter.java
I have written the code below, but after calling almost 150 times, it throws "Exception in thread "Thread-245" java.lang.OutOfMemoryError: Java heap space" the problem just ocure in (b = new byte[1024 * 1024];)
Java Code:
class Client implements Runnable {
private Socket socket;
private BufferedInputStream bufin = null;
private BufferedOutputStream bufout = null;
String path;
private byte[] b;
Client(Socket socket, String path) {
this.socket = socket;
this.path = path;
}
#Override
public void run() {
try {
bufin = new BufferedInputStream(socket.getInputStream());
bufout = new BufferedOutputStream(new FileOutputStream(path));
b = new byte[1024 * 1024];
int num = 0;
while ((num = bufin.read(b)) != -1)
bufout .write(b, 0, num);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
bufin.close();
bufout .close();
b = null;
socket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
I try to describe the problem more clearly. it is like this:
I write i ServerSocket, when a client send the request the Sever then put the request in a new thread as below:
public void start() {
boolean started = false;
try {
ServerSocket ss = new ServerSocket(8888);
started = true;
while (started) {
String path = "C:/Pic/"+ new SimpleDateFormat("yy-MM-dd-HH_mm_ss_ms").format(new Date()) + ".jpg";
Socket s = ss.accept();
new Thread(new Client(s, path)).start();
}
} catch (IOException e) {
e.printStackTrace();
}
}
in this case the Server will recieve the request from any client...
and also the second problem which i think that problem will cause the Java heap space Exception, that is when the Server recieve the picture, then the picture can not be deleted from the disk before you close the server, when i delete it,
it says "The action can't be completed because the file is open in Java(TM) platform SE Binary". Whereas i have already closed the input output in Client thread.
Now i can not discover the root of the problem, 1. why happen Heap Exception anr 2. why the file is not deletable from disk during server is running.
Thanks alot in advance of your reply!
You're running out of memory because each thread is creating a 1MB array buffer. You want to see what the max heap size is (this depends on a few variables) and increase your max size at the command line, example:
java -Xmx1024M myclass.class
Also, you said you started 150 clients. Why are they not getting garbage collected? Perhaps all 150 are starting instantly and all threads are grabbing for that memory at the same time? I recommend using a thread pool to execute these so you can put a limit on the running client count.
For your problem with the server holding the file (you can't delete it) is because you're getting that heap memory error. All bets are off when you get this. The server is still holding reference to the file and the bufout.close() probably did not work.
I suspect that the heap exhausted does not allow the program to operate properly after the exception occours and then in the finally block, leaving stream open. I suggest you change the program to allocate the memory block before opening the stream; in addition, to close the streams in the finally block I suggest you use the class IOUtils commons-io as described in the example:
closeQuietly
byte[] data = "Hello, World".getBytes();
OutputStream out = null;
try {
out = new FileOutputStream("foo.txt");
out.write(data);
out.close(); //close errors are handled
} catch (IOException e) {
// error handling
} finally {
IOUtils.closeQuietly(out);
}
This allows you to be sure that all streams are closed in the finally block.
I have this piece of code that would copy files from IFS to a local drive. And I would like to ask some suggestions on how to make it better.
public void CopyFile(AS400 system, String source, String destination){
File destFile = new File(destination);
IFSFile sourceFile = new IFSFile(system, source);
if (!destFile.exists()){
try {
destFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
IFSFileInputStream in = null;
OutputStream out = null;
try {
in = new IFSFileInputStream(sourceFile);
out = new FileOutputStream(destFile);
// Transfer bytes from in to out
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
} catch (AS400SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(in != null) {
in.close();
}
if(out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
} // end try catch finally
} // end method
Where
source = full IFS path + filename and
destination = full local path + filename
I would like to ask some things regarding the following:
a. Performance considerations
would this have a big impact in terms for CPU usage for the host AS400 system?
would this have a big impact on the JVM to be used (in terms of memory usage)
would including this to a web app affect app server performance (would it be a heavy task or not)?
would using this to copy multiple files (running it redundantly) be a big burden to all resources involved?
b. Code Quality
Did my implementation of IFSFileInputStream suffice, or would a simple FileInputStream object do the job nicely?
AFAIK, I just needed the AS400 object to make sure the source file referenced is a file from IFS.
I am a noob at AS400 and IFS an would like to ask an honest opinion from experienced ones.
All in all it looks fine (without trying). It should not have a noticeable impact.
in.read() may return 0. Test for -1 instead.
Instead of manually buffering, just wrap in and out with their respective BufferedInputStream/BufferedOutputstream and read one character at a time and test it for -1.
try-catch is hard to get pretty. This will do, but you will later get more experience and learn how to do it somewhat better.
Do NOT swallow exceptions and print them. The code calling you will have no idea whether it went well or not.
When done with an AS400 object, use as400.disconnectAllServices().
See IBM Help example code:
http://publib.boulder.ibm.com/infocenter/iadthelp/v7r1/index.jsp?topic=/com.ibm.etools.iseries.toolbox.doc/ifscopyfileexample.htm
Regards
So, I'm working on a project for class wherein we have to have a game with background music. I'm trying to play a .wav file as background music, but since I can't use clips (too short for a music file) I have to play with the AudioStream.
In my first implementation, the game would hang until the song finished, so I threw it into its own thread to try and alleviate that. Currently, the game plays very slowly while the song plays. I'm not sure what I need to do to make this thread play nice with my animator thread, because we we're never formally taught threads. Below is my background music player class, please someone tell me what I've done wrong that makes it hog all the system resources.
public class BGMusicPlayer implements Runnable {
File file;
AudioInputStream in;
SourceDataLine line;
int frameSize;
byte[] buffer = new byte [32 * 1024];
Thread player;
boolean playing = false;
boolean fileNotOver = true;
public BGMusicPlayer (File inputFile){
try{
file = inputFile;
in = AudioSystem.getAudioInputStream (inputFile);
AudioFormat format = in.getFormat();
frameSize = format.getFrameSize();
DataLine.Info info =new DataLine.Info (SourceDataLine.class, format);
line = (SourceDataLine) AudioSystem.getLine (info);
line.open();
player = new Thread (this);
player.start();
}
catch(Exception e){
System.out.println("That is not a valid file. No music for you.");
}
}
public void run() {
int readPoint = 0;
int bytesRead = 0;
player.setPriority(Thread.MIN_PRIORITY);
while (fileNotOver) {
if (playing) {
try {
bytesRead = in.read (buffer,
readPoint,
buffer.length - readPoint);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (bytesRead == -1) {
fileNotOver = false;
break;
}
int leftover = bytesRead % frameSize;
// send to line
line.write (buffer, readPoint, bytesRead-leftover);
// save the leftover bytes
System.arraycopy (buffer, bytesRead,
buffer, 0,
leftover);
readPoint = leftover;
try {
Thread.sleep(20);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
public void start() {
playing = true;
if(!player.isAlive())
player.start();
line.start();
}
public void stop() {
playing = false;
line.stop();
}
}
You are pretty close, but there are a couple of unusual things that maybe are contributing to the performance problem.
First off, if you are just playing back a .wav, there shouldn't really be a need to deal with any "readpoint" but a value of 0, and there shouldn't really be a need for a "leftover" computation. When you do the write, it should simply be the same number of bytes that were read in (return value of the read() method).
I'm also unclear why you are doing the ArrayCopy. Can you lose that?
Setting the Thread to low priority, and putting a Sleep--I guess you were hoping those would slow down the audio processing to allow more of your game to process? I've never seen this done before and it is really unusual if it is truly needed. I really recommend getting rid of these as well.
I'm curious where your audio file is coming from. Your not streaming it over the web, are you?
By the way, the way you get your input from a File and place it into an InputStream very likely won't work with Java7. A lot of folks are reporting a bug with that. It turns out it is more correct and efficient to generate a URL from the File, and then get the AudioInputStream using the URL as the argument rather than the file. The error that can come up is a "Mark/Reset" error. (A search on that will show its come up a number of times here.)