1 /* 2 * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 /* 25 * @test 26 * @bug 8030099 27 * @summary Memory usage of java process increases 28 after calling Win32ShellFolder:listFiles 29 multiple times on some directory with 30 large number of files/folders 31 * @modules java.desktop/sun.awt.shell 32 * @requires (os.family == "windows") 33 * @run main/timeout=1000 ShellFolderMemoryLeak 34 */ 35 import java.io.BufferedReader; 36 import java.io.File; 37 import java.io.FileNotFoundException; 38 import java.io.IOException; 39 import java.io.InputStream; 40 import java.io.InputStreamReader; 41 import java.util.logging.Level; 42 import java.util.logging.Logger; 43 import sun.awt.shell.ShellFolder; 44 45 public class ShellFolderMemoryLeak { 46 47 private final static String tempDir = System.getProperty("java.io.tmpdir"); 48 private static Process process; 49 public static void main(String[] args) throws Exception { 50 if (args.length == 0) { 51 boolean testResultParallel 52 = createChildProcessWithParallelCollector(); 53 String result = ""; 54 if (!testResultParallel) { 55 result = "Test failed with Parallel collector"; 56 } 57 boolean testResultDefault 58 = createChildProcessWithDefaultCollector(); 59 if (!testResultDefault && !testResultParallel) { 60 result += " and with default collector both."; 61 } else if (!testResultDefault) { 62 result = "Test failed with default collector"; 63 } 64 if (!"".equals(result)) { 65 throw new RuntimeException(result); 66 } 67 } else { 68 testListFile(args[args.length - 1]); 69 } 70 } 71 72 public static boolean createChildProcessWithDefaultCollector() 73 throws Exception { 74 String testDirectory = "TestDirectory1"; 75 testDirectory = tempDir + testDirectory +File.separator; 76 createTestData(testDirectory); 77 return runProcess("", testDirectory); 78 } 79 80 public static boolean createChildProcessWithParallelCollector() 81 throws Exception { 82 String testDirectory = "TestDirectory2"; 83 testDirectory = tempDir + testDirectory +File.separator; 84 createTestData(testDirectory); 85 return runProcess(" -XX:+UseParallelGC", testDirectory); 86 } 87 88 public static boolean runProcess(String arg1, String arg2) throws Exception { 89 String javaPath = System.getProperty("java.home"); 90 String classPathDir = System.getProperty("java.class.path"); 91 92 //creating java process which run same class with different Xmx value 93 String command = javaPath + File.separator + "bin" + File.separator 94 + "java -Xmx256M" + arg1 + " -cp " 95 + classPathDir 96 + " -XaddExports:java.desktop/sun.awt.shell=ALL-UNNAMED" 97 + " ShellFolderMemoryLeak " + arg2; 98 process = Runtime.getRuntime().exec(command); 99 BufferedReader input = null; 100 InputStream errorStream = null; 101 String line = null; 102 try { 103 int exitVal = process.waitFor(); 104 input = new BufferedReader(new InputStreamReader( 105 process.getInputStream())); 106 while ((line = input.readLine()) != null) { 107 } 108 errorStream = process.getErrorStream(); 109 if (checkExceptions(errorStream) || exitVal != 0) { 110 return false; 111 } 112 } catch (IllegalThreadStateException e) { 113 throw new RuntimeException(e); 114 } finally { 115 if (input != null) { 116 input.close(); 117 } 118 if (errorStream != null) { 119 errorStream.close(); 120 } 121 process.destroy(); 122 } 123 return true; 124 } 125 126 public static boolean checkExceptions(InputStream in) throws IOException { 127 String tempString; 128 int count = in.available(); 129 boolean exception = false; 130 while (count > 0) { 131 byte[] b = new byte[count]; 132 in.read(b); 133 tempString = new String(b); 134 if (!exception) { 135 exception = tempString.contains("RunTimeException"); 136 } 137 count = in.available(); 138 } 139 return exception; 140 } 141 142 private static void createTestData(String testDirectory) { 143 String folder = "folder_"; 144 File testFolder = new File(testDirectory); 145 if (testFolder.exists()) { 146 clearTestData(testDirectory); 147 } else { 148 if (testFolder.mkdir()) { 149 for (int inx = 0; inx < 100; inx++) { 150 new File(testFolder + File.separator + folder + inx).mkdir(); 151 } 152 } else { 153 throw new RuntimeException("Failed to create testDirectory"); 154 } 155 } 156 } 157 158 public static void deleteDirectory(File file) 159 throws IOException { 160 161 if (file.isDirectory()) { 162 if (file.list().length == 0) { 163 file.delete(); 164 } else { 165 String files[] = file.list(); 166 for (String temp : files) { 167 File fileDelete = new File(file, temp); 168 deleteDirectory(fileDelete); 169 } 170 if (file.list().length == 0) { 171 file.delete(); 172 } 173 } 174 } 175 } 176 177 private static void testListFile(String testDirectory) { 178 try { 179 int mb = 1024 * 1024; 180 ShellFolder folder = ShellFolder.getShellFolder( 181 new File(testDirectory)); 182 Runtime instance = Runtime.getRuntime(); 183 184 //Memory used before calling listFiles 185 long startmem = instance.totalMemory() - instance.freeMemory(); 186 long start = System.currentTimeMillis(); 187 long endmem = 0; 188 189 //Calling listFiles for 5 minutes with sleep of 10 ms. 190 while ((System.currentTimeMillis() - start) < 300000) { 191 try { 192 folder.listFiles(); 193 Thread.sleep(10); 194 endmem = instance.totalMemory() - instance.freeMemory(); 195 } catch (InterruptedException ex) { 196 Logger.getLogger(ShellFolderMemoryLeak.class.getName()) 197 .log(Level.SEVERE, "InterruptedException", ex); 198 } 199 } 200 201 //Total increase in memory after 5 minutes 202 long result = (endmem - startmem) / mb; 203 204 if (result > 100) { 205 clearTestData(testDirectory); 206 throw new RuntimeException("Test Failed"); 207 } 208 clearTestData(testDirectory); 209 } catch (FileNotFoundException ex) { 210 if(process != null && process.isAlive()) { 211 process.destroy(); 212 } 213 Logger.getLogger(ShellFolderMemoryLeak.class.getName()) 214 .log(Level.SEVERE, "File Not Found Exception", ex); 215 } 216 } 217 218 private static void clearTestData(String testDirectory) { 219 File testFolder = new File(testDirectory); 220 try { 221 deleteDirectory(testFolder); 222 } catch (IOException ex) { 223 Logger.getLogger(ShellFolderMemoryLeak.class.getName()) 224 .log(Level.SEVERE, "Unable to delete files", ex); 225 } 226 } 227 }