【问题标题】:Runtime memory increase when migrating a Swing application from Java 6 to Java 7 Update 11将 Swing 应用程序从 Java 6 迁移到 Java 7 Update 11 时运行时内存增加
【发布时间】:2023-03-26 01:28:01
【问题描述】:

一旦 Java Swing 应用程序从 Java 6 32 位迁移到 Java 7 32 位更新 11,我们将面临性能问题。谁能提供一些线索?

应用程序使用 Java Web-start 技术,服务器是 Tomcat 7。客户端应用程序正在消耗 1GB 内存,因此屏幕冻结。

我们正在交换序列化的对象,代码如下:

Object object = connection.sendCommand(command); // exchanging serialized object            

public class ConnectionImpl implements Connection {
    public Object sendCommand(Command command) throws Exception {
        URL url = new URL(System.getProperty("serverUrl"));
        URLConnection connection = url.openConnection();
        connection.setDoInput(true);
        connection.setDoOutput(true);
        connection.setUseCaches(false);
        oos = new ObjectOutputStream(new BufferedOutputStream(connection
                    .getOutputStream()));
        oos.writeObject(command);
        oos.flush();
        InputStream inputStream = connection.getInputStream();
        ZipInputStream zip = new ZipInputStream(inputStream);
        if (zip.getNextEntry() != null) {
            ois = new ObjectInputStream(zip);
            object = ois.readObject();
        }
        zip.close();
    }
}

// The serialized class
public class CommandImpl implements Command, Serializable {
    private String serviceName;
    private String methodName;
    private Map<String, Object> parameterMap;

// followed by getter & setters
}
  • 客户端计算机: Windows 7 32 位,Java 7 更新 11 32 位
  • 服务器机器: Windows 64 bit 2008 R2 Enterprise Server,Java 7 update 11 64 bit

代码没有变化,只是更新了JVM。

我使用 Java VisualVM 为 JDK 1.6 和 JDK 1.7 拍摄了内存快照,以下是包含快照的 rar 文件的链接:

Memory Snapshots using Java VisualVM Heap Dump using Java VisualVM

NetBeans IDE 提供了将代码从 Java 6 迁移到 Java 7 的选项。以下是与此相关的链接:

https://netbeans.org/kb/docs/java/editor-inspect-transform.html#convert

将整个源代码从 Java 6 迁移到 Java 7 不会出现任何问题吗?或者有人觉得这可能会产生一些问题?如果我们这样做,我们是否可以在某种程度上解决这个性能问题?

【问题讨论】:

  • "谁能提供一些线索"
  • 另外:哪些组件会导致性能下降?你能把它缩小到只有某些组件的屏幕吗?
  • 1GB内存是不是比以前多了?如果是这样,您是否也更改为 64 位 JVM?
  • 您是否尝试过使用 Java 7 重新编译应用程序?
  • @Adrian 是的,我们已经使用 java 7 update 11 (64 bit) 进行了遵守

标签: java swing memory java-web-start


【解决方案1】:

ObjectOutputStream oos 是在方法中构造的,但看起来您从未关闭过它。关闭流将释放内存。

ObjectOutputStream 何时收集垃圾? ObjectOutputStream and ObjectInputStream keep read and written objects in memory。这看起来像是内存泄漏。

我建议您添加 finally 块以确保 Streams 被关闭。 我无法从您发布的内容中判断序列化对象的生命周期,但您可能会从使用 readUnshared 和 writeUnshared 方法中受益。

你写道:

将整个源代码从 Java 6 迁移到 Java 7 不会有任何问题吗?或者有人觉得这可能会产生一些问题?如果我们这样做了,我们是否可以在某种程度上解决这个性能问题?

您不会从使用工具将代码更新到 1.7 中受益。如果你这样做了,你将不再能够在 1.6 中运行你的代码。我建议运行 Netbean 的“性能”检查。此外,我强烈建议对您的代码库运行 FindBugs。

