4年前 (2020-12-03)  jvm |   抢沙发  601 
文章评分 0 次,平均分 0.0

你是否有一个Java应用程序,它一开始运行良好,但过了一段时间就变慢了,还是它对少量文件运行良好,但对于大量文件,性能会下降?也许你的内存泄露了。

关于内存泄露

当修复内存泄漏时;如果有人问我:“如果你当时知道你现在知道的,你会告诉自己什么?”

嗯,我想说。。。(你不讲武德。。。)

虽然本文中描述的方法通常是独立于IDE和OS的,但我在截图和说明中使用了Linux[Fedora]和Eclipse[Plugin development]。

内存泄漏的症状

一开始工作很快,但随着时间的推移会变慢。

  • 适用于小数据集,严重的性能问题与大数据集
  • JVM中老年代内存使用量不断增加
  • JVM内存不足堆错误
  • Spontaneous crashes 自发性碰撞

常见的内存泄漏

如果您忘记关闭资源,或者对对象的引用未释放,则可能会发生这种情况。例如:

  • 文件/文本缓冲区未关闭。(和我的情况一样)
  • 如果没有实现equals()hashcode(),则哈希映射保持引用的活动性,如下代码
import java.util.Map;
public class MemLeak {
public final String key;
public MemLeak(String key) {
    this.key = key;
}

public static void main(String args[]) {
    try {
      Map map = System.getProperties();
      for(;;) {
         map.put(new MemLeak("key"), "value");
      }
    } catch(Exception e) {
        e.printStackTrace();
    }
  }
}

内存溢出怎么解决

有两种方法。第一种是“快速修复”尝试。如果失败了,那你就得走漫长的路了。

  1. 快速修复:Eclipse内存泄漏警告(捕获一些泄漏)
  2. 手动禁用并启用部分代码,并使用VisualVM(或Jconsole,或恒温器)等JVM工具观察JVM的内存使用情况。

快速修复:Eclipse内存泄漏警告/错误

对于符合jdk1.5+的代码,eclipse将针对明显的泄漏情况向您发出警告和错误。更准确地说,任何实现closable(从1.5开始)(例如outputstream从1.5开始)的引用被破坏但对象没有关闭时,都会向您抛出警告。然而,在eclipse项目中并不总是启用泄漏检测。你可能得先打开它们。转到项目设置并启用它们,如下所示:

内存泄露检测

现在eclipse将概述内存泄漏:

内存泄露检测

然而,即使使用花哨的Eclipse hocus pocus,也不能检测到所有的文件关闭和泄漏。尤其是在处理遗留代码(1.5之前的版本)时,很可能会遇到泄漏,因为它们是在实现“closable”之前编写的。或者有时文件打开/关闭嵌套得太深,以至于eclipse无法检测到它们。如果你在这个地方,你可以试试第二步。

手动禁用并启用部分代码,并使用VisualVM等JVM工具观察JVM的内存使用情况

如果你走到这一步,你就得卷起腰来做些体力活。您可以阅读所有代码并尝试了解泄漏发生在哪里。为了帮助您完成这个过程:我建议您尝试使用VisualVM这样的工具。(MAT也能可以)

配置VisualVM

1. 下载工具

2. 打开终端,导航到…/visualvm_xyz/bin运行shell脚本'./visualvm'(或visualvm.exe在windows上)。

3. 你应该看到主窗口。如果您展开'local'并双击正在运行的应用程序(在我的例子中是一个子eclipse),您可以看到配置属性:

内存泄露检测

4. 使用VisualVM进行故障排除(如果它工作正常,就跳过这个:对我来说,最初我无法连接到我的JVM,我无法进行堆转储,并且分析也不起作用。以下是一些可能有帮助的步骤:

  • 确保以自己的用户身份运行它,而不是sudo。
  • 对系统执行完全更新(sudo yum update)。
  • 重新启动有帮助
  • 尝试关闭所有正在运行的Java应用程序。启动VisualVM,然后再试一次。

5. 添加一些插件。在VisualVM对我有用之前,我首先要添加一些插件。转到“工具”->“插件”->“可用插件”。选择以下插件(请随意浏览并添加更多插件):

  • 内存池
  • Visual GC
  • 终止应用程序

用visualvm分析运行代码

1. 现在运行Java应用程序,

2. 将VisualVM附加到应用程序中。

3. 执行导致性能下降的操作。

4. 检查“监视器”和“内存池”选项卡。如果在“Monitor”选项卡中看到内存增加,请尝试按“performgc”(垃圾收集),看看这是否会减少内存使用。

内存泄露检测

5. 然后切换到“内存池”选项卡并检查“老年代”。(对象首先在“伊甸园eden”中徘徊,然后通过幸存者空间过渡,较老的物体进入“老一代”。如果有东西漏了,就在老年代里)

内存泄露检测

6. 现在返回并注释掉程序的大部分代码,直到应用程序刚刚启动和停止为止。

7. 重复上述步骤,直到应用程序完全不泄漏为止。

8. 然后通过多次迭代重新启用部分代码并检查VisualVM内存使用情况。当应用程序再次开始泄漏时,进入导致内存泄漏的方法并进一步缩小范围。

9. 最终,您将把问题缩小到单个类,甚至可能是单个方法。一旦你到了那里,仔细验证所有文件缓冲区是否关闭,哈希映射是否正确使用。

基准测试代码

有时很难判断你的新代码是否比旧代码更好。在这种情况下,您可能需要对应用程序的性能进行基准测试。以下是一些代码,您可以插入到正确的位置,以获取有关运行时和垃圾回收运行次数的信息:

long start = System.currentTimeMillis();
..
 //your code
..
long end = System.currentTimeMillis();
System.out.println("Run time: " + Long.toString(end - start));
System.out.println(printGCStats());
 
public static String printGCStats() {
 long totalGarbageCollections = 0;
 long garbageCollectionTime = 0;
 for (GarbageCollectorMXBean gc : ManagementFactory.getGarbageCollectorMXBeans()) {
 long count = gc.getCollectionCount();
 
 if (count >= 0) {
 totalGarbageCollections += count;
 }
 
 long time = gc.getCollectionTime();
 
 if (time >= 0) {
 garbageCollectionTime += time;
 }
 }
 return "Garbage Collections: " + totalGarbageCollections + "n" +
 "Garbage Collection Time (ms): " + garbageCollectionTime;
}

Heap dump 堆转储

在任何时候,您都可以获取堆转储,然后查看有多少类实例处于打开状态,以及它们使用了多少空间。你可以双击它们来查看它们的内容。如果您想查看应用程序生成了多少对象,这很有用。

内存泄露检测

定位内存泄露你还可以参考这篇文章:https://javakk.com/919.html

 

除特别注明外,本站所有文章均为老K的Java博客原创,转载请注明出处来自https://javakk.com/937.html

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册