《JVM由浅入深学习【五】 2024-01-08》JVM由简入深学习提升分享

目录

      • JVM何时会发生堆内存溢出?
        • 1. 堆内存溢出的定义
        • 2. 内存泄漏的原因
        • 3. 堆内存溢出的常见场景
        • 4. JVM参数调优
        • 5. 实际案例分析
      • JVM如何判断对象可以回收
        • 1.可达性分析的基本思路
        • 2.实际案例
        • 3.可以被回收的对象
        • 4.拓展, 谈谈 Java 中不同的引用类型?
      • 结语
      • 感谢阅读

JVM何时会发生堆内存溢出?

前言:

欢迎来到本篇博客,我们将深入探讨 Java 虚拟机(JVM)中堆内存溢出的情况。理解这些情况对于避免内存问题和编写稳定的Java应用程序至关重要。

1. 堆内存溢出的定义

在Java中,堆内存溢出指的是当应用程序在堆内存中创建的对象无法被垃圾回收器有效回收,导致堆内存不足。这是一种常见的内存问题,经常会导致程序的崩溃。

2. 内存泄漏的原因
  • 无效的引用: 对象的引用没有及时释放,导致垃圾回收器无法回收不再使用的对象。

  • 循环引用: 对象之间存在相互引用,形成了循环链,使得这些对象都无法被垃圾回收。

3. 堆内存溢出的常见场景
  • 大对象导致的溢出: 当创建大量大对象(如大数组)时,堆内存可能不足。

  • 长时间运行的应用: 在长时间运行的Java应用程序中,由于对象的持续创建和保留,可能导致堆内存溢出。

4. JVM参数调优
  • 堆内存大小设置: 可通过 -Xms-Xmx 参数设置堆内存的初始大小和最大大小,合理调整可以减少溢出的风险。

    java -Xms256m -Xmx512m -jar your-application.jar
    
5. 实际案例分析

考虑以下代码片段,它在一个循环中创建大量对象,但却没有释放引用:

import java.util.ArrayList;
import java.util.List;

public class HeapMemoryOverflowExample {

    public static void main(String[] args) {
        List<String> stringList = new ArrayList<>();

        try {
            while (true) {
                // 创建大量字符串对象
                String largeString = new String(new char[1000000]);
                // 将字符串对象添加到集合中
                stringList.add(largeString);
            }
        } catch (OutOfMemoryError e) {
            System.out.println("堆内存溢出异常捕获:" + e.getMessage());
        }
    }
}

在这个例子中,由于对象持续被添加到stringList列表中,垃圾回收器无法回收这些对象,最终导致堆内存溢出。

JVM如何判断对象可以回收

Java虚拟机(JVM)通过垃圾回收机制来自动管理内存,判断哪些对象可以被回收是垃圾回收的核心问题。JVM使用一种称为"可达性分析"的方法来判断对象的可达性,即判断对象是否还与引用链中的任何强引用相连。如果一个对象不再与任何强引用相连,那么它就成为不可达对象,可以被垃圾回收。

1.可达性分析的基本思路
  1. 根搜索算法(GC Roots): 通过一系列称为GC Roots的根对象作为起始点,从这些根对象开始向下搜索,能够到达的对象称为可达对象,不能到达的对象即为不可达对象。

  2. GC Roots的类型:

    • 虚拟机栈中引用的对象: 在方法的局部变量表中引用的对象。
    • 本地方法栈中JNI(Java Native Interface)引用的对象: JNI是Java调用本地语言的接口,本地方法中引用的Java对象。
    • 方法区中类静态属性引用的对象: 静态属性属于类的,它引用的对象也属于可达对象。
    • 方法区中常量引用的对象: 常量池中的字符串常量等。
2.实际案例

考虑以下代码,演示了一个对象何时成为不可达对象:

public class GarbageCollectionExample {

    public static void main(String[] args) {
        Object obj1 = new Object();  // 强引用 obj1 指向新创建的对象
        Object obj2 = new Object();  // 强引用 obj2 指向新创建的对象

        obj1 = null;  // obj1 不再指向对象,成为不可达对象

        System.gc();  // 提醒垃圾回收器进行垃圾回收

        // 在这里,垃圾回收器可能会回收 obj1 所指向的对象
    }
}

在上述代码中,obj1一开始指向一个新创建的对象,后来被置为null,不再与任何强引用相连。当程序调用System.gc()提醒垃圾回收器进行垃圾回收时,垃圾回收器可能会回收obj1原来所指向的对象。

要注意的是,垃圾回收器的执行是不确定的,调用System.gc()并不一定会立即触发垃圾回收。这只是一个提示,实际回收时机由垃圾回收器自行决定。

3.可以被回收的对象

1、在虚拟机栈(栈中的本地变量表)中引用的对象,警如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等所引用的对象;
2、方法区/元空间中的类静态属性引用的对象
3、方法区/元空间中的常量引用的对象
4、在本地方法栈中JNI (即通常所说的 Native 方法) 引用的对象
5、Java 虚拟机内部的引用,如基本数据类型对应的 Class 对象,一些常驻的异常对象 (比如
NullPointExcepiton、OutOfMemoryError) 等,还有系统类加载器
6、所有被同步锁 (synchronized 关键字) 持有的对象;
7、反映 Java 虚拟机内部情况的JMXBean、JVMTI 中注册的回调本地代码缓存等

4.拓展, 谈谈 Java 中不同的引用类型?

Java 里有不同的引用类型,分别是强引用、软引用、弱引用 和 虚引用
强引用: Object object = new Object() ;
软引用: SoftReference 内存充足时不回收,内存不足时则回收;
弱引用: WeakReference 不管内存是否充足,只要 GC 一运行就会回收该引用对象
虚引用: PhantomReference 这个其实暂时忽略也行,因为很少用,它形同虚设,就像没有引用一样,其作用就是该引用对象被 GC 回收时候触发一个系统通知,或者触发进一步的处理

结语

在Java中,垃圾回收机制是一项重要的特性,它通过判断对象的可达性来自动管理内存,确保程序运行时不会因为内存泄漏而导致性能问题。了解对象何时成为不可达对象,以及垃圾回收的基本原理,对于编写高效的Java程序至关重要。

通过本文的介绍,我们深入了解了JVM如何判断对象是否可以回收,以及可达性分析的基本思路。在编写Java程序时,及时释放不再需要的对象引用是一种良好的编程习惯,有助于提高程序的性能和资源利用率。

感谢阅读

感谢您阅读本篇关于JVM的文章。希望通过这篇分享,您对Java内存管理和垃圾回收有了更深入的理解。如果有任何问题或建议,欢迎在评论区与我们分享。愿您的编程之路愉快!

你可能感兴趣的:(面试,jvm,gc)