我写了个下载程序,用Java写的,但是写完以后发现下载大文件的时候报错,内存溢出,能看看是哪的问题么?
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我写了个下载程序,用Java写的,但是写完以后发现下载大文件的时候报错,内存溢出,能看看是哪的问题么?相关的知识,希望对你有一定的参考价值。
我写了个下载程序,用Java写的,但是写完以后发现下载大文件的时候报错,内存溢出,能看看是哪的问题么?Exception in thread "http-bio-8080-exec-10" java.lang.OutOfMemoryError: Java heap space,要怎么优化一下才行呢?我把代码也粘到下面吧,麻烦大神们帮我看看是代码的问题么?
public class downloadServlet extends HttpServlet
@Override
protected void service(HttpServletRequest request,
HttpServletResponse response) throws ServletException, IOException
String path = request.getParameter("filename");
path = new String(path.getBytes("ISO-8859-1"), "utf-8");
System.out.println("23:"+path);
download(path, request, response);
@SuppressWarnings("deprecation")
public HttpServletResponse download(String path,
HttpServletRequest request, HttpServletResponse response)
try
System.out.println(request.getRealPath("/") + "/"
+ "upload/" + path);
File file = new File(request.getRealPath("/") + "/"
+ "upload/" + path);
String filename = file.getName();
InputStream fis = new BufferedInputStream(new FileInputStream(file));
byte[] buffer = new byte[fis.available()];
fis.read(buffer);
fis.close();
response.reset();
response.addHeader("Content-Disposition", "attachment;filename="
+ new String(filename.getBytes("utf-8"), "ISO-8859-1"));
response.addHeader("Content-Length", "" + file.length());
OutputStream toClient = new BufferedOutputStream(response
.getOutputStream());
response.setContentType("application/octet-stream");
toClient.write(buffer);
toClient.flush();
toClient.close();
catch (IOException ex)
ex.printStackTrace();
return response;
不能一次读取完,大文件很容易内存溢出。参考下:
public static void download(String path, HttpServletResponse response) throws Exceptiontry
File file = new File(path);
if (file.exists())
String filename = file.getName();
InputStream fis = new BufferedInputStream(new FileInputStream( file));
response.reset();
response.setContentType("application/x-download");
response.addHeader("Content-Disposition","attachment;filename="+ new String(filename.getBytes(),"iso-8859-1"));
response.addHeader("Content-Length", "" + file.length());
OutputStream toClient = new BufferedOutputStream(response.getOutputStream());
response.setContentType("application/octet-stream");
byte[] buffer = new byte[1024 * 1024 * 4];
int i = -1;
while ((i = fis.read(buffer)) != -1)
toClient.write(buffer, 0, i);
fis.close();
toClient.flush();
toClient.close();
try
response.wait();
catch (InterruptedException e)
// TODO Auto-generated catch block
e.printStackTrace();
else
PrintWriter out = response.getWriter();
out.print("<script>");
out.print("alert(\\"not find the file\\")");
out.print("</script>");
catch (IOException ex)
PrintWriter out = response.getWriter();
out.print("<script>");
out.print("alert(\\"not find the file\\")");
out.print("</script>");
追问
我仔细看了看,好像没看出哪些代码可以避免内存溢出呀,能不能再帮忙说的明白一点呢?多谢你了
追答你的代码里面一次读取的:
byte[] buffer = new byte[fis.available()];fis.read(buffer);追问
哇,真的解决了,我还专门查了查这个方法,看来不靠谱啊这方法,多谢你了!
参考技术A 主要是你的Buffer开的太大了,4*1024*1024 也就是4G了。这个缓存很有点大。其实使用1024或者4096就行了。 参考技术B 你把这么大的文件都读到内存里,当然受不了了,你去网上搜搜看看怎么分块,或者内存分流读取把!老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手
老Java程序员花1天时间做了个飞机大战
引言:
前两天我发现CSDN上有两篇飞机大战的文章异常火爆,各种指标都很高(阅读、点赞、评论、收藏等),但都是python写的,竟然不是我大Java,说实话作为老java选手,我心里是有那么一些失落的,难道我大java打飞机不行?就算大java打飞机不行,那我用单身30年的打飞机手速,我肯定行(反正我的代码我做主,就是玩!),于是我决定一展伸手,用java写了一个飞机大战。我就问你们我打飞机行不行,我媳妇都说行,你们呢?欢迎我亲爱的大Java选手们 点赞+评论+收藏!给我冲、冲、冲。。。
代码实现
创建窗口
首先创建一个游戏窗体类GameFrame,继承至JFrame,用来显示在屏幕上(window的对象),每个游戏都有一个窗口,设置好窗口标题、尺寸、布局等就可以。
/*
* 游戏窗体类
*/
public class GameFrame extends JFrame {
public GameFrame() {
setTitle("飞机大战");//设置标题
setSize(526, 685);//设定尺寸
setLayout(new BorderLayout());
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//点击关闭按钮是关闭程序
setLocationRelativeTo(null); //设置居中
setResizable(false); //不允许修改界面大小
}
}
创建面板容器GamePanel继承至JPanel
package main;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
/*
* 画布类
*/
public class GamePanel extends JPanel{
GamePanel gamePanel=this;
private JFrame mainFrame=null;
//构造里面初始化相关参数
public GamePanel(JFrame frame){
this.setLayout(null);
mainFrame = frame;
mainFrame.setVisible(true);
}
@Override
public void paint(Graphics g) {
}
}
再创建一个Main类,来启动这个窗口,用来启动。
package main;
public class Main {
//主类
public static void main(String[] args) {
GameFrame frame = new GameFrame();
GamePanel panel = new GamePanel(frame);
frame.add(panel);
frame.setVisible(true);//设定显示
}
}
右键执行这个Main类,窗口建出来了
创建菜单及菜单选项
创建菜单
private void initMenu(){
// 创建菜单及菜单选项
jmb = new JMenuBar();
JMenu jm1 = new JMenu("游戏");
jm1.setFont(new Font("微软雅黑", Font.BOLD, 15));// 设置菜单显示的字体
JMenu jm2 = new JMenu("帮助");
jm2.setFont(new Font("微软雅黑", Font.BOLD, 15));// 设置菜单显示的字体
JMenuItem jmi1 = new JMenuItem("开始新游戏");
JMenuItem jmi2 = new JMenuItem("退出");
jmi1.setFont(new Font("微软雅黑", Font.BOLD, 15));
jmi2.setFont(new Font("微软雅黑", Font.BOLD, 15));
JMenuItem jmi3 = new JMenuItem("操作说明");
jmi3.setFont(new Font("微软雅黑", Font.BOLD, 15));
JMenuItem jmi4 = new JMenuItem("胜利条件");
jmi4.setFont(new Font("微软雅黑", Font.BOLD, 15));
jm1.add(jmi1);
jm1.add(jmi2);
jm2.add(jmi3);
jm2.add(jmi4);
jmb.add(jm1);
jmb.add(jm2);
mainFrame.setJMenuBar(jmb);// 菜单Bar放到JFrame上
jmi1.addActionListener(this);
jmi1.setActionCommand("Restart");
jmi2.addActionListener(this);
jmi2.setActionCommand("Exit");
jmi3.addActionListener(this);
jmi3.setActionCommand("help");
jmi4.addActionListener(this);
jmi4.setActionCommand("win");
}
实现ActionListener并重写方法actionPerformed
actionPerformed方法的实现
@Override
public void actionPerformed(ActionEvent e) {
String command = e.getActionCommand();
UIManager.put("OptionPane.buttonFont", new FontUIResource(new Font("宋体", Font.ITALIC, 18)));
UIManager.put("OptionPane.messageFont", new FontUIResource(new Font("宋体", Font.ITALIC, 18)));
if ("Exit".equals(command)) {
Object[] options = { "确定", "取消" };
int response = JOptionPane.showOptionDialog(this, "您确认要退出吗", "",
JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,
options, options[0]);
if (response == 0) {
System.exit(0);
}
}else if("Restart".equals(command)){
if(startFlag){
Object[] options = { "确定", "取消" };
int response = JOptionPane.showOptionDialog(this, "游戏中,您确认要重新开始吗", "",
JOptionPane.YES_OPTION, JOptionPane.QUESTION_MESSAGE, null,
options, options[0]);
if (response == 0) {
//需要先结束游戏
realGameEnd(1);
restart();
}
}else{
restart();
}
}else if("help".equals(command)){
JOptionPane.showMessageDialog(null, "游戏开始后,要先动鼠标到飞机处,触发移动效果,然后飞机就会跟随鼠标移动!",
"提示!", JOptionPane.INFORMATION_MESSAGE);
}else if("win".equals(command)){
JOptionPane.showMessageDialog(null, "得分1000,获得胜利!",
"提示!", JOptionPane.INFORMATION_MESSAGE);
}
}
创建背景
在GamePanel类中重写paint方法,绘制背景图即可
//绘图方法
@Override
public void paint(Graphics g) {
gameHeight = this.getHeight();
gameWidth = this.getWidth();
//绘制背景
g.drawImage((BufferedImage)imageMap.get("bg"), 0, -150, null);
}
开启主线程
主线程,用来重绘页面,重绘全部交给主线程,主线程调用 repaint方法就行,要产生动画就要靠这个repaint。
//刷新线程,用来重新绘制页面
private class RefreshThread implements Runnable {
@Override
public void run() {
while (startFlag) {
repaint();
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
在GamePanel的构造里面启动这个主线程
有了这个主线程刷新,待会我们更新飞机的位置,飞机就会移动,不需要另外的代码去调用repaint方法了(这是我的做法,仅供参考)。
创建我方飞机
创建MyPlane类,属性有坐标x、y,宽高、图片、是否存活、是否可以移动等;方法主要有绘制、移动、射击等。
public class MyPlane {
private int x = 0;
private int y = 0;
private int width = 0;
private int height = 0;
private BufferedImage image = null;
private GamePanel panel=null;
private HashMap imageMap=null;
private boolean alive=true;
private boolean canMove=false;
private int key=1;
private HashMap boomImageMap=null;
private boolean hitFlag=false;//正在碰撞
public MyPlane(int x,int y,int width,int height,GamePanel panel) {
this.x=x;
this.y=y;
this.width=width;
this.height=height;
this.panel=panel;
this.imageMap=panel.imageMap;
this.image=(BufferedImage)imageMap.get("myplane1");
this.boomImageMap=panel.mypalneBoomImageMap;
}
//绘制
public void draw(Graphics g) {
g.drawImage(image, x, y, width,height, null);
}
}
创建(这里只是创建好了飞机对象,需要绘制)
//创建自己飞机
private void initMyPlane() {
myPlane = new MyPlane(200, 530, 132, 86, this);
}
在paint方法中绘制
//绘图方法
@Override
public void paint(Graphics g) {
gameHeight = this.getHeight();
gameWidth = this.getWidth();
//绘制背景
g.drawImage((BufferedImage)imageMap.get("bg"), 0, -150, null);
//绘制飞机
if(myPlane!=null){
myPlane.draw(g);
}
}
鼠标事件监听
加入监听是为了让飞机跟随鼠标移动,我这里定的规则是第一次鼠标必须移动到飞机上,然后飞机才会跟随。
代码里面用一个属性canMove来控制,默认是false,只有鼠标第一次移入到飞机上时,这个属性设置为true,然后就可以跟随鼠标移动了。
//鼠标事件的创建
private void createMouseListener() {
MouseAdapter mouseAdapter = new MouseAdapter() {
@Override
public void mouseMoved(MouseEvent e) {
int x = e.getX();
int y = e.getY();
if(myPlane==null) return ;
//飞机第一次是不允许移动的,只有飞机的canMove为true才去跟随
if(myPlane.isCanMove()){
myPlane.move(x,y);
return;
}
//判断鼠标的移入,如果移动到飞机上则canMove设置为true
if(myPlane.isPoint(x,y)){
myPlane.setCanMove(true);
}
}
};
addMouseMotionListener(mouseAdapter);
addMouseListener(mouseAdapter);
}
来实现一下MyPlane的move方法,这里处理了边界,保证飞机不出界,同时保证鼠标在飞机的中间位置
//飞机跟随鼠标移动
public void move(int x,int y) {
//判断范围,当横向移动在窗口范围内
if(x-width/2>=0 && x<=panel.getWidth()-width/2){
this.x=x-width/2;
}
//判断范围,当纵向移动在窗口范围内
if(y-height/2>=0 && y<=panel.getHeight()-height/2){
this.y=y-height/2;
}
}
创建子弹类
属性也就是坐标、宽高这些,给子弹加入移动方法
//移动
void move(){
new Thread(new Runnable() {
@Override
public void run() {
while(alive){
y-=speed;
if(y<=0){
clear();
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
飞机类加入射击方法,200毫秒创建一发子弹
//射击
void shoot() {
new Thread(new Runnable() {
@Override
public void run() {
while(alive){
//创建子弹
createBullet();
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
private void createBullet() {
Bullet bullet = new Bullet(x+width/2-10, y, 20, 30, panel);
panel.bulletList.add(bullet);
new MusicPlayer("/music/shoot.wav").play();
}
}).start();
}
别忘记在paint方法里面绘制子弹出来
//绘制子弹
Bullet bullet=null;
for (int i = 0; i < bulletList.size(); i++) {
bullet = (Bullet)bulletList.get(i);
bullet.draw(g);
}
创建敌机
创建抽象类Plane
package main;
import java.awt.Graphics;
public abstract class Plane {
public abstract 以上是关于我写了个下载程序,用Java写的,但是写完以后发现下载大文件的时候报错,内存溢出,能看看是哪的问题么?的主要内容,如果未能解决你的问题,请参考以下文章
JSP页面怎样弹出提示框? 本来是要弹出Servlet传递回来的处理结果的,但是发现写了个最简单的
老Java程序员花一天时间写了个飞机大战,媳妇直呼打飞机高手