我有一项服务,要解析的域名来自不受信任的来源。最近,它因内存不足而崩溃。我缩小了可能的原因,并得出结论,它必须与最近的DNS请求流量有关。但是,该服务在解析域名后不存储任何内容,因此这似乎不太可能,但我尝试使用导致其解析域名的请求向我的服务发送垃圾邮件,以防万一。它确实死于此。然后,在得出代表我的代码没有存储内存之后,我将代码缩小到了这个范围:
import java.math.*;
import java.net.*;
class A {
static {
try {
for (BigInteger i = BigInteger.ZERO; i==i; i = i.add(BigInteger.ONE))
Inet4Address.getByName("a"+i+".dog");
} catch (Exception e) {throw new RuntimeException(e);}
}
}
我在/etc/dnsmasq.conf
中使用此行设置了dnsmasq,以使分辨率更快:
address=/dog/127.0.0.1
起初我跑这个时,它存活了几天,所以看起来这不是问题。但后来我用我用来启动服务的脚本来运行它,它启用了安全管理器,它崩溃了:
$ javac A.java && java -Xmx80m -Djava.security.manager A
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
Could not find the main class: A. Program will exit.
安全管理器使我的程序容易受到这种拒绝服务攻击。为什么?如何解决?
$ java -version
java version "1.6.0_27"
OpenJDK Runtime Environment (IcedTea6 1.12.6) (Gentoo build 1.6.0_27-b27)
OpenJDK 64-Bit Server VM (build 20.0-b12, mixed mode)
答案 0 :(得分:5)
“InetAddress类有一个缓存来存储成功和不成功的主机名解析。 默认情况下,安装安全管理器时,为了防止DNS欺骗攻击,正主机名解析的结果将永久缓存。未安装安全管理器时,默认行为是缓存有限(依赖于实现)时间段的条目。主机名解析失败的结果会在很短的时间内(10秒)缓存,以提高性能。“
source:InetAddress.java的{javadoc http://docs.oracle.com/javase/6/docs/api/java/net/InetAddress.html
我认为它正在缓存所有永久解析的主机名并最终导致outOfMemoryError。
您可以尝试设置Java安全属性来控制TTL(生存时间)并查看这是否有助于解决此问题
答案 1 :(得分:3)
您正在静态块中执行长时间运行的代码。
当JVM加载一个类时,它首先执行静态块。 我怀疑是由于您的代码(本质上是一个长时间运行的操作)而导致JVM无法加载该类并崩溃。
通常在静态块中执行简单的初始化步骤。 将代码移动到静态main方法,然后重试。看看它是否能改善运行时间?
答案 2 :(得分:2)
为什么不想进行基本的分析?有几个问题是可能的,因此使用适当的工具五分钟肯定会有所帮助并节省您的时间。
<强> 1。没有SecurityManager的示例
<强> 2。您使用SecurityManager的示例
正如预期的那样,启用SecurityManager时会更多地使用内存。我没有设置任何DNS基础设施,因此请求率不是那么高。
重要提示:
我建议您在使用您的设置对环境进行一些分析之前避免任何调整。我们可以花很多时间在这里尝试建议根本原因,但你会更快地找到答案。请尝试使用分析器,不要忘记分享您的结果。这是一个非常有趣的话题。
谢谢。
答案 3 :(得分:1)
我发现java.lang.OutOfMemoryError: Java heap space
经常会产生误导,可能意味着你的Perm Space耗尽而不是你的终身对象。
尝试使用:-XX:MaxPermSize=128M
安全管理器无疑会增加加载到Perm中的类的数量。
答案 4 :(得分:0)
崩溃是否可以与-Djava.compiler=NONE
重现?我想知道你是否遇到了JIT编译器错误。
另外,我很好奇在静态main
方法中运行for循环时失败是否相同。我想知道在加载类时是否在静态块中执行长循环for循环是一个因素。
答案 5 :(得分:-1)
我仍然认为问题与OpenJDK有关。
有2个JDK具有生产质量:Sun(现在是Oracle)和IBM之一。
Gentoo是很好的Linux,但我认为你无法从GNU开源构建高质量的JDK。至少现在。悲伤,但却是真的。
尝试使用Sun JDK重现它。 http://www.gentoo.org/doc/en/java.xml
关于静态初始化块的评论值得关注。