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

内存泄露的原因

在本篇文章中,我们将了解什么是内存泄漏、垃圾收集器、Java中的内存泄漏示例以及检测泄漏的工具。

Java的一个主要优点是内存管理主要由Java虚拟机或更具体的Java垃圾收集器(GC)处理。Java垃圾收集器负责在堆上分配和释放对象。

什么是内存泄漏

在计算机科学中,内存泄漏是指计算机程序不释放不需要的资源而错误地管理内存分配。在Java中,当一个对象不再被应用程序使用,但垃圾回收器无法将其从工作内存(堆)中删除时,就会发生内存泄漏。从长远来看,积累对象而不能删除它们会导致OutOfMemoryError

为什么对象不被垃圾回收

是否将对象从工作内存中删除取决于对象类型。有两种类型,引用和未引用。GC定期收集未引用的对象,另一方面GC无法收集具有有效引用的对象。

请看下面的图片,以便更好地理解。

内存泄露的原因

另外,在Java中有四种类型的引用可以用来管理GC如何以及何时收集对象。

  • 强引用-这是默认的对象引用。除非引用指向null,否则无法收集具有强引用的对象。
  • 软引用-对象引用使用java.lang.ref.SoftReference类不会被收集,即使对象对GC是空闲的。软GC需要清除所有JVM所需的内存。
  • 弱引用-使用java.lang.ref如果JVM检测到没有强引用或软引用链接到任何对象,则将为GC标记.WeakReference类。
  • 虚引用-对象引用使用java.lang.ref.PhantomReference不是由GC自动清理的,需要手动清理。在执行referent的finalize方法后,幻影引用被放入引用队列。

什么是垃圾收集器

垃圾收集器是一个JVM守护进程线程,它定期从堆内存收集未引用的对象。当一个对象不再被引用时,它就有资格从堆内存中移出。堆内存分为三个主要区域:年轻一代(Eden空间、S0幸存者空间和S1幸存者空间)、老一代和永久一代。

还有四种类型的垃圾收集器:

  • 串行垃圾收集器
  • 并行垃圾收集器(默认jvm gc)
  • CMS垃圾收集器
  • G1垃圾收集器

垃圾收集器之间的区别:

  1. 串行垃圾收集器是为单线程环境设计的,它使用单线程来收集对象。此GC适用于命令行应用程序,并通过使用-XX:+UseSerialGCJVM参数来启用。
  2. 并行垃圾收集器是默认的JVM垃圾收集器,与串行GC不同,并行GC使用多个线程从工作内存中收集未引用的对象。
  3. CMS垃圾收集器或并发标记清除垃圾收集器使用多个线程来收集未引用的对象,它是为喜欢缩短GC暂停的应用程序而设计的。要启用CMS垃圾收集器,请使用-XX:useConMarkSweepGC JVM参数。
  4. G1垃圾回收器用于较大的堆区域。它使用技术将堆内存划分为多个区域,并并行进行对象收集。要启用G1 GC,请使用XX:+UseG1GCJVM参数。

内存泄漏示例

为了更快地再现内存泄漏,我将使用JVM参数-Xmx125m将堆大小降低到125MB

内存泄漏最常见的情况之一是将大量元素集合引用为静态字段。让我们来看一个例子:

private static final List<String> listOfStrings = new ArrayList<>(100000);

public static void main(String[] args) {
    fillStaticList();

    try {
        Thread.sleep(100_000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private static void fillStaticList() {
    for (int i = 0; i < 100000; i++) {
        listOfStrings.add("Some large text + " + i);
    }
}

内存泄露的原因

在JVM进程的生命周期内,GC永远不会收集静态收集。现在,我将在示例中将重集合声明为局部变量:

public static void main(String[] args) {
    fillLocalList();

    try {
        Thread.sleep(100_000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

private static void fillLocalList() {
    List<String> listOfStrings = new ArrayList<>(100000);
    for (int i = 0; i < 100000; i++) {
        listOfStrings.add("Some large text + " + i);
    }
}

内存泄露的原因

正如您在探查器中所注意到的,一旦方法完成执行,GC就会删除集合。

Java分析器

javaprofiler是一个在JVM级别监视操作的工具。有许多可用的Java分析器,其中最流行的是YourKit、JProfiler和VisualVM。关于VisualVM工具的用法可以参考这篇文章:https://javakk.com/919.html

 

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

关于

发表评论

表情 格式

暂无评论

登录

忘记密码 ?

切换登录

注册