我们能够查看JVM内存中的对象吗?

9
在工作中,我们发现在某些情况下(特别是较慢的情况下),我们具有不同的行为,这种行为是在重新启动后获得的。
我们猜测缓存没有正确初始化,或者可能存在并发问题... 无论如何,在生产环境之外无法重现。
我们实际上没有激活日志记录器... 这是一个旧组件...
因此,我想知道是否有工具可以帮助我们查看JVM内存中存在的不同对象,以便检查缓存的内容...
谢谢!
编辑:我没有直接访问生产服务器的权限,我们的应用程序服务器是weblogic 10,我没有指向对象的指针,但我知道缓存对象类型...
编辑2:我们的服务器正在运行jre 1.5,是否可以使用jmap?在jdk5中找不到它:(同时,由于安全原因,我们不能进行远程调试...
编辑3:实际上,jhat + VisualVM对我来说很好,我在转储中找到了我的对象,但我无法正确阅读hashmap(包含约60000个项的对象)... 是否有一种友好的方法来读取concurrenthashmap的工具?我需要查找键的值(或其在映射中的存在),而无需手动浏览60k条记录。实际上,我在eclipse MAT论坛上读到,它也无法实现...
编辑4:经过一些实验,我真的很喜欢像VisualVM这样的工具。我还使用了YourKit。有一些有用的功能,比如OQL可以找到您需要查看的正确实例...

我不知道有什么方法可以在没有某种指针/变量引用的情况下访问对象。 - aperkins
3个回答

8

这基本上是对Will所说的内容的扩展。 我们的管理员通过在生产系统上进行堆转储而取得了巨大的成功,但需要注意的是,在进行堆转储的特定服务器完成之前,该服务器将无响应。然后获取该文件并使用Eclipse MAT插件查看它。如果您不喜欢Eclipse,则也可以使用Netbeans和普通的VisualVM插件。这可能会创建一些大文件,您可能需要在64位系统上运行。


+1 给 Eclipse MAT - 在我看来,这是目前最强大(免费)的堆可视化工具。强烈推荐,并且有很棒的插件可以以有趣的方式查看堆(例如集合填充%!)。 - Trent Gray-Donald

4

使用jmap命令来转储对象实例的数量相对容易,但我不知道这是否是您真正感兴趣的内容。

您还可以使用jmap命令转储整个堆,并结合jhat工具查看对象之间的关系(即哪个对象指向哪个对象),但不一定能查看对象的内容。

当然,在堆转储中存在这些数据,但它们不是“一键可见”的。

我认为一些专业的分析工具可以让您在堆转储中检查对象。

否则,最好添加一些特定的仪器到应用程序中,通过自定义代码、JMX或其他触发器来提供您正在寻找的特定内省。


+1 - 我之前不知道堆转储这个功能,如果我早一个月或两个月知道它的话,那真的会非常有用 :) - aperkins
我们使用Java 1.5版本。在1.5版本的JRE上是否可以使用jmap呢?我发现jmap只存在于JDK6中,而且它无法与我的本地1.5版本的JRE一起工作... - Sebastien Lorber

3
你是否有对象的句柄/指针?如果是这样,你可以在调试模式下启动它,并在像Eclipse这样的调试器中查看它。这将允许你检查变量等内容。
或者,你可以编写一个小记录器,反射地遍历类并记录正在进行的操作。所有这些都假定你有一个可以开始步入代码或可以获取缓存值引用的位置。
编辑:正如我在评论中所提到的,我不知道如何在没有某种引用或引用链接的情况下访问对象。例如,该对象是否封装在另一个对象中?如果是这样,你可以执行以下操作:
Class<?> objectClass = myPointer.getClass();
Field[] objectFields = objectClass.getDeclaredFields();
for (Field field : objectFields) {
    field.setAccessible(true);
    //Or whatever you would need to do to get the information you need
    System.out.println(field.get(myPointer).toString());
}

你也可以这样做:

Field targetField = objectClass.getDeclaredField("myFieldName");
targetField.setAccessible(true);
MyOldObjectType target = (MyOldObjectType)targetField.get(myPointer);
//do whatever you need to do here

请注意,所有反射方法都会抛出异常,因此您需要适当地处理这些异常。 此外,setAccessible(true)表示您可以访问对象上的私有字段和方法。 这是非常危险的 - 只有在绝对必要时才使用它。


我认为像Eclipse中那样的调试器不是一个选项,因为“它在除了生产环境以外的任何其他环境中都无法重现”。 - Andrew Hubbs
1
@Andrew Hubbs:我个人在生产环境中使用过Eclipse调试 - 您必须在调试模式下启动系统,并设置其进行远程连接。那里有选项 - 这并不容易,但是可以实现。 - aperkins
我们的服务器不在同一个城市,我不知道它们是否可以在调试模式下启动生产WebLogic,并且也不知道它们是否会通过隧道给我访问权限... - Sebastien Lorber
@Sebastien Lorber:我建议你使用@Will Hartung的建议,你可以获取一个堆转储文件,然后使用各种工具进行解析。只是提醒一下,第一次看到远程调试会话时,我也是连接到另一个城市的服务器上。虽然这是可能的,但对于你来说,获取堆转储文件似乎是更好的选择。 - aperkins
当然可以,我每天都在远程服务器上进行调试,但他们从未让我访问生产实例... - Sebastien Lorber
@Sebastian Lorber:我能理解这个情况 - 在我参与的几次类似情况中,都是非常特殊的情况,我们无法通过其他方式找出问题所在,只能通过我们内部的IT团队来完成,并且他们需要签署批准。 - aperkins

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接