3

My post got a little too long, sorry. Here is a summary:

  • File on disk cannot be deleted ("the JVM holds the file" error). both when deleting from the java code and when trying to manually delete the file from windows.
  • All streams to that file are closed and set to null. All file objects set to null.
  • The program does nothing at that point; but waiting 30 minutes allows me to deleted the file from windows. Weird. Is the file not used by java anymore? Plus, since nothing happens in the program, it indicates it cannot be some stream I forgot (plus, I triple checked nothing is open).
  • Invoking System.gc() seemed to work when files were small. Did not help when they got to about 20MB.

[EDIT2] - I tried writing some basic code to explain, but its tricky. I am sorry, I know it's difficult to answer like that. I can however write how I open and close streams, of course:

BufferedWriter bw = new BufferedWriter(new FileWriter(new File("C:\\folder\\myFile.txt")));
for(int i = 0; i < 10; i++)
{
    bw.write("line " + i);
    bw.newLine();
}
bw.close();
bw = null;

If I've used a file object:

File f = new File("C:\\folder\\myFile.txt");
// use it...
f = null;

Basic code, I know. But this is essentially what I do. I know for a fact I've closed all streams in this exact way. I know for a fact that nothing happens in the program in that 30-minutes interval in which I cannot delete the file, until I somehow magically can.

thank you for your input even without the coherent code. I appreciate that.


Sorry for not providing any specific code here, since I can't pinpoint the problem (not exactly specific-code related). In any case, here is the thing:

I have written a program which reads, writes and modifies files on disk. For several reasons, the handling of the read/write is done in a different thread, which is constantly operating.

At some point, I am terminating the "read/write" thread, keeping only the main thread - it waits for input from a socket, totally unrelated to the file, and does nothing. Then, I try to delete the file (using either File.delete(), even tried nio.Files delete option).

The thing is - and it's very weird - sometimes it works, sometimes it doesn't. Even manually, going to the folder and trying to delete the file via windows, gives me the "The file is open by the JVM" message.

Now, I am well aware that keeping references from all kinds of streams to the file prevents me from deleting it. Well past that by now :) I have made sure that all streams are closed. I even set their values to null, including any "File" objects I have used (even though it shouldn't make any difference). All set to null, all closed. And the thread which generates all of them - the "read/write" thread - well, it's terminated since it got the the end of its run() method.

Usually, if I wait about 30 minutes, while the JVM still operates, I can delete the file manually from windows. The error magically disappears. When the JVM is closed, I can always delete the file right away.

I am lost here. Tried specifically invoking System.gc() before trying to delete the file, even called it like 10 times (not that it should matter). Sometimes it helped, but on other occasions, for example, when the file got larger (say 20MB), that didn't help.

What am I missing here? Obviously, this couldn't be my implicit fault (not closing some stream), since the read/write thread is dead, the main thread awaits something unrelated (so the program is at a "standstill"), I have explicitly closed all streams, even nullified the references (inStream = null), invoked the garbage collector.

What am I missing? Why is the file "deletable" after 30 minutes (nothing happens at that time - not something in my code). Am I missing some gentle reference/garbage collection thingy?

Clashsoft
  • 11,553
  • 5
  • 40
  • 79
amirkr
  • 173
  • 3
  • 15
  • Well try to come up with an example code, it will definitely help you understand the problem better. – xiaofeng.li May 16 '16 at 04:08
  • Okay I'll try, it will be pretty much what I described. – amirkr May 16 '16 at 04:09
  • All streams to that file are closed and set to null. Can you show you open and close stream code? – andy May 16 '16 at 04:09
  • My guess is that some of the streams are not closed. – xiaofeng.li May 16 '16 at 04:10
  • 1
    What is likely happening here is that the file has not been closed properly. You will not be able to delete the file. After a period of time the garbage collector collects the file and as part of the finalization process closes it. You can delete the file now. There is possibly something wrong with the code in the thread that reads and writes the file, maybe when you are terminating the thread. We can't possibly help you with that problem unless you post some code. – clstrfsck May 16 '16 at 04:11
  • sometimes what we say, is not implemented properly in code, we know streams should be closed, but are we doing it properly no one knows, may be you can post the code how you read/write file and how threads are terminated. May be that will provide more info – Deepak Bhatia May 16 '16 at 04:13
  • Thanks all. It's hard to see how the streams are not closed, as I have explicitly closed them all and no exception was thrown at any point. Also, if they are not properly closed, why after 30 minutes I can delete the file? Shouldn't it be "banned" forever and ever? :) – amirkr May 16 '16 at 04:24
  • @msandiford I understand the code omission problem, it's just a gigantic code with dozens of streams (all closed, triple checked that!). – amirkr May 16 '16 at 04:25
  • It would be helpful to post at least a compilable example. You can't `write()` to a `BufferedReader`. – Jim Garrison May 16 '16 at 04:30
  • @JimGarrison ugh, sorry, it's a BufferedWriter of course. – amirkr May 16 '16 at 04:38
  • You have quoted two alleged error messages: 'the JVM holds the file' and 'the file is open by the JVM'. Which is it? Is either of them the real text? It doesn't seem right. – user207421 May 16 '16 at 05:20
  • 3
    When posting a question, NEVER paraphrase or write from memory any output, especially error messages. ALWAYS copy/paste and format as code. – Jim Garrison May 16 '16 at 05:54
  • How do you know your attempt to delete the file is correctly interlocked with the code that writes the file? This smells like some thread which holds the file open, and which you _believe_ has completed, is still executing and the file really IS open. – Jim Garrison May 16 '16 at 05:56
  • @JimGarrison - used JVM monitor to track threads. The read/write thread has terminated (naturally, got to the end of its run() method) completely. Even though I explicitly released the resources by calling close() for each and every one of them, without any exceptions thrown, the termination of the thread should have provided a second layer of protection against such problems, since the only thread having references to the resources, was the read/write thread. So its odd. – amirkr May 16 '16 at 06:07
  • @EJP - "The action cannot be completed because the file is open in Java(TM) Platform SE binary" – amirkr May 16 '16 at 06:08
  • The termination of a thread does not imply that the objects are immediately garbage collected. Even worse, garbage collection does not imply finalization, so even triggering a collection via `System.gc()` (which is only a hint anyway) does not imply running the finalizers of collected objects. – Holger Jun 07 '16 at 11:09

