【问题标题】:java.lang.OutOfMemoryError: Java heap space - Can not fix itjava.lang.OutOfMemoryError:Java 堆空间 - 无法修复
【发布时间】:2019-07-09 22:16:39
【问题描述】:

我有这个代码,这个错误不断出现。我只有一个 excel ,但似乎没有任何效果,我已经尝试了很多我在网上冲浪时发现的选项,但根据我想做的事情似乎没有任何效果。

我使用不同的案例来简化我的业务逻辑,我不会改变它,所以我不知道如何解决这个问题。

private static final String nombreArchivo = "casoPrueba.xlsx";
private static final String rutaArchivo = "src\\test\\resources\\data\\" + nombreArchivo;




 public static XSSFSheet SacaHojaSegunTipo(String tipo) throws IOException {
        if (workbook == null) {
            try (FileInputStream fis = new FileInputStream(new File(rutaArchivo))) {
                workbook = new XSSFWorkbook(fis);
            }
        }
        XSSFSheet spreadsheet = null;
        switch (tipo) {
            case "Candidatos Minorista":
                spreadsheet = workbook.getSheetAt(1);
                break;
            case "Conversion Candidatos":
                spreadsheet = workbook.getSheetAt(2);
                break;
            case "Cuentas":
                spreadsheet = workbook.getSheetAt(3);
                break;
            case "Detalle Cuenta":
                spreadsheet = workbook.getSheetAt(4);
                break;
            case "Historial de Cuentas":
                spreadsheet = workbook.getSheetAt(5);
                break;
            case "Cuentas Financieras":
                spreadsheet = workbook.getSheetAt(6);
                break;
            case "AR Estado Automático":
                spreadsheet = workbook.getSheetAt(7);
                break;
            case "Oportunidades":
                spreadsheet = workbook.getSheetAt(8);
                break;
            default:
                spreadsheet = workbook.getSheetAt(0);
                break;
        }
        return spreadsheet;
    }

我知道这不是一个有效的方法。希望任何人都可以帮助我。

