当我尝试上传80,193KB FITS文件进行处理时,我在跟踪中描述了错误,以显示选择字段。基本上我有一个模拟Web界面,允许用户选择最多6个FITS文件进行上传和处理。当我上传两个[不同的] FITS文件(每个大约54,574KB)时,我不会收到错误。字段在控制台上显示/打印。但是,在上传单个80,193KB文件时,我收到以下错误。我该如何解决?
我最初认为迭代的计算成本很高,但我怀疑它在调用80MB文件的readHDU时出现了:
while ((newBasicHDU = newFits.readHDU()) != null) {
如何有效解决问题呢?我在Windows 7上运行该程序。干杯
跟踪:
SEVERE: Servlet.service() for servlet FitsFileProcessorServlet threw exception
java.lang.OutOfMemoryError: Java heap space
at java.lang.reflect.Array.multiNewArray(Native Method)
at java.lang.reflect.Array.newInstance(Unknown Source)
at nom.tam.util.ArrayFuncs.newInstance(ArrayFuncs.java:1028)
at nom.tam.fits.ImageData.read(ImageData.java:258)
at nom.tam.fits.Fits.readHDU(Fits.java:573)
at controller.FITSFileProcessor.processFITSFile(FITSFileProcessor.java:79)
at controller.FITSFileProcessor.doPost(FITSFileProcessor.java:53)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:128)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:293)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:849)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:583)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:454)
at java.lang.Thread.run(Unknown Source)
代码:
/**
*
* @param
* @return
*/
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
// Check that we have a file upload request
boolean isMultipart = ServletFileUpload.isMultipartContent(request);
if (isMultipart) {
Fits newFits = new Fits();
BasicHDU newBasicHDU = null;
ServletFileUpload upload = new ServletFileUpload(); // Create a new file upload handler
// Parse the request
try {
//List items = upload.parseRequest(request); // FileItem
FileItemIterator iter = upload.getItemIterator(request);
// iterate through the number of FITS FILES on the Server
while (iter.hasNext()) {
FileItemStream item = (FileItemStream) iter.next();
if (!item.isFormField()) {
this.processFITSFile(item, newFits,newBasicHDU );
}
}
} catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/**
*
* @param
* @return
*/
public void processFITSFile(FileItemStream item, Fits newFits, BasicHDU newBasicHDU) throws IOException {
// Process the fits file
if (!item.isFormField()) {
String fileName = item.getName(); //name of the FITS File
try {
System.out.println("Fits File Fields Printout: " + fileName);
InputStream fitsStream = item.openStream();
newFits = new Fits(fitsStream);
System.out.println( "number of hdu's if: " + newFits.getNumberOfHDUs());
while ((newBasicHDU = newFits.readHDU()) != null) { //line 76
System.out.println("Telescope Used: " + newBasicHDU.getTelescope());
System.out.println("Author: " + newBasicHDU.getAuthor());
System.out.println("Observer: " + newBasicHDU.getObserver() );
System.out.println("Origin: " + newBasicHDU.getOrigin() );
System.out.println("End of Printout for: \n" + fileName);
System.out.println();
}
fitsStream.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
答案 0 :(得分:16)
大多数答案都围绕着从默认值(64MB)增加堆大小。 64MB可以解释为什么你能够成功上传54,574KB,因为它在那个大小之下,我打赌tomcat和你的程序在启动时不会占用超过10MB。增加记忆力是一个好主意,但它确实治疗的症状不是疾病。
如果您计划允许多个用户上传大文件,那么同时上传80MB文件的两个用户将需要160MB。并且,如果3个用户这样做,你将需要240MB,等等。这是我的建议。在将其写入磁盘之前,找一个不会将这些内容读入RAM的库。这将使您的应用程序扩展,这是解决此问题的真正解决方案。
使用jconsole(随JDK一起提供)在程序运行时查看堆大小。 Jconsole非常易于使用,您无需配置JVM即可使用它。所以从这个意义上说它比分析器容易得多,但是你不会得到关于你的程序的更多细节。但是,您可以更好地看到内存的三个部分(Eden,Survivor和Tenured)。有时奇怪的事情会导致其中一个区域内存不足,即使你已经为JVM分配了大量内存。 JConsole将向您展示这类内容。
答案 1 :(得分:5)
听起来你没有为Tomcat分配足够的内存 - 你可以通过指定-Xmx512m来解决这个问题,例如分配最多512Mb的内存。
有关如何设置此内容的详细信息,请参阅here。
答案 2 :(得分:3)
最好的解决方法是增强代码,使其不会不必要地重复内存中的数据。堆栈跟踪表明代码正在尝试克隆内存中的文件内容。如果无法增强(第三方?)代码以使其不这样做,而是立即处理它,那么配置/使用commons FileUpload以便它不会保持上传文件在内存中,而是在本地磁盘文件系统的临时存储中。
最好尽量减少使用的内存,然后使用带有一点阈值的DiskFileItemFactory
(顺便说一下默认为10KB,这是可以承受的)。
ServletFileUpload upload = new ServletFileUpload(new DiskFileItemFactory());
这样你就有足够的记忆力来进行实际处理。
如果仍然达到堆内存限制,那么下一步确实会增加堆。
答案 3 :(得分:2)
你正在尝试使用比你拥有的更多的RAM。
尝试在启动程序时添加-Xmx
标志来增加最大内存。
java -Xmx128m youProgram
这将为您的程序分配128兆字节的内存 maximum 。
答案 4 :(得分:0)
java.lang.OutOfMemoryError意味着您已超出JVM分配的内存。使用-Xmx更改JVM的最大内存堆大小。 (谢谢Software Monkey)
你可以这样看看jvm内存的大小:
MemoryMXBean memoryBean = ManagementFactory.getMemoryMXBean();
System.out.println( memoryBean.getHeapMemoryUsage() );