目前我正在处理我的第一次内存泄漏,我似乎无法修复它。我的程序应该通过一个名为JukeBox
的类播放.wav音频文件。 ~316,500K
工作正常,但每次播放新声音时,java正在使用的内存量在任务管理器中上升,一旦我点击System.err
,我在Exception in thread "Thread-6" java.lang.OutOfMemoryError: Java heap space
at com.sun.media.sound.DirectAudioDevice$DirectClip.open(DirectAudioDevice.java:1135)
at JukeBox.play(JukeBox.java:53)
at Instrument.play(Instrument.java:65)
at Conductor.play(Conductor.java:78)
at Game$2.run(Game.java:42)
终端中收到以下错误:
public void play(int pitch){
jukez = new JukeBox("pianoNote.wav");
Thread clipkill = new Thread() {
public void run() {
try {
Thread.sleep(jukez.getMicrosecondLength()*1000);
} catch (Exception e){System.out.println(e);}
jukez.killTheTunes();
}
};
jukez.play();
clipkill.start();
}
在我的调试器中,线程6是不断寻找键盘输入的两个线程之一(特别是触发声音的线程)。
以下是错误后我的调试器的屏幕截图:image from debugger of threads
所以我很确定我的问题是由于有太多的开放剪辑线程引起的,但那是我真的很困惑的地方,因为我以为我正在关闭它们,而实际上它们看起来就像是挂在那里,等候。
这是我的代码,我认为我正在关闭剪辑:
public class JukeBox{
private AudioInputStream stream;
private AudioFormat format;
private DataLine.Info info;
private Clip clip;
private String name;
/**
* Constructor for objects of the class Jukebox
* @param str the filename of the wave file to pla
*/
public JukeBox(String str){
try{
stream = AudioSystem.getAudioInputStream(new File(str));
name = str;
format = stream.getFormat();
info = new DataLine.Info(Clip.class, format);
clip = (Clip) AudioSystem.getLine(info);
} catch(Exception E){
System.out.println(E);
}
}
/**
* Returns the clip's length in microseconds.
* @return the clip's length in microseconds.
*/
public long getMicrosecondLength(){return clip.getMicrosecondLength();}
/**
* Plays the music on its own thread, enabling the game to run while music plays
*/
public void play(){
try {
clip.open(stream);
clip.start();
}
catch (Exception e) {
System.out.println(e);
}
}
/**
* Ends the music and it's thread as well
*/
public void killTheTunes(){
try{stream.close();}catch (Exception e) {
System.out.println(e);
}
clip.close();
}
/**
* Returns the filename the jukebox was initially set to play
* @return the filename the jukebox was initially set to play
*/
public String toString(){
return name;
}
}
和JukeBox在这里:
display = new Display();
copper = new Conductor();
display.main(); //sets up all the jframe things and graphics
Thread p1 = new Thread() {
public void run() {
while(true){
if(display.panel.playerClicked(1)){
display.panel.p1 = Color.black;
display.panel.p1 = copper.play(4,copper.determinePitch(display.panel.getY(1)));
display.panel.pause(250);
display.panel.p1 = Color.black;
}
}
}
};
Thread p2 = new Thread() {
public void run() {
while(true){
if(display.panel.playerClicked(2)){
display.panel.p2 = Color.black;
display.panel.p2 = copper.play(0,copper.determinePitch(display.panel.getY(2)));
display.panel.pause(250);
display.panel.p2 = Color.black;
}
}
}
};
p1.start();
p2.start();
我不知道为什么我仍然遇到堆内存问题。谢谢
编辑:
我的代码结构有点复杂,所以我会尝试解释它。运行游戏的主要方法如下:
import java.awt.*;
import java.awt.geom.*;
import java.awt.event.*;
import javax.swing.JPanel;
import java.util.ArrayList;
import java.awt.image.BufferedImage;
import java.io.*;
import javax.imageio.ImageIO;
import java.awt.Image;
import javax.swing.JLabel;
import javax.swing.ImageIcon;
public class Canvas extends JPanel {
// attributes
private Rectangle player1;
private Rectangle player2;
public final int x = 800 + 200;
public final int y = 1100/2 + 200;
private boolean[] player1bools = new boolean[5];
private boolean[] player2bools = new boolean[5];
BufferedImage dimg;
Color p1 = Color.black; //these are intentionally public so game can change them
Color p2 = Color.black;
// constructor
public Canvas() {
// initialize object
player1 = new Rectangle(250, 50, 50, 50);
player2 = new Rectangle(50, 50, 50, 50);
// set canavs background colour
// add the key listener in the constructor of your canavas/panel
addKeyListener(new myKeyListener());
BufferedImage img = null;
try {
img = ImageIO.read(new File("Pencils.jpg"));
} catch (IOException e) {}
dimg = resize(img, x, y-20);
// ensure focus is on this canavas/panel for key operations.
setFocusable(true);
setBackground(new Color(0,0,0,0));
setOpaque(true);
Thread gameLoop = new Thread() {
public void run() {
while(true){
updatePlayers();
repaint();
pause(20);
}
}
};
gameLoop.start();
}
public int getY(int i){
if(i == 1){return player1.y;}
if(i == 2){return player2.y;}
return -1;
}
public boolean isOptimizedDrawingEnabled(){return false;}
/**
* This method was made by David Kroukamp on
* http://stackoverflow.com/questions/14548808/scale-the-imageicon-automatically-to-label-size
*/
public static BufferedImage resize(BufferedImage image, int width, int height) {
BufferedImage bi = new BufferedImage(width, height, BufferedImage.TRANSLUCENT);
Graphics2D g2d = (Graphics2D) bi.createGraphics();
g2d.addRenderingHints(new RenderingHints(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY));
g2d.drawImage(image, 0, 0, width, height, null);
g2d.dispose();
return bi;
}
private void updatePlayers() {
if (player1bools[0]) {
moveX(-5, player1);
}
if (player1bools[2]) {
moveX(5, player1);
}
if (player1bools[1]) {
moveY(-5, player1);
}
if (player1bools[3]) {
moveY(5, player1);
}
if (player2bools[0]) {
moveX(-5, player2);
}
if (player2bools[2]) {
moveX(5, player2);
}
if (player2bools[1]) {
moveY(-5, player2);
}
if (player2bools[3]) {
moveY(5, player2);
}
}
// painting
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics);
graphics.clearRect(0, 0, getWidth(), getHeight()); //clears the trails
graphics.drawImage(dimg,0,0,null);
Graphics2D graphics2d = (Graphics2D) graphics;
graphics.setColor(p1);
graphics2d.fill(player1);
graphics.setColor(p2);
graphics2d.fill(player2);
// */
}
// function which essentially re-creates rectangle with varying x
// orientations. (x-movement)
public void moveX(int mutationDistance, Rectangle sampleObject) {
//I don't want horizontal movement to work
/*
sampleObject.setBounds(sampleObject.x + mutationDistance,
sampleObject.y, sampleObject.width, sampleObject.height);
*/
}
// function which essentially re-creates rectangle with varying y
// orientations. (y-movement)
public void moveY(int mutationDistance, Rectangle sampleObject) {
if(sampleObject.y < 0) {
sampleObject.y = 0;
}
else if(sampleObject.y > 670) {
sampleObject.y = 670;
}
sampleObject.setBounds(sampleObject.x, sampleObject.y
+ mutationDistance, sampleObject.width, sampleObject.height);
}
public boolean playerClicked(int i){
if(i == 1){return player1bools[4];}
if(i == 2){return player2bools[4];}
return false;
}
// listener
private class myKeyListener implements KeyListener {
// implement all the possible actions on keys
public void keyPressed(final KeyEvent keyEvent) {
if (keyEvent.getKeyCode() == KeyEvent.VK_ESCAPE) {
System.exit(0);
}
if (keyEvent.getKeyCode() == KeyEvent.VK_RIGHT) {
player1bools[2] = true;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_LEFT) {
player1bools[0] = true;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_UP) {
player1bools[1] = true;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_DOWN) {
player1bools[3] = true;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_SLASH) {
player1bools[4] = true;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_D) {
player2bools[2] = true;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_A) {
player2bools[0] = true;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_W) {
player2bools[1] = true;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_S) {
player2bools[3] = true;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_F) {
player2bools[4] = true;
}
}
public void keyReleased(KeyEvent keyEvent) {
if (keyEvent.getKeyCode() == KeyEvent.VK_RIGHT) {
player1bools[2] = false;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_LEFT) {
player1bools[0] = false;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_UP) {
player1bools[1] = false;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_DOWN) {
player1bools[3] = false;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_SLASH) {
player1bools[4] = false;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_D) {
player2bools[2] = false;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_A) {
player2bools[0] = false;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_W) {
player2bools[1] = false;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_S) {
player2bools[3] = false;
}
if (keyEvent.getKeyCode() == KeyEvent.VK_F) {
player2bools[4] = false;
}
}
public void keyTyped(KeyEvent keyEvent) {
}
}
public static void pause(int secs) {
try {
Thread.sleep(secs);
} catch (Exception e) {}
}
}
这是Canvas类,可以检测关键动作。显示基本上只是初始化一个JFrame并向其添加一个Canvas。
public class Conductor
{
private ArrayList<Instrument> symphony;
/**
* Constructor for objects of class Conductor, initializes the instruments
*/
public Conductor()
{
ArrayList<String> pianoList = new ArrayList<String>();
for(int i = 1; i <= 11; i++){
pianoList.add("piano"+i);
}
ArrayList<String> bassList = new ArrayList<String>();
for(int i = 1; i <= 11; i++){
bassList.add("bass"+i);
}
ArrayList<String> guitarList = new ArrayList<String>();
for(int i = 1; i <= 11; i++){
guitarList.add("guitar"+i);
}
ArrayList<String> percList = new ArrayList<String>();
for(int i = 1; i <= 11; i++){
percList.add("perc"+i);
}
ArrayList<String> vibeList = new ArrayList<String>();
for(int i = 1; i <= 11; i++){
vibeList.add("vibe"+i);
}
Instrument piano = new Instrument(pianoList, new Color(19,130,206));
Instrument bass = new Instrument(bassList, new Color(100,83,38));
Instrument guitar = new Instrument(guitarList, new Color(244,184,16));
Instrument perc = new Instrument(percList, new Color(24,57,165));
Instrument vibe = new Instrument(vibeList, new Color(57,24,135));
symphony = new ArrayList<Instrument>();
symphony.add(piano);
symphony.add(bass);
symphony.add(guitar);
symphony.add(perc);
symphony.add(vibe);
}
public static int determinePitch(int i){
if(i == -1){return i;}
if(i>600){return 1;}
if(i>535){return 2;}
if(i>470){return 3;}
if(i>410){return 4;} //these numbers correspond to the approximate heights of the colors in the background image
if(i>345){return 5;}
if(i>275){return 6;}
if(i>215){return 7;}
if(i>155){return 8;}
if(i>90){return 9;}
if(i>25){return 10;}
return 11;
}
/**
* Calls an instrument and makes it play a noise
* @param inst the instrument index to play
* @param pitch the pitch for the instrument to play
*/
public Color play(int inst, int pitch){
symphony.get(inst).play(pitch);
return symphony.get(inst).getColor();
}
}
最后有导体在这里管理乐器:
class Book:
title = ''
pages = 0
def __init__(self, title='', pages=0):
self.title = title
self.pages = pages
def __str__(self):
return self.title
def __gt__(self, other):
return self.pages > other
def __ge__(self, other):
return self.pages >= other
因此,对于结构的整体下降:
控制玩家的线程检测他们各自的玩家是否击中了触发键。如果是这样,他们会打电话给售票员演奏乐器。它们还会改变该玩家矩形的颜色。当指挥播放乐器时,它使用乐器的播放方法,该方法调用乐器的JukeBox方法,该方法使用导致崩溃的剪辑。
答案 0 :(得分:3)
Thread.sleep()以毫秒为参数,所以行:
Thread.sleep(jukez.getMicrosecondLength()*1000);
应该是:
Thread.sleep(jukez.getMicrosecondLength()/1000);
您的代码中可能还有其他问题,但目前,由于您等待的时间太长,您永远无法进入killTheTunez()
来电。
答案 1 :(得分:0)
所以我解决了我的问题。它有一些优点和缺点,但总体而言是有效的。这就是我所做的。
我在Instrument中改变了我的play方法,所以每次都没有创建一个新的JukeBox,而是Instrument有一个JukeBox []持有JukeBoxes。我最初正在重新创建它们,因为我只能播放一次,但我发现这是因为我必须将Clip的读取帧移回0.所以现在play方法很简单,就是这样:
public void play(int pitch){
sounds[pitch-1].clip.setFramePosition(0);
sounds[pitch-1].play();
}
声音是JukeBox []持有声音。
有点滞后,声音顺序有点剪裁。为了解决这个问题,我采取了让玩家长方形移动得更慢的hacky方法,加上游戏的暂停时间,因此玩家的投球速度可以更快,让他们有时间关闭。从好的方面来说,没有更多的内存泄漏。