jvm基础篇之垃圾回收[1](方法区、堆回收)

文章目录

  • 垃圾回收类型
    • 手动垃圾回收:C/C++的内存管理
    • 自动垃圾回收:Java的内存管理
    • 自动垃圾回收应用场景
    • 不同垃圾回收对比
  • 线程不共享部分的回收
  • 方法区的回收
    • 手动触发回收
  • 堆回收
    • 两种判断方法
      • 引用计数法
      • 查看垃圾回收日志
      • 可达性分析法
      • GC Root对象类型
      • 可达性算法案例分析
      • 查看GC Root
  • 五种对象引用
    • 强引用
    • 软引用
      • 执行过程
      • SoftReference对象的回收
      • 软引用编程案例
      • 软引用使用场景-缓存
    • 弱引用
    • 虚引用(不常用)
    • 终结器引用(不常用)

在这里插入图片描述

垃圾回收类型

手动垃圾回收:C/C++的内存管理

  • 在C/C++这类没有自动垃圾回收机制的语言中,一个对象如果不再使用,需要手动释放,否则就会出现内存泄漏。这种释放对象的过程为垃圾回收,而需要程序员编写代码进行回收的方式为手动回收。
  • 内存泄漏指的是不再使用的对象在系统中未被回收,内存泄漏的积累可能会导致内存溢出。
    在这里插入图片描述

自动垃圾回收:Java的内存管理

  • Java中为简化对象的释放,引入了自动的垃圾回收(Garbage Collection简称GC)机制。通过垃圾回收器来对不再使用的对象完成自动的回收,垃圾回收器主要负责对堆上的内存进行回收。很多语言比如C#、Python、Go都拥有垃圾回收器。
    在这里插入图片描述

自动垃圾回收应用场景

  1. 解决系统僵死的问题:大厂的系统出现的许多系统僵死问题都与频繁的垃圾回收有关
  2. 性能优化:对垃圾回收器进行合理的设置可以有效提升程序的执行性能

不同垃圾回收对比

类型方式优点缺点
自动垃圾回收自动根据对象是否使用由虚拟机来回收对象降低程序员实现难度、降低对象回收bug的可能性程序员无法控制内存回收的及时性
手动垃圾回收由程序员编程实现对象的删除回收及时性高,由程序员把控回收的时机编写不当容易出现悬空指针、重复释放、内存泄漏等问题

线程不共享部分的回收

  • Java虚拟机在运行Java程序过程中管理的内存区域,称之为运行时数据区。
    在这里插入图片描述
  • 线程不共享的部分,都是伴随着线程的创建而创建,线程的销毁而销毁。而方法的栈帧在执行完方法之后就会自动弹出栈并释放掉对应的内存。
    在这里插入图片描述
  • 类的生命周期
    在这里插入图片描述

方法区的回收

  • 方法区中能回收的内容主要就是不再使用的类。判定一个类可以被卸载。需要同时满足下面三个条件:
  1. 此类所有实例对象都已经被回收,在堆中不存在任何该类的实例对象以及子类对象
    Class<?> clazz = loader.loadClass("com.itheima.my.A");
    Object o = clazz.newInstance();
    o=null;
    
  2. 加载该类的类加载器已经被回收
    URLClassLoader loader = new URLClassLoader(new URL[]{new URL("file:D:\\lib\\")});
    loader=null;
    
  3. 该类对应的 java.lang.Class 对象没有在任何地方被引用
    Class<?> clazz = loader.loadClass("com.itheima.my.A");
    clazz=null;
    

手动触发回收

  • 如果需要手动触发垃圾回收,可以调用System.gc()方法。
  • 注意事项:调用System.gc()方法并不一定会立即回收垃圾,仅仅是向Java虚拟机发送一个垃圾回收的请求,具体是否需要执行垃圾回收Java虚拟机会自行判断。
  • 注意:开发中此类场景一般很少出现,主要在如 OSGi、JSP 的热部署等应用场景中。每个jsp文件对应一个唯一的类加载器,当一个jsp文件修改了,就直接卸载这个jsp类加载器。重新创建类加载器,重新加载jsp文件

