如何在Java中记忆配置文件?

时间:2012-04-11 15:20:27

标签: java profiling visualvm

我还在学习Java的绳索,如果对此有明显的答案,那就很抱歉。我有一个占用大量内存的程序,我想找到一种方法来减少它的使用,但在阅读了很多SO问题之后,我认为我需要在开始优化它之前证明问题所在。

所以这就是我所做的,我在程序的开头添加了一个断点并运行它,然后我启动了visualVM并让它描述了内存(我也在netbeans中做了同样的事情,只是为了比较结果和它们是相同的)。我的问题是我不知道如何阅读它们,我得到的最高区域只是说char[]我看不到任何代码或任何东西(这是有道理的,因为visualvm连接到jvm而不能看看我的来源,但netbeans也没有像我在进行cpu性能分析时那样向我展示源代码。)

基本上我想知道的是哪个变量(希望更多的细节,比如在哪种方法中)所有的内存都被使用,所以我可以专注于在那里工作。有一个简单的方法来做到这一点?我现在正在使用eclipse和java进行开发(并且专门用于分析安装了visualVM和netbeans,但是我愿意安装其他任何你认为可以完成这项工作的东西)。

编辑:理想情况下,我正在寻找能够获取所有物体并按尺寸排序的东西(所以我可以看到哪一个占用内存)。目前它返回一般信息,如string []或int [],但我想知道它所指的是哪个对象,所以我可以努力使其大小更加优化。

5 个答案:

答案 0 :(得分:24)

字符串存在问题

基本上在Java中,String引用(在幕后使用char[]的内容)将主导大多数业务应用程序内存。它们的创建方式决定了它们在JVM中消耗了多少内存。

仅仅因为它们对于大多数业务应用程序来说是如此基本的数据类型,并且它们也是最需要内存的东西之一。这不仅仅是Java的事情,String数据类型在几乎所有语言和运行时库中占用大量内存,因为至少它们只是每个字符1个字节或更差的数组(Unicode )它们是每个字符多个字节的数组。

在对具有Oracle JDBC依赖关系的Web应用程序中分析CPU使用情况时,我发现StringBuffer.append()在所有其他方法调用组合中占据了CPU周期的数量级,更少的任何其他单一方法调用。 JDBC驱动程序进行了大量的String操作,对于使用PreparedStatements进行一切权衡。

你关心的是你无法控制,而不是直接控制

您应该关注的是控制中的内容,即确保您不会持续超过您需要的参考,并且您不会不必要地复制内容。 Java中的垃圾收集例程已经过高度优化,如果您了解其算法的工作原理,您可以确保您的程序以最佳方式运行,以使这些算法正常工作。

Java堆内存与其他语言的手动内存不同,这些规则不适用

在其他语言中被认为是内存泄漏与Java及其垃圾收集系统不同,它们与根本原因不同。

Java内存中很可能不会被泄漏的单个超级对象(在其他环境中悬挂引用)消耗掉。

很可能是很多较小的分配,因为StringBuffer / StringBuilder对象在第一个瞬时中没有适当调整大小,然后必须自动增长char[]数组以保留后续{{1调用。

这些中间对象可能会被垃圾收集器保持的时间超过预期,因为它们所处的范围以及许多其他可能在运行时发生变化的事情。

示例:垃圾收集器可能会认为有候选者,但是因为它认为仍有大量内存可能需要花费太多时间才能将其清除掉时间点,它会等到记忆压力升高。

垃圾收集器现在真的很好,但它不是魔术,如果你正在做退化的事情,它将导致它不能最佳地工作。互联网上有很多关于所有JVM版本的垃圾收集器设置的文档。

这些未引用的对象可能还没有达到垃圾收集器认为需要它们从内存中清除它们的时间,或者可能有一些其他对象(append()对它们进行引用。 )例如,你没有意识到仍然指向该对象。这就是Java中最常被称为 leak 的东西,它更具体地是一个参考泄漏。

示例:如果您知道需要使用List构建4K String,请使用StringBuilder创建new StringBuilder(4096);,而不是默认值,即32和将立即开始创建垃圾,可以表示您认为该对象应该是大小明智的很多次。

您可以发现使用VisualVM实例化了多少类型的对象,这将告诉您需要了解的内容。没有一个大的闪光点指向单个类的单个实例,它说“这是大内存消费者!”,除非只有一个char[]的实例你正在阅读一些大量的文件,这也是不可能的,因为许多其他类在内部使用char[];然后你几乎已经知道了。

我没有看到提及OutOfMemoryError

您的代码中可能没有问题,垃圾收集系统可能没有足够的压力来启动并释放您认为它应该清理的对象。 您认为是一个问题可能不是,除非您的程序与OutOfMemoryError崩溃。这不是C,C ++,Objective-C或任何其他手动内存管理语言/运行时。你没有在你期望的细节层面上决定你的内存是什么。

答案 1 :(得分:9)

JProfiler中,您可以转到堆步行器并激活最大的对象视图。您将看到保留最多内存的对象。 “保留”内存是如果删除了对象,垃圾收集器将释放的内存。

然后,您可以打开对象节点以查看保留对象的参考树。这是最大对象视图的屏幕截图:

enter image description here

免责声明:我公司开发JProfiler

答案 2 :(得分:2)

我建议捕获堆转储并使用Eclipse MAT之类的工具来分析它们。有很多tutorials可用。它提供了dominator tree的视图,以便深入了解堆上对象之间的关系。特别是对于你提到的内容,MAT的“GC根路径”功能将告诉你哪些char [],String []和int []对象被引用。 JVisualVM在识别泄漏和分配时也很有用,特别是通过使用具有分配堆栈跟踪的快照。有很多walk-throughs of the process获取快照并比较它们以找到分配点。

答案 3 :(得分:2)

Java JDK在bin文件夹下附带 JVisualVM ,一旦您的应用程序服务器(例如正在运行),您可以运行visualvm并将其连接到您的localhost,这将为您提供内存分配并使您能够执行堆转储

enter image description here

有关如何启用的更详细步骤: http://sysdotoutdotprint.com/technologies/java/6

答案 4 :(得分:0)

如果使用visualVM检查内存使用情况,则会关注数据,而不是方法。也许你的大char []数据是由许多String值引起的?除非您使用递归,否则数据不会来自局部变量。因此,您可以专注于将元素插入大型数据结构的方法。为了找出导致“内存泄漏”的精确语句,我建议你另外