FULL PRODUCT VERSION :
java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.16299.192]
A DESCRIPTION OF THE PROBLEM :
Given a directory with large number of files in it, the Files.walkFileTree method runs exceedingly slower when a SecurityManager is installed.
This is due the fact that the FileTreeWalker implementation drops the cached attributes received from the directory enumeration when a SecurityManager is installed. Looking at the code of FileTreeWalker.getAttributes: (JDK 8 & 9 both have the same source)
// if attributes are cached then use them if possible
if (canUseCached &&
(file instanceof BasicFileAttributesHolder) &&
(System.getSecurityManager() == null))
{
BasicFileAttributes cached = ((BasicFileAttributesHolder)file).get();
if (cached != null && (!followLinks || !cached.isSymbolicLink())) {
return cached;
}
}
The attribute query only reuses an attribute object when it is allowed to (by canUseCached), and there is no security manager. It is a faulty method, as instead of checking the presence of the security manager, it should check if the security manager allows the reading of the path. This will result in slower performance as the attributes needs to be queried again for every file instead of accessing the one coming from the directory enumeration.
The access check should be done in the following way:
// if attributes are cached then use them if possible
if (canUseCached &&
(file instanceof BasicFileAttributesHolder))
{
BasicFileAttributes cached = ((BasicFileAttributesHolder)file).get();
if (cached != null && (!followLinks || !cached.isSymbolicLink())) {
SecurityManager sm = System.getSecurityManager();
if(sm != null) {
// TODO check read access
}
return cached;
}
}
I've selected "Can not make any progress until this bug is resolved." for the severity of the problem as it is impossible to implement high performance I/O application with this bug present.
REGRESSION. Last worked in version 8u151
ADDITIONAL REGRESSION INFORMATION:
java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the following function on a directory which contains a lot of files. (>50 000)
Run the provided source.
Run the test twice and drop the first result to reduce the impact of any OS level file memory caching.
Output of the program on my computer (with HDD and 50 000 files in the directory):
Without SecurityManager: 690 ms
With SecurityManager: 4666 ms
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The Files.walkFileTree function should have the same performance, independent of the presence of a SecurityManager.
ACTUAL -
The Files.walkFileTree function runs exceedingly slower when a SecurityManager is present.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.security.Permission;
public class SlowTreeWalking {
private static void enumerateDirectory(Path directory) throws IOException {
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
});
}
public static void main(String[] args) throws IOException {
Path directory = Paths.get("...");
long nanos = System.nanoTime();
enumerateDirectory(directory);
long firstrun = System.nanoTime() - nanos;
//set dummy security manager
System.setSecurityManager(new SecurityManager() {
@Override
public void checkPermission(Permission perm) {
}
});
nanos = System.nanoTime();
enumerateDirectory(directory);
long secondrun = System.nanoTime() - nanos;
System.out.println("Without SecurityManager: " + firstrun / 1_000_000 + " ms");
System.out.println("With SecurityManager: " + secondrun / 1_000_000 + " ms");
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Removing the SecurityManager.
java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 10.0.16299.192]
A DESCRIPTION OF THE PROBLEM :
Given a directory with large number of files in it, the Files.walkFileTree method runs exceedingly slower when a SecurityManager is installed.
This is due the fact that the FileTreeWalker implementation drops the cached attributes received from the directory enumeration when a SecurityManager is installed. Looking at the code of FileTreeWalker.getAttributes: (JDK 8 & 9 both have the same source)
// if attributes are cached then use them if possible
if (canUseCached &&
(file instanceof BasicFileAttributesHolder) &&
(System.getSecurityManager() == null))
{
BasicFileAttributes cached = ((BasicFileAttributesHolder)file).get();
if (cached != null && (!followLinks || !cached.isSymbolicLink())) {
return cached;
}
}
The attribute query only reuses an attribute object when it is allowed to (by canUseCached), and there is no security manager. It is a faulty method, as instead of checking the presence of the security manager, it should check if the security manager allows the reading of the path. This will result in slower performance as the attributes needs to be queried again for every file instead of accessing the one coming from the directory enumeration.
The access check should be done in the following way:
// if attributes are cached then use them if possible
if (canUseCached &&
(file instanceof BasicFileAttributesHolder))
{
BasicFileAttributes cached = ((BasicFileAttributesHolder)file).get();
if (cached != null && (!followLinks || !cached.isSymbolicLink())) {
SecurityManager sm = System.getSecurityManager();
if(sm != null) {
// TODO check read access
}
return cached;
}
}
I've selected "Can not make any progress until this bug is resolved." for the severity of the problem as it is impossible to implement high performance I/O application with this bug present.
REGRESSION. Last worked in version 8u151
ADDITIONAL REGRESSION INFORMATION:
java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the following function on a directory which contains a lot of files. (>50 000)
Run the provided source.
Run the test twice and drop the first result to reduce the impact of any OS level file memory caching.
Output of the program on my computer (with HDD and 50 000 files in the directory):
Without SecurityManager: 690 ms
With SecurityManager: 4666 ms
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The Files.walkFileTree function should have the same performance, independent of the presence of a SecurityManager.
ACTUAL -
The Files.walkFileTree function runs exceedingly slower when a SecurityManager is present.
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
package test;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.security.Permission;
public class SlowTreeWalking {
private static void enumerateDirectory(Path directory) throws IOException {
Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
});
}
public static void main(String[] args) throws IOException {
Path directory = Paths.get("...");
long nanos = System.nanoTime();
enumerateDirectory(directory);
long firstrun = System.nanoTime() - nanos;
//set dummy security manager
System.setSecurityManager(new SecurityManager() {
@Override
public void checkPermission(Permission perm) {
}
});
nanos = System.nanoTime();
enumerateDirectory(directory);
long secondrun = System.nanoTime() - nanos;
System.out.println("Without SecurityManager: " + firstrun / 1_000_000 + " ms");
System.out.println("With SecurityManager: " + secondrun / 1_000_000 + " ms");
}
}
---------- END SOURCE ----------
CUSTOMER SUBMITTED WORKAROUND :
Removing the SecurityManager.
- relates to
-
JDK-8338411 Implement JEP 486: Permanently Disable the Security Manager
-
- Resolved
-