
1. 简介
Oracle JDK在发布的SDK包中,除了核心类库、编译、运行和调试工具,还带了大量的监视查看工具。这些监视工具可以辅助开发者查看Java程序的运行状态,包括环境配置、内存、CPU、线程、堆栈、类加载等各种信息。本文将对这些监视工具进行介绍。
本文的工具介绍将主要参考JDK 8的官方文档,并以jdk 8为运行环境演示命令输出。
一分耕耘,一分收获
在使用jinfo查看进程所运行的环境变量信息时,报can’t determine target’s VM version的错误。
$ jinfo -sysprops 15444
Attaching to process ID 15444, please wait...
Error attaching to process: java.lang.RuntimeException: can't determine target's VM version : field "_reserve_for_allocation_prefetch" not found in type Abstract_VM_Version
sun.jvm.hotspot.debugger.DebuggerException: java.lang.RuntimeException: can't determine target's VM version : field "_reserve_for_allocation_prefetch" not found in type Abstract_VM_Version
at sun.jvm.hotspot.HotSpotAgent.setupVM(HotSpotAgent.java:435)
at sun.jvm.hotspot.HotSpotAgent.go(HotSpotAgent.java:305)
at sun.jvm.hotspot.HotSpotAgent.attach(HotSpotAgent.java:140)
at sun.jvm.hotspot.tools.Tool.start(Tool.java:185)
at sun.jvm.hotspot.tools.Tool.execute(Tool.java:118)
at sun.jvm.hotspot.tools.JInfo.main(JInfo.java:138)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.tools.jinfo.JInfo.runTool(JInfo.java:108)
at sun.tools.jinfo.JInfo.main(JInfo.java:76)
Caused by: java.lang.RuntimeException: can't determine target's VM version : field "_reserve_for_allocation_prefetch" not found in type Abstract_VM_Version
at sun.jvm.hotspot.runtime.VM.<init>(VM.java:291)
at sun.jvm.hotspot.runtime.VM.initialize(VM.java:370)
at sun.jvm.hotspot.HotSpotAgent.setupVM(HotSpotAgent.java:431)
... 11 more
Path=...;G:\local\java-1.8.0-openjdk-1.8.0.151\bin;...
在path中定义的java路径为open jdk 8。
将oracle jdk 9的路径指定到path中后,问题得到解决。
set JDK_HOME=G:\local\java\jdk-9.0.1
set PATH=%JDK_HOME%\bin;%PATH%
上面看到的问题应该是open jdk 8和oracle jdk 9之间的匹配问题。Spring Boot Application在启动时报如下错误,
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v1.5.8.RELEASE)
...
2018-08-02 10:46:59.333 WARN 12168 --- [ main] ationConfigEmbeddedWebApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.pphh.oauth.sample.App]; nested exception is java.lang.IllegalStateException: Unable to read meta-data for class com.pphh.oauth.config.FilterAutoConfiguration
org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.pphh.oauth.sample.App]; nested exception is java.lang.IllegalStateException: Unable to read meta-data for class com.pphh.oauth.config.FilterAutoConfiguration
Caused by: java.lang.IllegalStateException: Unable to read meta-data for class com.pphh.oauth.config.FilterAutoConfiguration
at org.springframework.boot.autoconfigure.AutoConfigurationSorter$AutoConfigurationClass.getAnnotationMetadata(AutoConfigurationSorter.java:217) ~[spring-boot-autoconfigure-1.5.8.RELEASE.jar:1.5.8.RELEASE]
... 14 common frames omitted
Caused by: java.io.FileNotFoundException: class path resource [ com/pphh/oauth/config/FilterAutoConfiguration.class] cannot be opened because it does not exist
at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:172) ~[spring-core-4.3.12.RELEASE.jar:4.3.12.RELEASE]
... 22 common frames omitted
Disconnected from the target VM, address: '127.0.0.1:64322', transport: 'socket'
Process finished with exit code 1
错误消息中告知无法找到com/pphh/oauth/config/FilterAutoConfiguration.class这个class。但是,这个依赖包肯定是加载进来了,这个类也一定有,但是为什么找不到?
先看异常抛出的地方,
org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:172)
执行的代码如下,
InputStream is;
if(this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
} else if(this.classLoader != null) {
is = this.classLoader.getResourceAsStream(this.path);
} else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if(is == null) {
throw new FileNotFoundException(this.getDescription() + " cannot be opened because it does not exist");
} else {
return is;
}
可以看到,原因就是无法加载类文件,这里设置一个断点,准备调试看下。
在调试模式中,进入上面的执行代码,发现确实is加载后为null。我觉得很奇怪,类明明在class加载路径上,怎么为null。于是我在调试框手动执行类加载,
ClassLoader.getSystemResourceAsStream("com/pphh/oauth/config/FilterAutoConfiguration.class");
上面的手动执行能够正确加载。
最后比较了下两次ClassLoader命令的文件路径,发现了问题,this.path所指向的class path resource路径前面有一个空格。
然后定位问题到相应的spring.factories配置文件中,
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pphh.oauth.config.ClientAutoConfiguration, \
com.pphh.oauth.config.FilterAutoConfiguration
将其更新为,
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.pphh.oauth.config.ClientAutoConfiguration,\
com.pphh.oauth.config.FilterAutoConfiguration
问题得到解决,此记以参考。
很多时候,代码没有问题,却由于配置出错导致应用启动失败,这种问题非常多,防不胜防,也是比较头疼的问题。多加细心,同时对于配置问题的容错报错,也有助于问题的解决。
就以上面的例子,spring boot在加载auto configuration类之前,若能够自动去掉类名的前后空格,就不会导致上述问题。毕竟,在编写spring.factories的时候,很难确保不人为的添加空格。