2 Answers2

2

What you're doing just calls for problems. You say that "if an IOexception occurred, it is printed immediately" and it may be true, but given that something inexplicable happens, let's better doubt it.

I'd first ensure that everything gets always closed, and then I'd care about related logic (logging, exiting, ...).

Anyway, what you did is not how resources should be managed. The answer above is not exactly correct either. Anyway, try-with-resources is (besides @lombok.Cleanup) about the only way, clearly showing that nothing gets ever left open. Anything else is more complicated and more error-prone. I'd strongly recommend using it everywhere. This may be quite some work, but it also forces you to re-inspect all the critical code pieces.

Things like nullifying references and calling the GC should not help... and if they seem to do, it may be a chance.

Some ideas:

  • Are you using memory mapped files?
  • Are you sure System.exit is not disabled by a security manager?
  • Are you running an antivirus? They love to scan files just after they get written.

Btw., locking files is one reason why the WOW never started for me. Sometimes the locks persisted long after the culprit was gone, at least according to tools I could use.

Community
  • 1
  • 1
maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • Thank you for your comment. Not using memory mapped files, System.exit is not disabled and no antivirus (those were indeed good reasons though). Still trying to fix the issue, hope to post when it is resolved. – amirkr May 17 '16 at 05:53
0

Are you closing your streams in a try...finally or try(A a = new A()) block? If not the streams may not be closed. I would strongly recommend using either Automatic Resource Block Management ( try(A a = new A()) ) or a try...finally block for all external resources.

try(BufferedWriter br = new BufferedWriter(new FileWriter(new File("C:\\folder\\myFile.txt")));
for(int i = 0; i < 10; i++)
{
  br.write("line " + i);
  br.newLine();
})
David Waters
  • 11,979
  • 7
  • 41
  • 76
  • Thanks for your answer. the thread is terminated regularly - it's run() method just reaches its end. It operates with a while(condition) loop and the condition is set to false (also using wait-notify so it doesn't busy-wait but that's another matter). I am not using finally. But no exception was thrown. Could this be a cause for concern? – amirkr May 16 '16 at 04:22
  • Yes, when ever you use an external resource you want to be using `try...finally` or `try(ExampleThing resource = new ExampleThing){...}` to ensure you are really releasing these resources. – David Waters May 16 '16 at 04:24
  • This "answer" is a request for clarification and should be a comment. – Jim Garrison May 16 '16 at 04:25
  • @JimGarrison phrased more as an answer. – David Waters May 16 '16 at 04:29
  • Thanks again. I will try adding these finally statements. But just to clarify (if you can :)), is there possibly a way in which the resource was not properly closed even though *no exception* was thrown and the program indicated it closed successfully? – amirkr May 16 '16 at 04:36
  • @amirkr: Do you know *for sure* that no exception was thrown? Is it possible that an exception was thrown, but later caught/swallowed/handled/etc. in such a way that made it no longer obvious? – ruakh May 16 '16 at 04:50
  • @ruakh - fair question, but I am absolutely sure. The close() command is always inside a try-catch clause (catching IO exceptions), and if an IOexception occurred, it is printed immediately, followed by System.exit(1). Also, if any other exception, which was not an IOException occurred, it is propagated "upwards" until caught by the run() method of the read/write object, where it is printed and again invoking System.exit(1). – amirkr May 16 '16 at 04:53
  • 1
    Have you registered a Thread.UncaughtExceptionHandler? If not it is possible for an uncaught exception within a thread to terminate the thread without the main program noticing. Additionally it is possible a programming error causes the thread to exit without successfully closing all resorces. – David Waters May 16 '16 at 04:55