堆转储

  • jdk 1.7 转储在使用 53M 时从堆中取出。
  • 在使用 61M 时进行了 jdk 1.6 转储。

您发布的堆转储并没有那么有用,因为它们看起来像是在内存出现问题之前被采用的。 您需要在系统使用大量内存时进行堆转储,以便您可以检查转储并找出正在使用内存的位置。

添加-XX:+HeapDumpOnOutOfMemoryError jvm 选项。使用此选项,java 将在内存不足时自动获取自己的 heapdump。

快照

** 虽然您的快照存档中有四个文件,但其中两个文件是重复的,但名称不同。查看大小和校验和,您会发现它们是相同的。 **

这些快照信息量更大,但它们似乎也是在使用大量内存之前拍摄的。

1.7 的快照有更多的 String 实例。

1.6 的快照如下所示:

1.7 的快照如下所示:

String.substring no longer shares an underlying character array。这对于执行大量字符串操作的代码来说可能是一个很大的不同。

那些 char[] 将实际数据保存在您的 String 对象中。我会仔细看看哪些对象包含字符串以及这些字符串是如何构造的。

【讨论】:

  • 将整个源代码从 Java 6 迁移到 Java 7 不会有任何问题吗?或者有人觉得这可能会产生一些问题?如果我们这样做了,我们是否可以在某种程度上解决这个性能问题?
【解决方案2】:

我认为问题中可能的解释太多,而确凿的证据太少,无法说出问题所在。

我建议你:

  1. 增加堆大小以使其达到应用程序在 Java 7 上启动的程度,然后
  2. 在 Java 7 和 Java 6 版本上运行内存分析器,以找出 Java 7 使用显着更多内存的原因。

注意事项:

  • 从 Java 6 32 位到 Java 7 64 位客户端执行平台足以解释内存使用量的增加。 (但显然客户端操作系统是 32 位的,因此消除了这种解释,尽管您的问题实际上在这一点上自相矛盾......)
  • 使用 Java 7 编译器(32 位或 64 位)重新编译不太可能产生任何影响。
  • 服务器平台不太可能相关。

【讨论】:

    【解决方案3】:

    看起来很可能且正常

    你说:

    代码没有变化,只是更新了JVM。

    除了即使您的代码没有变化之外,以下方面可能还有很多变化:

    • JVM自己的操作代码
    • 类库的代码

    JVM 的实现变化

    它可能已经为其进程使用了​​更多内存。这不完全是 JVM 规范所涵盖的内容,您对此无能为力。

    类库的代码

    它的静态占用空间可能更大,因此当 JVM 加载它的普通库时,它已经消耗了一些分配的内存。但是,更有可能的是,您对它的使用也会产生影响。假设您经常使用的类更改了其内部实现并使用了比 1.6.x 中更多的内存,这意味着您的程序现在为您持有的该对象的每个实例使用更多内存。

    因此,您可以在此处获得完全不同的内存消耗数据。例如,哈希映射的哈希实现发生了变化(尽管这不应该改变它在最终状态下使用的内存量),一些基本数据结构可能有不同的内部结构(我希望不会),更复杂的组件可能是在内部(记录器、实用程序、数据和事件处理器等)有很多不同。

    类库中的任何更改以及您使用的或您自己引用的任何库正在使用的任何内容都可以解释这种差异,并且足以让您过火。

    可能的解决方法

    简介!

    在启动后使用两种设置拍摄程序的内存快照,并比较每个类的消耗。看看有什么变化,什么可以重新加工、修剪、用其他东西代替或完全丢弃。

    在问题上投入更多资源

    这是一个简单的解决方案,但它通常是一个足够好的解决方案,如果它不是您或使用您的程序的客户的操作问题。

    购买更多 RAM,并增加 JVM 的内存设置。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-09-20
      • 2019-01-24
      • 1970-01-01
      • 2017-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-05-23
      相关资源
      最近更新 更多