资源未被释放

4
我们有一个传统系统,其中有一个管理模块,允许用户上传jar文件。上传后,会对jar文件进行验证,如果不符合内部规则,则会被删除。
问题在于,Windows会抛出一个异常,告诉我该文件“已经被其他进程使用”。(当我调用Files.delete(tmpJar)时)。我无法确定为什么该文件被打开了。对我来说,似乎我已经关闭了所有东西。
首先,我们使用primefaces(4.0)上传文件。Primefaces依赖于commons-fileupload(1.3.1)。它调用以下方法:
public void handleFileUpload(FileUploadEvent event) {
   Path tmpJar = null;
   try {
      tmpJar = Files.createFile(Paths.get(event.getFile().getFileName()));
      Files.write(tmpJar, event.getFile().getContents());
   } catch (IOException e) {
      LOGGER.error(e.getMessage(), e);
   }

   if (tmpJar != null) {
      try {
         this.validateJar(tmpJar.toString());
         Files.delete(tmpJar);
      } catch (IOException e) {
         LOGGER.error(e.getMessage(), e);
      }
   }
}

在使用NIO Files.write之前,我一直在使用“标准”的Java IO类。问题与上面的代码无关,因为如果我注释掉对validateJar的调用,Files.delete(tmpJar)将被成功执行并删除文件。所以,问题与下面的代码有关,但我找不到具体位置... Job是一个内部类,基本上是一个简单的POJO。 "jobAnnotation"是一个自定义注解,用于识别作业。我已经缩短了代码,但保留了基本部分。
private List<Job> validateJar(final String jarPath) throws IOException {
   List<Job> jobs = new ArrayList<Job>();

   try (JarFile jarFile = new JarFile(jarPath)) {
      URL[] jars = { new URL("file:" + jarPath) };

      ClassLoader jobClassLoader = URLClassLoader.newInstance(jars, this.getClass().getClassLoader());

      Enumeration<JarEntry> jarEntries = jarFile.entries();
      while (jarEntries.hasMoreElements()) {
         JarEntry jarEntry = jarEntries.nextElement();
         String className = jarEntry.getName();
         Class<?> classToLoad;
         try {
            classToLoad = Class.forName(className, true, jobClassLoader);
         } catch (Exception e1) {
            LOGGER.error(e1.getMessage(), e1);
            continue;
         }

         if (classToLoad.isAnnotationPresent(jobAnnotation)) {
            String vlr = null;
            try {
               Class<?> jobClass = (Class<?>) Class.forName(classToLoad.getCanonicalName(), true, jobClassLoader);
                Annotation annotation = jobClass.getAnnotation(jobAnnotation);
                Method method = annotation.getClass().getMethod("getValue");
                vlr = ((String) method.invoke(annotation, new Object[0]));
            } catch (Exception e1) {
               LOGGER.error(e1.getMessage(), e1);
            }

            Job job = new Job();
            job.setEnabled(true);
            job.setJarfile(jarPath);
            job.setClassName(classToLoad.getName());

            Parameter parameter = new Parameter();
            parameter.setRequired(true);
            parameter.setName("name");
            parameter.setValue(vlr);

            job.addParameter(parameter);
            jobs.add(job);
         }
      }
   } catch (IOException e) {
      throw e;
   }
   return jobs;
}

在使用try-with-resources之前,我使用普通的try-catch-finally来关闭JarFile,因为这是唯一有显式close方法的东西。可能是类加载正在保持文件打开,但我不知道如何关闭它。

我进行了一些搜索,发现无法卸载类(在Java中卸载类?)。

所以,问题是如何释放它?或者如何删除文件?

顺便说一下,我正在使用Java 1.7.0_71、JBoss 7.1.1和Windows 7(64位)。


1
URLClassLoader类有一个close()方法,你可以尝试使用它吗?close()方法将关闭使用URLClassLoader打开的任何Jar文件。 - codelion
如果关闭ClassLoader无效,您可以尝试使用字节码解释器,这样您就可以像打开常规文件一样打开.class文件,并完全控制何时关闭文件。BECL和其他工具可用于此。 - markspace
@codelion 把你的评论作为答案,这样我就可以关闭我的问题并接受你的答案了。它起作用了。我的错误在于我使用接口来创建类加载器--ClassLoader jobClassLoader = URLClassLoader.newInstance(jars, this.getClass().getClassLoader());--而它没有close()方法。所以我改成了--URLClassLoader jobClassLoader = URLClassLoader.newInstance(jars, this.getClass().getClassLoader());--然后我调用了close(),它就起作用了... - Bob Rivers
@BobRivers 啊,知道它起作用了真好! - codelion
3个回答

1
URLClassLoader类已经有一个close()方法。 close()方法将关闭使用URLClassLoader打开的任何Jar文件。这应该可以避免"文件已在使用中"异常。

0

文件正在被另一个进程使用。表示这可能不是你的错,可能只是另一个应用程序正在使用该文件。您可以查看this question以查找正在使用您的文件的进程。


如果你看他的代码,他正在打开jar文件并使用URLClassLoader进行加载,因此没有其他应用程序在使用该文件。 - codelion

0

一些病毒扫描软件在检查JAR文件时需要很长时间。尝试禁用病毒扫描程序。其他可能的原因是Windows索引进程或explorer.exe本身。如果您找不到文件锁定的原因,请在验证和删除之间设置延迟。也许您需要循环多次尝试。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接