介绍
最近(201506),碰到一个关于在java 8_45下加载applet非常缓慢的问题。而java 1.7(如 1.7.0_51)没有这个问题。
本文章将介绍该问题调查过程与结论,包括此间涉及的相关细节。
基本上涉及以下方面的知识
1) JVM CPU profiling - JVisualVM
2) DNS
3) Java 8 关于Applet的更改
实际工作中的问题可能是比较复杂的。如本文中的问题,它不仅是简单的检查cpu使用,还涉及DNS解析、新版JDK引入的对既有行为的改变等。忽然想起一句话:没有人会像教科书上讲的那样生病。
有道云笔记链接: http://note.youdao.com/share/?id=1e426753c042c0b420be2ccd2259dad7&type=note
根本原因
1)Applet在加载第一个图片(toolkit.getImage)时,出现SocketPermission。此过程时间长度不定,有时很快(1秒内),有时10秒+.
此过程为crossdomain检查是,JDK(1.8.0_45)调用了DNS解析(参考下面章节中的调用栈)。此 解析被JDK8自身新引入的RIA permission变化所拦截,导致无法解析。异常信息 如:java.security.AccessControlException: access denied ("java.net.SocketPermission" "your_domain_name.com" "resolve") 。
2)在加载接下来的其他图片(还是toolkit.getImage)时,每个图片5秒钟时间,共30+个图片。作为客户,算是等的花儿已谢了。
是由于JDK调用了DNS反向解析(从ip 查询域名)- java.net.Inet64AddressImpl.getHostByAddr。此解析失败。
第N(N>=2)此加载图片,JDK的调用分支改变为调用DNS反向解析,而非1)中的正向解析,可能是记住了上次的permission检查失败的状态,此不同可以通过比较上面1)中AccessControlException的调用栈,和 本2)中的cpu profiling结果得到。
注意:虽然加载图片出现了异常或者timeout,图片最终还是成功加载。具体原因也将在后面所描述。
关于java 8引入的Permision,请参考
解决方案
暂时没有最终完美解决方案
1 将所有图片文件写入一个zip包,applet读取的时候缓存整个zip包
那么,加载很多图片文件不会引起多次5秒timeout。因为只有一次获取zip文件的过程。
或者
2 将域名加入本地hosts文件
或者
3 更改本地java.policy,加入socketpermission,允许resolve
接下来将详细解释如何发现的这个问题,其过程比较曲折。
步骤
1. 重现问题
本机安装1.8.0_45, 访问测试环境的applet。重现成功! 注意:测试环境的url也使用域名,而非ip。域名解析是使用公司内部的域名服务器。
同时,还通过Eclipse ,以调用application的形式试图重现,发现其在非sandbox环境下,没有问题!
2.检查java console log
感谢写图片加载的同事,每个图片加载前和加载成功分别打印了log。由此成功发现了直接原因 - 即加载每个图片都花费了5秒钟之间。
目前可以定位到具体代码行。
大概看了一下,只有两行实际调用的程序,如下:
print 'begin loading' URL URL = this.getClass().getResource(imageFileName); iconImg = Toolkit.getDefaultToolkit().getImage(URL); pring 'load done'
都是jdk内部类,使用方法也没看到什么不妥的地方,而且只有jdk8有问题,jdk7没问题,此中必有隐情(元芳,你怎么看).
3. 明确到底是上面哪行代码(getResource, OR getImage )出了问题, by JVisualVM Sampler
同时确定线程名称,对接下来的profiling JDK的时候,快速定位有帮助。否则jdk的profiling结果老长老长。
JVisualVM的操作步骤很简单
1) 网页打开applet 2) visualvm挂载applet 3) 在load图片之前,点击 Sampler -> CPU 4) 等图片load结束以后,点击 'Snapshot'保存当前profiling结果。Snapshot可以作为单独文件保存,使用jvisualvm随时查看。
备注:为什么使用Sampler,而非Profilger标签。因为Sampler简单实用。关于 Sampler标签与Profiler标签区别,可以参考http://stackoverflow.com/questions/12130107 /difference-between-sampling-and-profiling-in-jvisualvm
检查也很简单,上图
此图明确的告诉我们,getImage花费了146秒。它就是真凶,但为什么呢?由于再往里的调用均为JDK内部的实现,我们需要更改JVisualVM的设置,才能捕捉到。下一个步骤就做这个事情。
4. 检查JVM里面到底在做什么, by JVisualVM Sampler
JVisualVM的操作步骤与上面一样,除了在点击 ‘CPU' 进行profiling之前,设定Settings 为 只检查 java.*. javax.*, sun.* 等等(这是java 8 visualvm 默认的).
检查结果如下(好深的调用! 要尽量避免如此深的调用栈。)
看来是在进行DNS反向解析的时候,5秒钟TimeOut了(这个5秒是另外一个同事告诉我的,他调查过Java7的一个相关问题)。
备注:为啥调用URL.hashCode就扯出这么长的一段调用呢。URL应该设计成不可变类。我们写程序要注意,尽量多用。快速看了一下java.net.URL类,只有StreamHandler可以修改它,其他情况下,它是不可变的。继续上图
备注:其实在上面检查步骤的同时,我通过google查询了java 8的关于applet的release note,以及其他人是否碰到过加载applet缓慢的问题。
--java 8在applet permission方面,增加了一些限制(https://docs.oracle.com/javase/8/docs/technotes /guides/deploy/whatsnew_deployment.html),我粗粗看了一下,认为与此无关(我错了!)。想起一篇文章《Tomcat7连接数异常导致超时问题的排查》,文中作者碰到问题,应用了很多很厉害的troubleshooting过程,最后定位到根本原因。但是他最后也发现其实log中已经有有偶发的StackOverflowError。我猜测也是log太多,这行log没有得到及时发现。否则可能更快的找到问题。
--有人在加载文件(jar in jar)时,加载缓慢(http://stackoverflow.com/questions/28504943/java-sound- dramatically-slower-after-jvm-8-update)。我们的applet不是这个情况,也排除了。
此处,我想说明的是,要从内向外(profiler),和从外向内(java 8 release note)双向调查。不要拘泥于profiling。一头扎进profiling,可能会使你迷失方向。
("java.net.SocketPermission" "your_domain_name" "connect,accept,resolve")
)
1)在加载第一个图片(toolkit.getImage())时,JDK(1.8.0_45)调用意外的调用 了DNS解析(参考下面章节中的调用栈)。而此解析被JDK8自身新引入的RIA permission变化所拦截,导致无法解析。异常信息 如:java.security.AccessControlException: access denied ("java.net.SocketPermission" "your_domain_name.com" "resolve") 。 此过程时间长度不定,有时很快(1秒内),有时10秒+.
2)在加载接下来的其他图片时,JDK记住了上次的permission检 查失败(原因未明,但与sun.plugin2.applet.SecurityManager.checkConnectionHelper有关),而 改变了调用分支,最终调用了 DNS反向解析(从ip 查询域名)- java.net.Inet64AddressImpl.getHostByAddr。此解析失败,有5秒钟的time out时间。
每个图片5秒钟,共30+个图片,共花了150秒左右。作为客户,算是等的花儿已谢了。
[深入:为什么第一次解析以后不存下来,后面每次重新解析一次导致了每个5秒的延时?]
因为:第一次压根就没成功,以后任意一次也都没有成功!
不管怎样,临时解决方案出来了,虽然很Ugly。把域名加到客户本地的hosts文件。客户是不会同意的,但技术上是可行的(已验证)
相关推荐
首先,让我们深入了解一下Java 1.8.0_45包含的主要组件: 1. **Java编译器**(javac):Java源代码通过这个编译器被转化为字节码,这是Java程序运行的基础。在这一版本中,编译器支持了Java 8的新特性,如lambda...
JDK 1.8.0_251是Java 8的一个特定更新版本,它包含了Java编译器、Java运行时环境(JRE)、Java类库以及各种开发工具,如Java调试器(JDB)和Java文档生成器(Javadoc)。这个压缩包文件"jdk1.8.0_251.rar"就是JDK ...
JDK 1.8.0_45是JDK 8的一个更新版本,它包含了Java运行环境(JRE)、编译器(javac)、Java应用程序启动器(java.exe)以及其他用于开发和运行Java应用的工具。这个版本的发布主要是为了修复已知的安全漏洞和性能...
了解这些知识点,开发者可以有效地利用JDK1.8.0_45进行Java开发工作,同时享受到Java 8带来的各种新功能和性能优化。对于初学者而言,这是一个很好的起点,而对于经验丰富的开发者,这将是一个稳定可靠的开发环境。
Java JDK(Java Development Kit)是Java编程语言的软件开发工具包,它包含了编译、调试、性能优化等所需的所有工具和库。JDK1.8.0_31是Oracle公司发布的Java 8的一个更新版本,它在Java 8的基础上进行了一些功能...
《OpenJDK 1.8.0_181:深入理解开源Java开发工具包》 OpenJDK,全称为Open Java Development Kit,是Java开发工具包的一个开源实现,它为开发者提供了构建、运行Java应用程序所需的全部工具。本文将重点讨论OpenJDK...
Java 1.8.0_45是Java 8更新中的一个版本,发布于2015年4月。这个版本包含了一些重要的安全修复、性能优化和功能增强。Java 8是Java历史上的一个里程碑,引入了多个创新特性,极大地提高了开发效率和代码质量。 1. *...
"1.8.0_144"这一命名方式表明这是Java 8更新到第144次的小版本更新,通常会修复已知问题,提升性能,并增加一些新特性。 Java 8是Java历史上的一个重要里程碑,引入了诸多创新功能,如Lambda表达式、函数式接口、...
标题“java-1.8.0_222-openjdk-amd64.tgz”指出这是一个与Java开发工具包(JDK)相关的压缩文件,特别提到了版本是1.8.0_222,针对AMD64架构,即64位系统。此文件很可能是OpenJDK的开源实现,OpenJDK是Java SE(标准...
Java JDK,全称为Java Development Kit,是Oracle公司提供的用于开发和运行Java应用程序的软件开发工具包。本压缩包“jdk-1.8.0_251-windows-x64.zip”包含了适用于Windows 64位操作系统的JDK 1.8.0_251版本。这个...
JRE 1.8.0_45是Java 8的一个特定版本,64位版本则是为在64位操作系统上运行Java应用程序设计的。这个版本的发布解决了之前版本的一些已知问题,并且引入了一些新特性、性能改进和安全更新。 1. **Java版本号解析**...
nginx-1.8.0_linux Linux版0积分免费下载nginx-1.8.0_linux Linux版0积分免费下载nginx-1.8.0_linux Linux版0积分免费下载nginx-1.8.0_linux Linux版0积分免费下载nginx-1.8.0_linux Linux版0积分免费下载nginx-...
linux(x86_64)下的jdk压缩包,版本...export JAVA_HOME=/usr/local/java/jdk1.8.0_333 export JRE_HOME=${JAVA_HOME}/jre export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib export PATH=${JAVA_HOME}/bin:$PATH
JDK 1.8.0_45 JDK详细介绍 JDK(Java Development Kit) 是 Java 语言的软件开发工具包(SDK)。 SE(J2SE),standard edition,标准版,是我们通常用的一个版本,从JDK 5.0开始,改名为Java SE。 EE(J2EE),enterprise ...
Java 1.8.0_181 是Oracle公司发布的Java Development Kit (JDK) 的一个版本,主要用于运行和开发Java应用程序。这个版本是针对Java 8的一个更新,包含了Java运行环境(JRE)和Java开发工具集(JDK)。在Java 8中,有...
本文将深入探讨Java的环境变量,包括`JAVA_HOME`, `PATH`, 和 `CLASSPATH`,以及它们在JDK 1.8.0_241版本中的作用。 首先,`JAVA_HOME` 是一个系统环境变量,它指向Java开发工具集(JDK)的安装目录。在Windows系统...
2. **Java 1.8.0_131**:这是Java的一个具体版本,其中“1.8”代表Java SE 8,这是Java的一个主要版本,而“0_131”是该版本的小更新,通常修复了一些已知问题,增强了性能和安全性。Java 8引入了许多新特性,如...
在本篇中,我们将深入探讨JDK 1.8.0_112版本的关键特性、安装与配置方法,以及其在Java编程中的重要性。 1. **Java 8新特性** - **Lambda表达式**:Java 8引入了函数式编程的概念,允许开发者使用简洁的lambda...