3

我有一项服务,其中要解析的域名来自不受信任的来源。最近,它一直因内存不足而崩溃。我缩小了可能的原因,得出的结论一定与最近的 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);}
    }
}

我用这条线设置了 dnsmasq/etc/dnsmasq.conf以使分辨率更快:

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)
4

6 回答 6

5

" InetAddress 类有一个缓存来存储成功和不成功的主机名解析。默认情况下,安装安全管理器时,为了防止 DNS 欺骗攻击,正向主机名解析的结果将被永久缓存。当安全没有安装管理器,默认行为是在有限的(依赖于实现的)时间段内缓存条目。不成功的主机名解析的结果被缓存很短的时间(10秒)以提高性能。

来源:InetAddress.java 的 javadoc http://docs.oracle.com/javase/6/docs/api/java/net/InetAddress.html

我认为它正在缓存所有永久解析的主机名并最终导致 outOfMemoryError 。

您可以尝试设置 Java 安全属性来控制 TTL(生存时间),看看这是否有助于解决这个问题

于 2013-08-22T07:48:30.770 回答
3

您正在静态块中执行长时间运行的代码。

当 JVM 加载一个类时,它首先执行静态块。我的怀疑是 JVM 无法加载该类,因为您的代码(本质上是一个长时间运行的操作)和崩溃。

通常您在静态块中执行简单的初始化步骤。将您的代码移动到静态 main 方法,然后重试。看看它是否提高了运行时间?

于 2013-08-21T19:47:35.163 回答
2

你为什么不想做一个基本的分析?有几个问题是可能的,所以使用合适的工具五分钟肯定会帮助并节省您的时间。

1.你的例子没有SecurityManager

在此处输入图像描述

2. 您的 SecurityManager 示例

在此处输入图像描述

正如预期的那样,启用 SecurityManager 时内存使用量更大。我还没有设置任何 DNS 基础设施,所以请求率没有那么高。

需要注意的重要事项:

  • 垃圾收集需要一些时间
  • 在某些情况下,默认 GC 无法提供足够的“吞吐量”,因此最终内存会耗尽
  • VM内存模型没那么简单
  • 默认虚拟机不提供实时性能保证

我建议避免任何调整,直到您使用您的设置对您的环境进行一些分析。我们可以在这里花费大量时间尝试提出根本原因,但您会更快地找到答案。请尝试使用分析器,不要忘记分享您的结果。这是一个相当有趣的话题。

谢谢你。

于 2013-08-19T19:28:03.247 回答
1

我发现这java.lang.OutOfMemoryError: Java heap space通常具有误导性,可能意味着您的 Perm Space 已用尽,而不是您的永久对象堆。

尝试通过以下方式增加您的烫发空间:-XX:MaxPermSize=128M

安全管理器无疑增加了加载到 Perm 中的类的数量。

于 2013-08-15T14:35:20.310 回答
0

崩溃可以重现-Djava.compiler=NONE吗?我想知道您是否遇到了 JIT 编译器错误。

另外,我很好奇在静态main方法中运行 for 循环时失败是否相同。我想知道在加载类时在静态块中执行长时间运行的 for 循环是否是一个因素。

于 2013-08-21T01:15:34.967 回答
-1

我仍然认为问题与 OpenJDK 有关。

有 2 种具有生产质量的 JDK:一种是 Sun(现在是 Oracle),一种是 IBM。

Gentoo 是很好的 Linux,但我认为您无法从 GNU 开源构建高质量的 JDK。至少现在。悲伤,但真实。

尝试使用 Sun JDK 重现它。 http://www.gentoo.org/doc/en/java.xml

关于静态初始化块的注释值得关注。

于 2013-08-21T14:18:50.413 回答