Use finalize () in my case?

I have an ImageWrapper class that saves images to temporary files on disk to free up heap memory and allows them to be reloaded if necessary.

 class ImageWrapper { File tempFile; public ImageWrapper(BufferedImage img) { // save image to tempFile and gc() } public BufferedImage getImage() { // read the image from tempFile and return it. } public void delete() { // delete image from disk. } } 

I am worried about making sure that the files are deleted when such an instance of ImageWrapper collects garbage (otherwise I risk filling the disk with unnecessary images). This needs to be done while the application is still running (unlike cleaning suggestions at the time of completion) due to the fact that it can run for a long time.

I'm not completely familiar with the Java Java concept, and I was wondering if finalize() is what I'm looking for. My idea was to call delete () (in a separate thread, if that matters) from the overriden finalize() method. Is this the right way to do this?

UPDATE:

I do not think that I can close() an object proposed by many users, because each such image is selected in the list of listeners that I do not control, and could save a link to the object. the only time I'm sure I can delete a file when there are no links, so I thought finalize() was the right way. Any suggestions?

UPDATE 2:

What are the scenarios in which finalize() will not be called? If the only way to exit the program is (expected / unexpected), I can accept this because it means that I risk that only one unnecessary temporary file will remain un deleted (the one that was processed during the exit).

+4
source share
6 answers

A good alternative to finalize is PhantomReference . best way to use it:

 public class FileReference extends PhantomReference<CachedImage> { private final File _file; public FileReference(CachedImage img, ReferenceQueue<CachedImage> q, File f) { super(img, q); _file = f; } public File getFile() { _file; } } 

Then use it like:

 public class CachedImage { private static final ReferenceQueue<CachedImage> refQue = new ReferenceQueue<CachedImage>(); static { Thread t = new Thread() { @Override public void run() { try { while (true) { FileReference ref = (FileReference)refQue.remove(); File f = ref.getFile(); f.delete(); } } catch (Throwable t) { _log.error(t); } } }; t.setDaemon(true); t.start(); } private final FileReference _ref; public CachedImage(BufferedImage bi, File tempFile) { tempFile.deleteOnExit(); saveAndFree(bi, tempFile); _ref = new FileReference<CachedImage>(this, refQue, tempFile); } ... } 
+3
source

Another approach is to use File.deleteOnExit () , which marks the file for the JVM to be deleted on exit. I understand that this is not exactly what you are looking for, but may be of interest.

To be clear, if your JVM dies unexpectedly, it will not clear these files. Thus, you might want to architect your solution to clear cache files at startup so that you don't build up a lot of unused cache files over time.

+6
source

It is not recommended to use finalize() . The problem is that you cannot count on the garbage collector to delete the object. Thus, any code that you put in your class with an overridden finalize() method is not guaranteed.

+3
source

There is no guarantee that your finalize method will be called; in particular, any objects that hang upon exiting the program are usually simply deleted without cleaning. Closeable is a much better option.

+2
source

As an alternative to @Brian Agnew's answer, why not set up a ShutdownHook that clears the cache directory?

 public class CleanCacheOnShutdown extends Thread { @Override public void run() { ... } } System.getRuntime().addShutdownHook(new CleanCacheOnShutdown()); 
0
source

I ended up using a combination of File.deleteOnExit() (thanks @Brian) and ScheduledExecutorService , which translates the ReferenceQueue from PhantomReference into my class instances, according to this post . I am adding this answer because no one suggested using a ReferenceQueue (which I think is the perfect solution for my problem) and I think it will be useful for future readers.

The result (somewhat simplified) is this (the class name has changed to CachedImage ):

 public class CachedImage { private static Map<PhantomReference<CachedImage>, File> refMap = new HashMap<PhantomReference<CachedImage >, File>(); private static ReferenceQueue<CachedImage> refQue = new ReferenceQueue<CachedImage>(); static { Executors.newScheduledThreadPool(1).scheduleWithFixedDelay(new Thread() { @Override public void run() { try { Reference<? extends CachedImage> phanRef = refQue.poll(); while (phanRef != null) { File f = refMap.get(phanRef); f.delete(); phanRef = refQue.poll(); } } catch (Throwable t) { _log.error(t); } } }, 1, 1, TimeUnit.MINUTES); } public CachedImage(BufferedImage bi, File tempFile) { tempFile.deleteOnExit(); saveAndFree(bi, tempFile); PhantomReference<CachedImage> pref = new PhantomReference<CachedImage>(this, refQue); refMap.put(pref, tempFile); } ... } 
0
source

Source: https://habr.com/ru/post/1495143/


All Articles