Bug 547631 - Standalone ASTParser does not close opened classpath ZIP files
Summary: Standalone ASTParser does not close opened classpath ZIP files
Status: NEW
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.12   Edit
Hardware: PC Windows 10
: P3 normal (vote)
Target Milestone: ---   Edit
Assignee: JDT-Core-Inbox CLA
QA Contact:
URL:
Whiteboard: stalebug
Keywords:
Depends on:
Blocks:
 
Reported: 2019-05-24 10:39 EDT by Eric Burn CLA
Modified: 2022-07-26 15:45 EDT (History)
5 users (show)

See Also:


Attachments
Test program showing the issue (1.26 KB, text/x-java)
2019-05-24 10:39 EDT, Eric Burn CLA
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Eric Burn CLA 2019-05-24 10:39:02 EDT
Created attachment 278743 [details]
Test program showing the issue

When using JDT's ASTParser standalone, opened ZipFile instances for resolving bindings from the classpath are never closed. This makes it impossible to touch the files on the classpath - until the ZipFile instances opened by JDT are destroyed by the garbage collector.

The attached test program shows the problem. "test.jar" is a dummy JAR file that is added to the classpath of the ASTParser. Then "ASTParser.createASTs" is used to parse a Java source file. After it returns, attempting to delete "test.jar" fails on Windows platforms with:

java.nio.file.FileSystemException: test.jar: The process cannot access the file because it is being used by another process.
	at sun.nio.fs.WindowsException.translateToIOException(WindowsException.java:86)
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:97)
	at sun.nio.fs.WindowsException.rethrowAsIOException(WindowsException.java:102)
	at sun.nio.fs.WindowsFileSystemProvider.implDelete(WindowsFileSystemProvider.java:269)
	at sun.nio.fs.AbstractFileSystemProvider.delete(AbstractFileSystemProvider.java:103)
	at java.nio.file.Files.delete(Files.java:1126)
	at com.example.jdtzipleak.JdtZipLeak.main(JdtZipLeak.java:39)

If "System.gc()" is un-commented, deleting the file succeeds. This is because the file handles are cleaned up automatically when they are destroyed.

The suspected cause is that a new "NameEnvironmentWithProgress" is created in CompilationUnitResolver.resolve (see https://github.com/eclipse/eclipse.jdt.core/blob/3d670fe48cd3ec2fe4d3f7a2fca5830a4057291b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/CompilationUnitResolver.java#L649).
However, `environment.cleanup()` is not called in the finally block. This means that the classpath is never cleaned up.

Adding `environment.cleanup()` in the existing finally block of `CompilationUnitResolver.resolve` also avoids the issue.
Comment 1 Stephan Herrmann CLA 2019-05-25 06:01:36 EDT
I imagine the current design is driven by needs for working with bindings (I saw you have parser.setResolveBindings(true)): when you query any of the bindings in the result for additional details, it may be necessary to perform additional lookup of types. This needs the name environment to exist as long as those bindings are in use.

The javadoc of setResolveBindings() mentions:
"...The additional space is not reclaimed until the AST, all its nodes, and all its bindings become garbage. So it is very important to not retain any of these objects longer than absolutely necessary...."

I could imagine addition of a method like
    AST#releaseNameEnvironment()

Not sure if this has been discussed before. Perhaps this is a bad idea, but with this we could create a protocol that enables clients to tell us, when internal context can be freed.

Technical note: AST seems to be the central location where we hold the BindingResolver. If it is a DefaultBindingResolver with a non-null #scope, then we will have a path towards the name environment (and the LookupEnvironment, too, while we're at it).

PS: obviously, the actual symptom is a windows-only issue.
Comment 2 Eric Burn CLA 2019-05-25 07:18:17 EDT
(In reply to Stephan Herrmann from comment #1)
> I imagine the current design is driven by needs for working with bindings (I
> saw you have parser.setResolveBindings(true)): when you query any of the
> bindings in the result for additional details, it may be necessary to
> perform additional lookup of types. This needs the name environment to exist
> as long as those bindings are in use.
At least for the batched API (ASTParser.createASTs) with FileASTRequestor I thought that this is only possible from inside FileASTRequestor, i.e. the objects are no longer usable after "createASTs" returns. For example, FileASTRequestor.createBindings only works from within the "createASTs" call.

However, given that there are also separate "ASTParser.createAST" methods that directly return an AST node with the bindings, I'm guessing that this is not the case.

> The javadoc of setResolveBindings() mentions:
> "...The additional space is not reclaimed until the AST, all its nodes, and
> all its bindings become garbage. So it is very important to not retain any
> of these objects longer than absolutely necessary...."
> 
> I could imagine addition of a method like
>     AST#releaseNameEnvironment()
> 
> Not sure if this has been discussed before. Perhaps this is a bad idea, but
> with this we could create a protocol that enables clients to tell us, when
> internal context can be freed.
I definitely think there should be some way to signal that processing has been completed, and that the remaining open system resources can be freed. 

> Technical note: AST seems to be the central location where we hold the
> BindingResolver. If it is a DefaultBindingResolver with a non-null #scope,
> then we will have a path towards the name environment (and the
> LookupEnvironment, too, while we're at it).
> 
> PS: obviously, the actual symptom is a windows-only issue.
Technically yes, but having many open files is also bad for long running daemon processes. And I don't think we can rely on having the garbage collector clean it up reliably.
Comment 3 Philippe Cadé CLA 2019-10-04 04:36:43 EDT
We are also affected by this bug when doing a SVN Team/Update and a JAR file was updated. We are using Subclipse.

Our workaround is now to click on the "Run Garbage Collector" icon, then perform a Team/Refresh/Clean up and Team/Update again. This then updates the JAR file correctly.
Comment 4 Eclipse Genie CLA 2022-07-26 15:45:35 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet.

If you have further information on the current state of the bug, please add it. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.