堆回收

  • Java中的对象是否能被回收,是根据对象是否被引用来决定的。如果对象被引用了,说明该对象还在使用,不允许被回收。
    在这里插入图片描述
    在这里插入图片描述
  • 图中A的实例对象要回收,有两个引用要去除:
    1. 栈中a1变量到对象的引用
    2. B对象到A对象的引用
      在这里插入图片描述
  • 如果在main方法中最后执行 a1 = null ,b1 = null,可以回收对象,方法中已经没有办法使用引用去访问A和B对象。
    在这里插入图片描述

两种判断方法

  • 常见的有两种判断方法:引用计数法和可达性分析法

引用计数法

  • 引用计数法会为每个对象维护一个引用计数器,当对象被引用时加1,取消引用时减1

缺点

  1. 每次引用和取消引用都需要维护计数器,对系统性能会有一定的影响
  2. 存在循环引用问题,所谓循环引用就是当A引用B,B同时引用A时会出现对象无法回收的问题
    在这里插入图片描述

查看垃圾回收日志

  • 查看垃圾回收日志,可以使用-verbose:gc参数
    在这里插入图片描述

可达性分析法

  • Java使用的是可达性分析算法来判断对象是否可以被回收。可达性分析将对象分为两类:垃圾回收的根对象(GC Root)和普通对象,对象与对象之间存在引用关系。
  • 可达性分析算法指的是如果从某个到GC Root对象是可达的,对象就不可被回收。
    在这里插入图片描述

GC Root对象类型

  1. 线程Thread对象,引用线程栈帧中的方法参数、局部变量等

  2. 系统类加载器加载的java.lang.Class对象。
    在这里插入图片描述

  3. 监视器对象,用来保存同步锁synchronized关键字持有的对象。
    在这里插入图片描述

  4. 本地方法调用时使用的全局对象


可达性算法案例分析

  • 下面代码中的A实例对象和B示例对象,通过可达性算法判断对象能被回收的
    在这里插入图片描述

查看GC Root

  • 通过arthas和eclipse Memory Analyzer (MAT) 工具可以查看GC Root,MAT工具是eclipse推出的Java堆内存检测工具
  • 注意:需要使用JDK17+,如果使用JDK8请更新JDK
  • 具体操作步骤如下:
    1. 使用arthas的heapdump命令将堆内存快照保存到本地磁盘中。
    2. 使用MAT工具打开堆内存快照文件。
    3. 选择GC Roots功能查看所有的GC Root。
      在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

五种对象引用

  • 可达性算法中描述的对象引用,一般指的是强引用,即是GCRoot对象对普通对象有引用关系,只要这层关系存在,普通对象就不会被回收
  • 完整的对象引用方式为:强引用、软引用、弱引用、虚引用、终结器引用

强引用

  • 强引用(Strong Reference):是最常见的引用类型,也是默认的引用类型。当一个对象具有强引用时,即使系统内存紧张,垃圾回收器也不会回收该对象。
  • 只有当该对象没有任何强引用指向时,才会被回收。
Object obj = new Object(); // 强引用

软引用

  • 软引用是一种相对弱化的引用类型,用于描述还有用但非必需的对象。如果一个对象只有软引用关联到它,当程序内存不足时,就会将软引用中的数据进行回收。可以使用SoftReference类来创建软引用
SoftReference<Object> softRef = new SoftReference<>(new Object()); // 软引用
  • 在JDK 1.2版之后提供了SoftReference类来实现软引用,软引用常用于缓存中
    在这里插入图片描述

执行过程

  • 软引用的执行过程如下:
  1. 将对象使用软引用包装起来,new SoftReference<对象类型>(对象)
  2. 内存不足时,虚拟机尝试进行垃圾回收。
  3. 如果垃圾回收仍不能解决内存不足的问题,回收软引用中的对象。
  4. 如果依然内存不足,抛出OutOfMemory异常
    在这里插入图片描述

