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

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

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内存管理和垃圾回收有了更深入的理解。如果有任何问题或建议,欢迎在评论区与我们分享。愿您的编程之路愉快!


相关文章
|
3月前
|
缓存 算法 Java
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
这篇文章详细介绍了Java虚拟机(JVM)中的垃圾回收机制,包括垃圾的定义、垃圾回收算法、堆内存的逻辑分区、对象的内存分配和回收过程,以及不同垃圾回收器的工作原理和参数设置。
110 4
JVM知识体系学习六:JVM垃圾是什么、GC常用垃圾清除算法、堆内存逻辑分区、栈上分配、对象何时进入老年代、有关老年代新生代的两个问题、常见的垃圾回收器、CMS
|
3月前
|
存储 SQL 小程序
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
这篇文章详细介绍了Java虚拟机(JVM)的运行时数据区域和JVM指令集,包括程序计数器、虚拟机栈、本地方法栈、直接内存、方法区和堆,以及栈帧的组成部分和执行流程。
47 2
JVM知识体系学习五:Java Runtime Data Area and JVM Instruction (java运行时数据区域和java指令(大约200多条,这里就将一些简单的指令和学习))
|
7月前
|
缓存 Java
《JVM由浅入深学习九】 2024-01-15》JVM由简入深学习提升分(生产项目内存飙升分析)
《JVM由浅入深学习九】 2024-01-15》JVM由简入深学习提升分(生产项目内存飙升分析)
58 0
|
3月前
|
Java 应用服务中间件 程序员
JVM知识体系学习八:OOM的案例(承接上篇博文,可以作为面试中的案例)
这篇文章通过多个案例深入探讨了Java虚拟机(JVM)中的内存溢出问题,涵盖了堆内存、方法区、直接内存和栈内存溢出的原因、诊断方法和解决方案,并讨论了不同JDK版本垃圾回收器的变化。
46 4
|
3月前
|
Arthas 监控 Java
JVM知识体系学习七:了解JVM常用命令行参数、GC日志详解、调优三大方面(JVM规划和预调优、优化JVM环境、JVM运行出现的各种问题)、Arthas
这篇文章全面介绍了JVM的命令行参数、GC日志分析以及性能调优的各个方面,包括监控工具使用和实际案例分析。
88 3
|
3月前
|
SQL 缓存 Java
JVM知识体系学习三:class文件初始化过程、硬件层数据一致性(硬件层)、缓存行、指令乱序执行问题、如何保证不乱序(volatile等)
这篇文章详细介绍了JVM中类文件的初始化过程、硬件层面的数据一致性问题、缓存行和伪共享、指令乱序执行问题,以及如何通过`volatile`关键字和`synchronized`关键字来保证数据的有序性和可见性。
39 3
|
3月前
|
缓存 前端开发 Java
JVM知识体系学习二:ClassLoader 类加载器、类加载器层次、类过载过程之双亲委派机制、类加载范围、自定义类加载器、编译器、懒加载模式、打破双亲委派机制
这篇文章详细介绍了JVM中ClassLoader的工作原理,包括类加载器的层次结构、双亲委派机制、类加载过程、自定义类加载器的实现,以及如何打破双亲委派机制来实现热部署等功能。
85 3
|
3月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
66 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
3月前
|
小程序 Oracle Java
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
这篇文章是关于JVM基础知识的介绍,包括JVM的跨平台和跨语言特性、Class文件格式的详细解析,以及如何使用javap和jclasslib工具来分析Class文件。
62 0
JVM知识体系学习一:JVM了解基础、java编译后class文件的类结构详解,class分析工具 javap 和 jclasslib 的使用
|
7月前
|
存储 缓存 NoSQL
Redis系列学习文章分享---第十三篇(Redis多级缓存--JVM进程缓存+Lua语法)
Redis系列学习文章分享---第十三篇(Redis多级缓存--JVM进程缓存+Lua语法)
85 1