有没有办法计算 IO 活动(文件访问、读取字节数)?



我希望能够找出正在运行的JVM访问磁盘的频率,如果可能的话,它访问了什么。
例如,我想知道: 通过 ImageIO 加载图像是否真的将其加载到内存中,或者它只是创建一个指向文件的指针并在真正绘制到屏幕上时访问它? 还有其他类似的事情。 有没有办法做到这一点?

更新,我添加了这个:

public class SecurityManagerExtension extends SecurityManager {

@Override
public void checkRead(FileDescriptor fd)
{
logger.error("reading file1: {}", fd.toString());
super.checkRead(fd);
}
@Override
public void checkRead(String file)
{
logger.error("reading file2: {}", file);
super.checkRead(file);
}
@Override
public void checkRead(String file, Object context)
{
logger.error("reading file3: {} in context {}", file, context.toString());
super.checkRead(file, context);
}
}

我已经设置了安全管理器:

SecurityManagerExtension secMan = new SecurityManagerExtension();
java.lang.System.setSecurityManager(secMan);

现在,新的问题是: 基本上,除了类文件之外,我加载或加载的每个文件似乎都会导致如下所示的 StackTrace:

原因:java.security.AccessControlException: access den("java.io.FilePermission" "C:\Program Files\Java\jdk-13.0.1\bin\awt.dll" "read"(

如果我注释掉 setSecurityManager(( 行,一切将再次正常运行。 我甚至试图让我的用户完全访问JDK目录,所以这不可能是一个真正的权限问题。事实并非如此,因为如果我回到默认值,一切将再次正常运行。

我做错了什么?

对于 Java 应用程序的文件系统访问,本机跟踪工具始终是首选。在 Windows 上,使用进程监视器跟踪 I/O。在 Linux 上,使用 strace。其他平台提供类似的设施。

通过直接在 Java 中跟踪文件系统访问,您可以解决环境限制。例如,strace 在缺少CAP_SYS_PTRACE功能的容器中不可用,并且容器主机并不总是可访问的。

要走 Java 路线,您可以通过扩展java.lang.SecurityManager来实现自己的安全管理器。此类提供checkReadcheckWritecheckDelete方法,一旦代码尝试相应的访问,就会调用这些方法。

示例实现:

public class TraceSecurityManager extends SecurityManager {
public void checkRead(String file) {
System.out.println("Read: " + file);
}
public void checkRead(String file, Object context) {
System.out.println("Read: " + file);
}
public void checkWrite(String file) {
System.out.println("Write: " + file);
}
public void checkDelete(String file) {
System.out.println("Delete: " + file);
}
}

为了测试示例,我们使用 Java 编译器作为测试对象。要启用跟踪安全管理器,我们设置适当的系统属性并使用有效的 Java 源文件执行命令Test.java

$ java -Djava.security.manager=TraceSecurityManager com.sun.tools.javac.Main Test.java
Read: /home/user/com/sun/tools/javac/resources/spi/compilerProvider.class
Read: /home/user/com/sun/tools/javac/resources/compiler_en.properties
Read: /home/user/com/sun/tools/javac/resources/compiler_en_US.properties
Exception in thread "main" java.security.AccessControlException: access denied ("java.lang.RuntimePermission" "getenv.JDK_JAVAC_OPTIONS")
at java.base/java.security.AccessControlContext.checkPermission(AccessControlContext.java:472)
at java.base/java.security.AccessController.checkPermission(AccessController.java:897)
at java.base/java.lang.SecurityManager.checkPermission(SecurityManager.java:322)
at java.base/java.lang.System.getenv(System.java:999)
at jdk.compiler/com.sun.tools.javac.main.CommandLine.appendParsedEnvVariables(CommandLine.java:252)
at jdk.compiler/com.sun.tools.javac.main.CommandLine.parse(CommandLine.java:99)
at jdk.compiler/com.sun.tools.javac.main.CommandLine.parse(CommandLine.java:123)
at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:215)
at jdk.compiler/com.sun.tools.javac.main.Main.compile(Main.java:170)
at jdk.compiler/com.sun.tools.javac.Main.compile(Main.java:57)
at jdk.compiler/com.sun.tools.javac.Main.main(Main.java:43)

跟踪实现有效。我们甚至可以看到类加载尝试。但是,javac 失败,因为缺少权限。原因是,通过系统属性安装安全管理器时,缺省 Java 安全策略处于活动状态,并且不授予所需的许可权。若要解决此问题,可以提供最小的自定义策略,也可以使用空实现重写checkPermission方法。在这种情况下,我选择了最小策略:

grant {
permission java.security.AllPermission "", "";
};

制定政策后,我们可以重新测试:

$ java -Djava.security.policy=test.policy -Djava.security.manager=TraceSecurityManager com.sun.tools.javac.Main Test.java
Read: /home/user/com/sun/tools/javac/resources/spi/compilerProvider.class
Read: /home/user/com/sun/tools/javac/resources/compiler_en.properties
Read: /home/user/com/sun/tools/javac/resources/compiler_en_US.properties
Read: Test.java
Read: Test.java
Read: /usr/lib/jvm/java-11-openjdk-11.0.10.0.9-1.el7_9.x86_64/lib/modules
Read: /usr/lib/jvm/java-11-openjdk-11.0.10.0.9-1.el7_9.x86_64/lib/modules
Read: /usr/lib/jvm/java-11-openjdk-11.0.10.0.9-1.el7_9.x86_64/lib/jfxrt.jar
Read: /home/user/META-INF/services/java.nio.file.spi.FileSystemProvider
Read: /usr/lib/jvm/java-11-openjdk-11.0.10.0.9-1.el7_9.x86_64/lib/modules
Read: /usr/lib/jvm/java-11-openjdk-11.0.10.0.9-1.el7_9.x86_64/lib/modules
Read: Test.java
Read: /home/user/Test.java
Read: ./.bash_logout
Read: /home/user/.bash_logout
Read: ./.bash_profile
Read: /home/user/.bash_profile
Read: ./.bashrc
Read: /home/user/.bashrc
[...]
Read: /home/user/com/sun/tools/javac/resources/spi/ctProvider.class
Read: /home/user/com/sun/tools/javac/resources/ct_en.properties
Read: /home/user/com/sun/tools/javac/resources/ct_en_US.properties
Read: /home/user/TraceSecurityManager.class
Read: /home/user/Test.class
Read: /home/user
Write: /home/user/Test.class
Read: Test.java

这一次,我们得到了javac的完整文件系统访问跟踪。也可以在运行时启用安全管理器,这在由于任何原因无法控制 Java 命令行时非常有用:

System.setSecurityManager(new TraceSecurityManager());

在这种特殊情况下,自定义策略不是必需的,因为默认策略未处于活动状态。

使用安全管理器来跟踪文件系统访问当然不是最佳选择,因为缺少可能与调试方案相关的详细信息,但如果您没有替代方案并且需要完成工作,这是一个很好的折衷方案。

最新更新