SoftReference对象的回收

  • 软引用中的对象如果在内存不足时回收,SoftReference对象本身也需要被回收。如何知道哪些SoftReference对象需要回收呢?
  • SoftReference提供了一套队列机制:
  1. 软引用创建时,通过构造器传入引用队列(软引用对象已被回收而SoftReference对象还未回收的SoftReference对象队列,用于跟踪被垃圾回收器回收的软引用对象)
  2. 在软引用中包含的对象被回收时,该软引用对象会被放入引用队列
  3. 通过代码遍历引用队列,将SoftReference的强引用删除
    在这里插入图片描述

软引用编程案例

  1. 创建软引用:使用Caffeine中的软引用
    public static void main(String[] args) {Cache<Object, Object> build = Caffeine.newBuilder().softValues().build();}
    
  2. 基本使用:再运行配置中设置VM选项-Xmx200m(设置堆的大小为200M)
        public static void main(String[] args) throws IOException {byte[] bytes = new byte[1024 * 1024 * 100];SoftReference<byte[]> softReference = new SoftReference<>(bytes);bytes = null;System.out.println(softReference.get());byte[] bytes2 = new byte[1024 * 1024 * 100];System.out.println(softReference.get());}// 运行结果:由于vm启动还要占用一部分的堆空间,如果byte释放,才可以放下byte2,// byte释放后,softReference对象也会释放,所以返回为null// [B@58ceff1// null
    
  3. 引用队列使用:设置堆大小VM参数-Xmx900m是,count=8,否则堆空间足够的情况下,不触发软引用对象的回收
    import java.io.IOException;
    import java.lang.ref.SoftReference;
    import java.lang.ref.ReferenceQueue;
    import java.util.ArrayList;public class SoftReferenceExample {public static void main(String[] args) throws IOException {// 创建一个列表来存储软引用ArrayList<SoftReference> softReferences = new ArrayList<>();// 创建一个引用队列,用于跟踪被垃圾回收器回收的软引用对象ReferenceQueue<byte[]> queues = new ReferenceQueue<>();// 循环创建10个大对象,并为每个对象创建一个软引用for (int i = 0; i < 10; i++) {// 创建一个100MB的字节数组byte[] bytes = new byte[1024 * 1024 * 100];// 创建一个软引用,关联字节数组和引用队列SoftReference<byte[]> studentRef = new SoftReference<>(bytes, queues);// 将软引用添加到列表中softReferences.add(studentRef);}// 创建一个软引用变量,用于从引用队列中取出被回收的软引用SoftReference<byte[]> ref = null;// 用于计数被垃圾回收器回收的软引用数量int count = 0;// 循环,从引用队列中取出软引用直到没有更多的软引用while ((ref = (SoftReference<byte[]>) queues.poll()) != null) {// 对回收的软引用计数count++;}// 输出被回收的软引用数量System.out.println(count);}
    }
    
  4. 使用ArrayList来存储软引用(SoftReference),软引用允许其引用的对象在内存不足时被垃圾回收器回收。
  5. 使用ReferenceQueue来跟踪垃圾回收器回收的对象。当软引用所引用的对象被回收时,软引用对象本身会被添加到ReferenceQueue中。
  6. 在for循环中创建了10个大的字节数组(每个大约100MB)并为每个数组创建了一个SoftReference,同时将该软引用关联到了之前创建的引用队列queues。
  7. 在while循环中,使用poll()方法从ReferenceQueue中非阻塞地移除并返回被垃圾回收器回收的软引用对象。如果队列为空,则返回null。
  8. 通过计数器count记录被回收的软引用数量,并在循环结束后打印出来。
  • 注意:这段代码仅仅创建了引用,并没有显式地触发垃圾回收,所以程序运行结束时输出的count可能是0。软引用的回收通常发生在系统内存不足时,垃圾回收器决定回收一些软可达对象来释放内存。
    在这里插入图片描述

软引用使用场景-缓存

  • 使用软引用实现学生数据的缓存
    在这里插入图片描述
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.util.HashMap;
import java.util.Map;/*** 软引用案例 - 学生信息的缓存*/
public class StudentCache {private static final StudentCache cache = new StudentCache();private final Map<Integer, StudentRef> StudentRefs;// 用于Cache内容的存储private final ReferenceQueue<Student> q;// 垃圾Reference的队列// key:当前学生id// 构建一个缓存器实例(类的构造方法)private StudentCache() {StudentRefs = new HashMap<>();q = new ReferenceQueue<>();}// 取得缓存器实例public static StudentCache getInstance() {return cache;}public static void main(String[] args) {for (int i = 0; ; i++) {StudentCache.getInstance().cacheStudent(new Student(i, String.valueOf(i)));}}// 以软引用的方式对一个Student对象的实例进行引用并保存该引用private void cacheStudent(Student em) {cleanCache();// 清除垃圾引用StudentRef ref = new StudentRef(em, q);StudentRefs.put(em.getId(), ref);System.out.println(StudentRefs.size());}// 清除那些软引用Student对象已经被回收的StudentRef对象private void cleanCache() {StudentRef ref;// q中存放被回收数据的软引用对象while ((ref = (StudentRef) q.poll()) != null) {StudentRefs.remove(ref._key);}}// 继承SoftReference,使得每一个实例都具有可识别的标识。// 并且该标识与其在HashMap内的key相同private class StudentRef extends SoftReference<Student> {private Integer _key;public StudentRef(Student em, ReferenceQueue<Student> q) {super(em, q);_key = em.getId();}}
}class Student {int id;String name;public Student(int id, String name) {this.id = id;this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}

弱引用

  • 弱引用(Weak Reference):弱引用是一种更弱化的引用类型,用于描述非必需的对象。当垃圾回收器运行时,不管内存是否充足,都会回收被弱引用指向的对象。可以使用WeakReference类来创建弱引用
  • 在JDK 1.2版之后提供WeakReference类来实现弱引用,弱引用主要在ThreadLocal中使用。
  • 弱引用对象本身也可以使用引用队列进行回收
    在这里插入图片描述
WeakReference<Object> weakRef = new WeakReference<>(new Object()); // 弱引用
  • 案例
public static void main(String[] args) throws IOException {byte[] bytes = new byte[1024 * 1024 * 100];WeakReference<byte[]> weakReference = new WeakReference<byte[]>(bytes);bytes = null;System.out.println(weakReference.get()); //[B@58ceff1System.gc(); //通知Gc回收对象System.out.println(weakReference.get()); //null
}

虚引用(不常用)

  • 虚引用(Phantom Reference):虚引用是最弱化的引用类型,主要用于监控对象被垃圾回收的状态。
  • 虚引用无法通过引用直接获取对象,需要通过引用队列(ReferenceQueue)来监听对象的回收状态。可以使用PhantomReference类来创建虚引用。例如:
    ReferenceQueue<Object> queue = new ReferenceQueue<>();
    PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), queue); // 虚引用
    

