7

一旦 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 位 2008 R2 Enterprise Server,Java 7 更新 11 64 位

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

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

使用 Java VisualVM 的内存快照 使用 Java VisualVM 的堆转储

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

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

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

4

3 回答 3

6

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

ObjectOutputStream 何时收集垃圾? ObjectOutputStream 和 ObjectInputStream 将读取和写入的对象保存在内存中。这看起来像内存泄漏。

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

你写了:

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

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

堆转储

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

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

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

快照

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

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

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

1.6 的快照如下所示: 视觉图像

1.7 快照如下所示: 在此处输入图像描述

String.substring 不再共享底层字符数组。这对于进行大量字符串操作的代码来说可能是一个很大的区别。

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

于 2013-06-11T00:33:03.710 回答
5

我认为问题中有太多可能的解释和太少的确凿证据来说明问题所在。

我建议你:

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

笔记:

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

似乎很有可能和正常

你说:

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

除了即使您的代码没有变化,可能还有很多变化:

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

JVM 的实现变化

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

类库的代码

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

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

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

可能的解决方法

轮廓!

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

在问题上投入更多资源

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

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

于 2013-06-07T12:46:30.690 回答