【问题讨论】:

  • 文件大小是多少?
  • @rodrigoap 260 kb
  • 您是否多次加载文件?似乎 SacaHojaSegunTipo 方法在每次请求工作表时都没有理由加载文件。好像不太对。
  • 那么可能是 Maven:stackoverflow.com/questions/4066424/…
  • 您的代码有两个大问题:首先,正如@rodrigoap 所说,每次请求工作表时您都在加载文件。抱歉,我不在乎您的测试如何,这是您的生产代码。一次读取文件(可能读入静态变量,因为这是一个静态方法),然后从该副本返回工作表。其次,你不要关闭你的 FileInputStream (这会吃资源 - 见stackoverflow.com/questions/22889075/…

标签: java excel out-of-memory


【解决方案1】:

类似这样的东西(我试图尽可能少地改变你的代码,所以它并不完美)

private static final String nombreArchivo = "casoPrueba.xlsx";
private static final String rutaArchivo = "src\\test\\resources\\data\\" + nombreArchivo;

private static XSSFWorkbook workbook = null;

public static XSSFSheet SacaHojaSegunTipo(String tipo) throws IOException {
    if (workbook == null) {
        try (FileInputStream fis = new FileInputStream(new File(rutaArchivo))) {
            workbook = new XSSFWorkbook(fis);
        }
    }
    XSSFSheet spreadsheet = null;
    switch (tipo) {
    case "Candidatos Minorista":
        spreadsheet  = workbook.getSheetAt(1);          
        break;
    case "Conversion Candidatos":
        spreadsheet  = workbook.getSheetAt(2);          
        break;
    case "Cuentas":
        spreadsheet  = workbook.getSheetAt(3);          
        break;
    case "Detalle Cuenta":
        spreadsheet  = workbook.getSheetAt(4);          
        break;
    case "Historial de Cuentas":
        spreadsheet  = workbook.getSheetAt(5);          
        break;
    case "Navegar Cuentas":
        spreadsheet  = workbook.getSheetAt(6);          
        break;
    case "Validar Número Operación":
        spreadsheet  = workbook.getSheetAt(7);          
        break;
    case "Validar Tipos de Productos":
        spreadsheet  = workbook.getSheetAt(8);          
        break;
    case "Validar Referencia y Cód. Auto.":
        spreadsheet  = workbook.getSheetAt(9);          
        break;
    default:
        spreadsheet = workbook.getSheetAt(0);
    }
    return spreadsheet;
}

【讨论】:

  • 非常感谢您花时间尝试改进我的代码。我试过这个,但只在我需要获取一个值的第一次工作,当我需要从另一行获取另一个值时,出现错误并且控制台显示 java.lang.NullPointerException
【解决方案2】:

首先,顺便说一句:值得注意的是来自
https://poi.apache.org/apidocs/dev/org/apache/poi/xssf/usermodel/XSSFWorkbook.html#XSSFWorkbook-java.io.InputStream-

的以下内容

使用 InputStream 比使用 File 需要更多内存,所以如果 文件可用,那么您应该改为执行类似的操作

   OPCPackage pkg = OPCPackage.open(path);
   XSSFWorkbook wb = new XSSFWorkbook(pkg);
   // work with the wb object
   ......
   pkg.close(); // gracefully closes the underlying zip file

(虽然wb.close() 也会关闭文件和流)。

现在,您的核心问题是您需要在不再需要工作表或工作簿后释放资源,但目前您无法这样做,因为它们隐藏在方法内部。

因此,您需要授予调用者访问权限,以便在完成后关闭它们。这是一个偏好问题,但我个人更喜欢将电子表格封装到它自己的类中——毕竟,电子表格本身就是一个明确定义的对象!因此,这将需要从 static 进行更改,因此类似于:

public class RutaArchivo implements AutoCloseable {
    private static final String nombreArchivo = "casoPrueba.xlsx";
    private static final String rutaArchivo = "src\\test\\resources\\data\\" + nombreArchivo;

    public static final String CANDIDATOS_MINORISTA = "Candidatos Minorista";
    public static final String CONVERSION_CANDIDATOS = "Conversion Candidatos"
    public static final String CUENTAS = "Cuentas";

    private XSSFWorkbook workbook;

    public RutaArchivo() throws InvalidFormatException, IOException {
        workbook = new XSSFWorkbook(new File(rutaArchivo));
    }

    @Override
    public void close() throws Exception {
        if (workbook != null) {
            workbook.close();
            workbook = null;
        }
    }

    public XSSFSheet sacaHojaSegunTipo(String tipo) {
         if (workbook == null) {
             throw new IllegalStateException("It's closed");
         }
         XSSFSheet spreadsheet = workbook.getSheetAt(0);
         if (tipo .equals(CANDIDATOS_MINORISTA)) {
             spreadsheet  = workbook.getSheetAt(1);
         }else if(tipo.equals(CONVERSION_CANDIDATOS)){
             spreadsheet  = workbook.getSheetAt(2);
         }else if(tipo.equals(CUENTAS)){
              spreadsheet  = workbook.getSheetAt(3);
         // etc, etc
         }

         return spreadsheet;
    }
}

有几点需要注意:

  • 如果我们想让调用者关闭文件,那么我们应该明确地让他们采取一些行动来打开它,否则它太容易被挂起。在上面的示例中,这在创建对象时是隐含的——就像 FileInputStream 等标准 Java 类型一样。

  • 使RutaArchivoAutoCloseable 意味着它可以在try-with-resources 中使用,因此自动关闭-例如:

    try (RutaArchivo rutaArchivo = new RutaArchivo()) {
        XSSFSheet cuentas = rutaArchivo.getSheet(RutaArchivo.CUENTAS);
    }
    
  • 为工作表名称使用常量可减少错误(例如,调用方法时不会出现拼写错误)

  • 由于这是它自己的类而不是 static 方法,因此在编写单元测试时更容易替换或模拟。

无论如何,一些想法 - 希望他们有所帮助。

【讨论】:

  • 嗨@racraman 你的代码真的对我有帮助。真的感谢你的时间。我有几个问题。当我实现方法close()出现在intellij“需要返回类型”和覆盖“方法不会从其超类覆盖,'不适用于构造函数'。此外,在公共rutaArchivo()中出现需要返回类型。我是试图在我的项目中实现你的代码。
  • 抱歉,我离开了我的主计算机,所以我在没有 IDE 的情况下在旅途中输入了它,所以出现了一些错误。 close() 应该是 public void close() throws Exception,这也解决了覆盖问题。还有一些其他的(构造函数应该throw Exception,常量当然应该是String,getSheet 返回XSSFSheet。)。我已经在代码中修复了这些问题,但主要是理解原理以及它是如何工作的,很高兴它有帮助:)
  • 谢谢!。你的代码真的对我有帮助,我有一个问题。我已经实现了关闭方法,但我不知道如何使用它。我不知道如何使这个自动关闭。我让我的方法是这样的: public static XSSFSheet SacaHojaSegunTipo(String tipo) throws IOException { if (workbook == null) { try (FileInputStream fis = new FileInputStream(new File(rutaArchivo))) { workbook = new XSSFWorkbook(fis); } }
  • @recraman,我更新了代码,也将类实现为可自动关闭,只是不知道如何使工作簿可自动关闭。请帮帮我。
  • 但是您评论中的代码仍然来自您的问题;你没有使用我的代码。比如你为什么还在用static?恕我直言,只有少数情况可以接受static 方法,这绝对不是其中之一(我喜欢@TellMeHow 在stackoverflow.com/questions/2671496/… 中的回答)。此外,您仍在 getSheet (ok, SacaHojaSegunTipo) 方法中打开工作簿,这排除了使用 try-with-resources 的可能性。所以试试我的代码,是吗? :) (ps Java 命名约定是方法名以小写开头)。
猜你喜欢
  • 2014-12-02
  • 2019-08-15
  • 2018-12-07
  • 2015-07-13
  • 2010-12-08
  • 2015-05-14
相关资源
最近更新 更多