终结器引用(不常用)

  • 终结器引用是一种特殊的引用类型,用于与对象的终结器(Finalizer)相关联。在Java中,每个对象都可以有一个终结器方法,该方法在对象被垃圾回收之前被调用。终结器引用可以用来保留具有终结器的对象,以便在垃圾回收器运行时调用其终结器方法。

  • 终结器引用指的是在对象需要被回收时,终结器引用会关联对象并放置在Finalizer类中的引用队列中,在稍后由一条由FinalizerThread线程从队列中获取对象,然后执行对象的finalize方法,在对象第二次被回收时,该对象才真正的被回收。在这个过程中可以在finalize方法中再将自身对象使用强引用关联上,但是不建议这样做

    /*** 终结器引用案例 仅用于学习*/
    public class FinalizeReferenceDemo {public static FinalizeReferenceDemo reference = null;public void alive() {System.out.println("当前对象还存活");}@Overrideprotected void finalize() throws Throwable {try{System.out.println("finalize()执行了...");//设置强引用自救reference = this;}finally {super.finalize();}}public static void main(String[] args) throws Throwable {reference = new FinalizeReferenceDemo();test();//Java虚拟机对任何给定对象调用finalize方法的次数永远不会超过一次test();}private static void test() throws InterruptedException {reference = null;//回收对象System.gc();//执行finalize方法的优先级比较低,休眠500ms等待一下Thread.sleep(500);if (reference != null) {reference.alive();} else {System.out.println("对象已被回收");}}
    }
    
  • 运行结果:只能救活一次,原因:Java虚拟机对任何给定对象调用finalize方法的次数永远不会超过一次

    当前对象还存活
    对象已被回收
    
  • 注意:终结器引用已被认为是不安全和不推荐使用的,因为终结器的执行是不可预测的,并且可能导致性能问题和资源泄漏。

  • 在Java 9之后,终结器引用已被废弃,推荐使用其他方式来进行资源释放和清理操作,如使用try-with-resources语句块或实现AutoCloseable接口。

  • 在实际开发中,建议尽量避免使用终结器引用,并采用更可靠和可控的方式来处理对象的资源释放和清理操作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/660208.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

函数式接口当参数使用

如果函数式接口作为一个方法的参数&#xff0c;就以为着要方法调用方自己实现业务逻辑&#xff0c;常见的使用场景是一个业务整体逻辑是不相上下的&#xff0c;但是在某一个步骤有不同的逻辑&#xff0c;例如数据处理有不同的策略&#xff0c;如果有大量的if-els&#xff0c;或…

机器学习4-多元线性回归

多元线性回归(Multiple Linear Regression)是线性回归的一种扩展形式&#xff0c;用于建立因变量与多个自变量之间的关系。在简单线性回归中&#xff0c;我们考虑一个因变量和一个自变量之间的线性关系&#xff0c;而多元线性回归允许我们考虑多个自变量对因变量的影响。 一般…

轻松录制视频,WPS录屏功能全攻略

“有人知道wps怎么录屏吗&#xff1f;老师要求我们录制一段视频&#xff0c;是关于课堂教学的&#xff0c;可是我不会录制文档&#xff0c;眼看就快到提交的时间了&#xff0c;现在真的很着急&#xff0c;希望大家帮帮我&#xff01;” 随着信息技术的发展&#xff0c;录制屏幕…

数字图像处理(实践篇)三十二 OpenCV-Python比较两张图片的差异

目录 一 方案 二 实践 ​通过计算两张图像像素值的均方误差(MSE)来比较两张图像。差异大的两张图片具有较大的均方差值,相反,相似的图片间则具有较小的均方差值。需要注意的是。待比较的两张图像要具有相同的高度、宽度和通道数。 一 方案 ①导入依赖库 import cv2 import…

ROR之.nil? .empty? .blank?的用法

1、出处 Ruby的方法&#xff1a;.nil?、.empty? Rails的方法&#xff1a;.blank? 2、意义&#xff1a; .nil? 判断对象是否存在&#xff1b; .empty? 对象已经存在&#xff0c;判断是否为空字段 .blank? 相当于同时满足.nil?和.empty? 注&#xff1a;Rails API中…

幻兽帕鲁专用服务器延迟高怎么解决?

幻兽帕鲁专用服务器延迟高的问题&#xff0c;可能是由于网络环境、服务器负载、数据传输等原因导致的。下面将针对这些问题&#xff0c;提供一些解决方案和建议&#xff0c;帮助您解决延迟高的问题&#xff0c;提升游戏体验。 1.检查网络环境。网络环境不稳定或者带宽较低&…

mysql navicat 定时执行sql脚本

1、查看数据库是否开启定时任务。 show variables like event_scheduler;2、没有开起的话&#xff0c;执行以下开启命令。 set global event_scheduler on;3、选择数据库&#xff0c;创建事件。 4、创建定义的执行函数或者存储过程。我自己写的是存储过程&#xff0c;如下。…

windows下postgresql的安装使用

一、安装 1、安装包安装 1.1 下载exe安装包 选择安装包&#xff1a;官网 或者点击下载&#xff1a;postgresql-12.12-1-windows-x64.exe Tip&#xff1a;此时若报错&#xff1a;There has been an error.An error occured executing the Microsoft VC runtime installer。 参…

阿里云 DMS 执行sql变更

数据库开发-数据变更-无锁变更 选择数据库&#xff1a;比如要更新生产库&#xff0c;搜索生产库名字。 填入变更sql。

V2X,启动高增长引擎

车载通讯的下一个新周期&#xff0c;毋庸置疑是V2X。从4G、5G再到C-V2X&#xff0c;是车载通讯逐步从信息娱乐、行车数据监控到万物互联的关键。 去年5月&#xff0c;全球车载通讯芯片巨头—高通公司宣布&#xff0c;与以色列车联网&#xff08;V2X&#xff09;芯片设计公司Aut…

10个关键字让你的谷歌竞价排名瞬间飙升-华媒舍

在现代社会中&#xff0c;搜索引擎已经成为获取信息的主要途径之一。在这其中&#xff0c;谷歌搜索引擎以其强大的搜索算法和智能化的用户体验而闻名。对于企业主来说&#xff0c;如何提高在谷歌搜索结果中的排名&#xff0c;对于他们的品牌推广和获取潜在客户非常重要。 1. 关…

pointer-events: none;解决页面水印导致子节点鼠标事件失效的问题

背景&#xff1a;实现水印功能之后&#xff0c;由于水印是一个遮罩层&#xff0c;导致z-index元素比较低的子元素&#xff0c;鼠标移入事件、点击事件都失效了。 解决方案&#xff1a;给添加水印样式的元素&#xff0c;添加css样式&#xff1a;pointer-events: none;。子元素添…

如何在树莓派安装运行Nginx实现无公网ip环境访问内网静态网站

文章目录 1. Nginx安装2. 安装cpolar3.配置域名访问Nginx4. 固定域名访问5. 配置静态站点 安装 Nginx&#xff08;发音为“engine-x”&#xff09;可以将您的树莓派变成一个强大的 Web 服务器&#xff0c;可以用于托管网站或 Web 应用程序。相比其他 Web 服务器&#xff0c;Ngi…

linux☞ Centos 基础篇

切换用户 重启系统、退出 su 用户 ### su switch user 重启系统 reboot 退出当前账户 logout 或者 exit 或者 CtrlD 修改网卡配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33 TYPEEthernet&#xff1a;指明网卡类型为以太网 DEVICEens33&#xff1a;指定当前配置的…

LeetCode 每日一题Day 54 - 61

2859. 计算 K 置位下标对应元素的和 给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。 请你用整数形式返回 nums 中的特定元素之 和 &#xff0c;这些特定元素满足&#xff1a;其对应下标的二进制表示中恰存在 k 个置位。 整数的二进制表示中的 1 就是这个整数的 置位…

Find My点读笔|苹果Find My技术与点读笔结合,智能防丢,全球定位

点读笔是采用国际最新光学图像识别技术和先进的数码语音技术开发而成的新一代智能阅读和学习工具。它体现了电子产品与教育行业的完美融合&#xff0c;实现了科技以人为本的理念。点读笔能同时实现点读、复读、跟读、录音、娱乐等诸多功能。由于小孩贪玩很容易造成点读笔的丢失…

负载均衡下的webshell连接

一、环境配置 1.在Ubuntu上配置docker环境 我们选择用Xshell来将环境资源上传到Ubuntu虚拟机上&#xff08;比较简单&#xff09; 我们选择在root模式下进行环境配置&#xff0c;先将资源文件复制到root下&#xff08;如果你一开始就传输到root下就不用理会这个&#xff09; …

华为配置接口二三层切换示例

配置接口二三层切换示例 组网图形 图1 配置非自协商模式下速率和双工模式组网图 二三层切换简介配置注意事项组网需求配置思路操作步骤配置文件 二三层切换简介 基于接口板的硬件构造&#xff0c;某些形态设备上接口只能作为二层以太网接口&#xff0c;某些形态设备上接口…

百无聊赖之JavaEE从入门到放弃(十四)异常

目录 一.异常机制 二.异常分类 三.异常的处理方式 1.捕获异常(try-catch-finally) 2.声明异常&#xff08;throws 子句&#xff09; 四.try-with-resource 五.自定义异常 六.IDEA 调试 debug 一.异常机制 工作中&#xff0c;程序遇到的情况不可能完美。比如&#xff1a…