Details

    • Type: Bug
    • Status: Closed
    • Priority: Major
    • Resolution: Fixed
    • Affects Version/s: 8.4.4
    • Fix Version/s: 9.10-rc-1
    • Component/s: Attachments
    • Environment:
      XWiki 8.4.4 in a clustered environment
    • Difficulty:
      Unknown
    • Documentation:
      N/A
    • Documentation in Release Notes:
      N/A
    • Similar issues:

      Description

      XWiki Platform uses java.io.File.deleteOnExit() method in several locations in the codebase in order to clean up temporary files once the JVM is shutting down. Internally this method calls the static DeleteOnExitHook.add() which maintains a LinkedHashSet containing Strings of all of the temporary file locations that have been created since the application started. We have noticed that our servers have been running out of Heap space because this Set is being filled with hundreds of thousands of Strings that can take 20% and even up to 40% of the Heap space that can never be cleaned out by Garbage Collection.

      We are seeing that temporary files are being cleaned up as the server is running. The temp folder would get to about 3500 files and then a process would clean the folder out. I have not been able to determine if XWIki or another process is cleaning this folder. However, once the file is deleted, the DeleteOnHook HashSet is not purged of those entries. Sun/Oracle has had an open Bug for this since 2008 and it seems that they really do not want to change the functionality as this functionality was not intended for servers that are rarely restarted.

      Ideally XWiki should use a different API or create an API that will still clean out the temp files on shutdown, clean up temporary files periodically to not eat all of the HD space, and clean up any object that maintains references to these temp files once they are deleted.

      Example

      • Create a page with the following content:
         
        {{groovy}} 
        import org.xwiki.environment.Environment; 
        import org.apache.commons.fileupload.disk.DiskFileItem; 
        import org.xwiki.store.UnexpectedException; 
        import com.xpn.xwiki.web.Utils; 
        Environment env = Utils.getComponent(Environment.class); 
        while(true) { 
          File dir = new File(env.getTemporaryDirectory(), "attachment-cache"); 
          try { 
            if (!dir.mkdirs() && !dir.exists()) 
            { 
               throw new UnexpectedException("Failed to create directory for attachments " + dir); 
            } 
            DiskFileItem dfi = new DiskFileItem(null, null, false, null, 10000, dir);
            // This causes the temp file to be created. 
            dfi.getOutputStream().close(); 
             // Make sure this file is marked for deletion on VM exit because DiskFileItem does not. 
            dfi.getStoreLocation().deleteOnExit(); 
            // return dfi 
          } catch (IOException e) { 
            throw new UnexpectedException("Failed to create new attachment temporary file.", e); 
          } 
          sleep(50) } 
        {{/groovy}}
        
      • Save and view the page
      • Attach to the JMX using jvisualvm or other similar tool.
      • Watch the heap usage continue to rise.
      • View the attachment-cache folder and notice there are no files actually being saved
      • Get a heap dump of the process and view the contents of it using MAT or similar tool
      • View the contents of the DeleteOnExitHook object and see it contains thousands of strings

        Attachments

          Issue Links

            Activity

              People

              • Assignee:
                tmortagne Thomas Mortagne
                Reporter:
                jmiklos Jim Miklos
              • Votes:
                0 Vote for this issue
                Watchers:
                3 Start watching this issue

                Dates

                • Created:
                  Updated:
                  Resolved:
                  Date of First Response: