当前位置: 首页 > news >正文

Java知识点小结3:内存回收

文章目录

  • 对象引用
    • 强引用
    • 软引用(SoftReference)
    • 弱引用(WeakReference)
      • 考一考
    • 虚引用(PhantomReference)
    • 总结
  • 垃圾回收
    • 新生代
    • 老年代
    • 永生代
  • 内存管理小技巧
    • 尽量使用直接量
    • 使用StringBuilder和StringBuffer进行字符串拼接
    • 尽早释放无用对象的引用
    • 尽量少用静态变量
    • 避免在循环中创建对象
    • 缓存经常使用的对象
    • 避免使用finalize()方法
    • 使用SoftReference

注:本文是对《疯狂Java面试讲义》的小结。

在这里插入图片描述

对象引用

Java通过 new 关键字来创建对象实例,JVM会在堆内存中为对象分配空间。当对象失去引用时,JVM的垃圾回收机制会自动清理对象,回收内存空间。

可以把对象的引用关系理解为有向图。如果某个对象在图中处于不可达状态,则认为该对象不再被引用。

Java的对象引用方式有:

  • 强引用
  • 软引用
  • 弱引用
  • 虚引用

下面举例说明各种引用方式。

准备:已知类 Person 定义如下:

public class Person {private String name;private int age;......
}

强引用

强引用是最普通、最常见的引用方式。

        Person person = new Person("Tom", 20);

强引用的对象,一定不会被JVM回收。

对于强引用,当内存占用过多时,就会出现 OutOfMemoryError

        Person[] arr1 = new Person[80000];for (int i = 0; i < arr1.length; i++) {arr1[i] = new Person("Tom" + i, i % 20);System.out.println(arr1[i]);}

正常情况下运行结果OK。为了模拟内存被占满的情况,我们把JVM的内存设置为较低值:

-Xmx8m -Xms8m

如果是在命令行下运行:

java -Xmx8m -Xms8m xxxxxx

如果是IntelliJ IDEA,右键,More Run/Debug -> Modify Run Configuration…:

在这里插入图片描述

在弹出的对话框里,点击“Modify options”,在子菜单中确保勾选了“Add VM options”,然后填入 -Xmx8m -Xms8m

在这里插入图片描述

运行结果如下:

......
Person{name='Tom61694', age=14}
Person{name='Tom61695', age=15}
Exception in thread "main" java.lang.OutOfMemoryError: Java heap spaceat java.base/jdk.internal.misc.Unsafe.allocateUninitializedArray(Unsafe.java:1375)at java.base/java.lang.StringConcatHelper.newArray(StringConcatHelper.java:494)
......

可见,在创建到大约60000个对象的时候,内存就会溢出。

软引用(SoftReference)

软引用的作用是,当内存空间充足时,它不会被系统回收,但是当内存空间不足时,它就会被系统回收。

软引用的用法如下:

        SoftReference<Person> person = new SoftReference<>(new Person("Tom", 20));......person.get();

把上面强引用的代码稍作修改,如下:

        SoftReference<Person>[] arr1 = new SoftReference[80000];for (int i = 0; i < arr1.length; i++) {arr1[i] = new SoftReference<>(new Person("Tom" + i, i % 20));System.out.println(arr1[i].get());}System.out.println(arr1[1].get());System.out.println(arr1[3].get());System.out.println(arr1[79999].get());

运行结果如下(别忘了设置JVM内存):

......
Person{name='Tom79998', age=18}
Person{name='Tom79999', age=19}
null
null
Person{name='Tom79999', age=19}

可见,80000个对象依次创建成功,内存没有溢出,但是创建完毕后,再访问这些对象时,发现前面一部分对象已经变成null了。这是由于在创建后面的对象时,内存不够,触发了垃圾回收,前面的软引用对象被清除了。

多次运行代码,最后一行,有时也会打印出 null ,这说明每次垃圾回收的时机和回收的对象数量是不一定的。

弱引用(WeakReference)

弱引用和软引用类似,区别在于,不管内存是否充足,只要有垃圾回收,弱引用对象就会被回收。

弱引用的用法如下:

        WeakReference<Person> person = new WeakReference<>(new Person("Tom", 20));System.out.println(person.get());System.gc();System.runFinalization();System.out.println(person.get());

运行结果如下(无需设置JVM内存):

Person{name='Tom', age=20}
null

可见,垃圾回收时,弱引用对象被回收了。

WeakReference 功能类似的还有 WeakHashMap

对于map里的key值,如果其对象是弱引用,则在垃圾回收时,会把该对象回收,并把该key值从map里移除。

        WeakHashMap<Person, String> map = new WeakHashMap<>();map.put(new Person("Tom", 20), "aaa");map.put(new Person("Jerry", 30), "bbb");System.out.println(map);System.gc();System.runFinalization();System.out.println(map);

运行结果如下:

{Person{name='Jerry', age=30}=bbb, Person{name='Tom', age=20}=aaa}
{}

考一考

下面的代码,运行结果是什么?

        WeakHashMap<Person, String> map = new WeakHashMap<>();Person person1 = new Person("Tom", 20);map.put(person1, "aaa");// person1 = null;Person person2 = new Person("Jerry", 30);map.put(person2, "bbb");// person2 = null;System.out.println(map);System.gc();System.runFinalization();System.out.println(map);

乍一看,似乎跟前一个例子没什么太大区别,但运行结果是不同的:

{Person{name='Jerry', age=30}=bbb, Person{name='Tom', age=20}=aaa}
{Person{name='Jerry', age=30}=bbb, Person{name='Tom', age=20}=aaa}

这两个key对象为什么没有被回收掉呢?其实原因很简单,因为person1和person2还在引用它们。这是强引用,所以不会被垃圾回收。要想被垃圾回收,只需解除强引用(参见被注释掉的那两行代码)。

虚引用(PhantomReference)

虚引用的主要作用是跟踪对象被垃圾回收的状态。

虚引用不能单独使用,必须和引用队列(ReferenceQueue)一起使用。引用队列保存了被回收后对象的引用。它和软引用、弱引用等联合使用,这些对象被回收时,会把引用添加到相关联的引用队列中。

        ReferenceQueue<Person> queue = new ReferenceQueue<>();PhantomReference<Person> phantomReference = new PhantomReference<>(new Person("Tom", 20), queue);// 虚引用的 get() 方法总是返回nullSystem.out.println(phantomReference.get());// 还没做垃圾回收,所以队列为空System.out.println(queue.poll());System.gc();System.runFinalization();// 垃圾回收后,虚引用对象在队列中System.out.println(queue.poll());

运行结果如下:

null
null
java.lang.ref.PhantomReference@36baf30c

既然虚引用都get不到实际对象,那它到底有什么用呢?

看起来,虚引用的用处,就是会触发一个事件。我们可以另起一个线程,对队列进行监听,比如:

  • remove() :阻塞方法
  • poll() :非阻塞方法

这样,当对象被回收时,我们就会得到通知,做相应的处理(比如清理资源)。

总结

强引用软引用弱引用
垃圾回收时,不会被回收YNN
垃圾回收时,若内存不足,就会被回收NYY
只要有垃圾回收,就会被回收NNY

垃圾回收

垃圾回收机制主要做两件事:

  • 跟踪每个Java对象的可达状态,回收不可达对象的内存
  • 清理分配和回收过程中产生的内存碎片

垃圾回收的设计思想:

  • 串行和并行:使用单CPU还是多CPU来做回收
  • 并发和停止(stop-the-world):回收时,应用是否暂停
  • 紧凑和不紧凑:如果只是回收对象,则内存可能会产生很多碎片。把所有活着的对象复制到一起,可以避免内存碎片

回收方式:

  • 复制:把内存分成两个相同的空间A和B,以A空间为例,从根开始,把每个可达对象复制到B空间,最后把整个A空间重置
  • 标记清除(mark-sweep):从根开始,标记每个可达对象,最后回收所有没有标记可达的对象
  • 标记清除紧凑(mark-sweep-compact):从根开始,标记每个可达对象,最后把所有活着的对象搬迁在一起,并回收其它内存空间

分代内存:

  • 新生代(Young):大部分对象存活时间不会很长,处于新生代
  • 老年代(Old):少量对象存活时间很长,处于老年代
  • 永生代(Permanent):主要用于存放Class对象,方法等信息

新生代

新生代又分为Eden(伊甸园)区和Survivor区。

绝大部分对象先分配到Eden区,而Survivor区的对象至少熬过一次垃圾回收。

Survivor区又分为From区和To区,参见上面提到的“复制”回收方式。

垃圾回收时,将Eden区和From区的可达对象复制到To区,然后清空Eden区和From区,最后把From和To互换一下。

老年代

如果一个对象熬过了数次垃圾回收,就可能会被转入老年代。

老年代的垃圾回收频率无需太高,因为老年代的对象都很能熬。

  • 次要回收:新生代的垃圾回收,频率较高
  • 主要回收:新生代和老年代的垃圾回收,频率较低

老年代的垃圾回收通常采用标记清除紧凑算法。

永生代

主要用于存放Class对象,方法等信息,默认为64MB。

内存管理小技巧

尽量使用直接量

        String str1 = "hello"; // 在字符串缓存池里缓存了hello字符串String str2 = new String("hello"); //同上,此外还多创建了一个char[]数组

使用StringBuilder和StringBuffer进行字符串拼接

String字符串是不可变的,如果直接对字符串拼接,将产生大量的临时字符串。

        String str1 = "hello";String str2 = "world";String str3 = "!!!";String result1 = str1 + str2 + str3; // 产生大量的临时字符串System.out.println(result1);StringBuilder result2 = new StringBuilder();result2.append(str1);result2.append(str2);result2.append(str3);System.out.println(result2.toString());StringBuffer result3 = new StringBuffer();result3.append(str1);result3.append(str2);result3.append(str3);System.out.println(result3.toString());

尽早释放无用对象的引用

        Person person = new Person("Tom", 20);person.doSomething();// person = null;......

本例中,创建并使用完person对象后,最好将其释放(参见注释处代码),否则,person变量在其作用域范围内,会一直持有对象引用,导致对象无法被系统回收。

尽量少用静态变量

class A {static B b = new B();
}

本例中,b是A类的静态变量,其生命周期与A类一致(存入永生代)。

避免在循环中创建对象

        for (int i = 0; i < 100; i++) {Person person = new Person("Tom", 20);......}

创建了100个Person对象,它们的生存时间都很短。系统需要不断的分配和回收内存。

缓存经常使用的对象

典型的例子是数据库连接池。

避免使用finalize()方法

垃圾回收的工作量已经很大了,尤其是新生代,对象很多,回收频繁,若再使用finalize()方法清理资源,更加重了垃圾回收的负担。

使用SoftReference

参见前面的介绍。

注意SoftReference和WeakReference的不确定性,在使用对象时,应检查其是否为空。

相关文章:

Java知识点小结3:内存回收

文章目录 对象引用强引用软引用&#xff08;SoftReference&#xff09;弱引用&#xff08;WeakReference&#xff09;考一考 虚引用&#xff08;PhantomReference&#xff09;总结 垃圾回收新生代老年代永生代 内存管理小技巧尽量使用直接量使用StringBuilder和StringBuffer进行…...

LeetCode746:使用花费最小爬楼梯

题目链接&#xff1a;746. 使用最小花费爬楼梯 - 力扣&#xff08;LeetCode&#xff09; 代码如下 class Solution { public:int minCostClimbingStairs(vector<int>& cost) {int m cost.size();if(m 1) return min(cost[1], cost[0]);if(m 0) return cost[0]…...

列表、数组排序总结:Collections.sort()、list.sort()、list.stream().sorted()、Arrays.sort()

列表类型 一.Collections.sort() Collections.sort()用于List类型的排序&#xff0c;其提供了两个重载方法&#xff1a; 1.sort(List<T> list) &#xff08;1&#xff09;List指定泛型时只能指定引用数据类型&#xff0c;也就是说无法用于基本数据类型的排序。 &am…...

【资料分析】刷题日记3

第一套 √ 考点&#xff1a;基期比重差很温柔的题 普通专科女生 占比 52.5% - 1.7% 50.8% 成人本专科女生 占比 57.8% - 4.6% 53.2% 相比降低了2.4% 知比重和部分量&#xff0c;求整体在花生老师的解法中体会啥叫适当约分 0.1899 / 47.8% / 87.5% 》0.19 / &#xff08;4…...

基于SpringBoot+Vue的商场停车场管理系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、SSM项目源码 系统展示 【2025最新】基于JavaSpringBootVueMySQL的…...

4. 密码协议

4. 密码协议 (1) 协议的基本概念 协议是一种在两个或多个参与者之间进行通信的规范,它定义了参与者之间的交互方式、消息格式和通信过程。协议的目的是确保通信的可靠性和安全性,防止信息被篡改、伪造或泄露。 (2) 密码协议分类及基本密码协议 密码协议是用于加密和解密数…...

基于嵌入式的智能物流柜( 触摸屏/0.96寸oled屏)

演示 智能物流柜&#xff08;基础版&#xff09; 智能物流柜&#xff08;升级版&#xff09; 前言 这是本人在大二在学校接的一个简单的实验室项目&#xff0c;之前发布了一个&#xff0c;由于那是在暑假&#xff0c;家里器材有限&#xff0c;代码敲完之后&#xff0c;用面包板…...

VSCode创建C++项目和编译多文件

前言 在刚安装好VSCode后&#xff0c;我简单尝试了仅main.cpp单文件编译代码&#xff0c;没有问题&#xff0c;但是当我尝试多文件编译时&#xff0c;就出现了无法识别cpp文件。 内容 创建项目 首先点击左上角“文件”&#xff1b;在菜单中选择“打开文件夹”&#xff1b;在…...

7个提升网站分页体验的 CSS 和 JavaScript 代码片段

文章目录 前言正文1.简洁直观的悬停分页效果2.实时显示页码的分页3.适合响应式设计的多功能分页4.专为移动设备优化的分页5.无数字的极简分页设计6.触屏友好的分页7.结合无限滚动与分页的设计 总结 前言 分页是内容丰富的网站中不可缺少的导航工具&#xff0c;能帮助用户更轻松…...

C++——用带有默认参数的函数实现,求两个整数或三个整数中的最大数。

没注释的源代码 #include <iostream> using namespace std; int max(int a,int b,int c0); int main() { int a,b,c; cout<<"请输入三个整数&#xff1a;"; cin>>a>>b>>c; cout<<"三个整数的最大值是&am…...

对商品分类系统的若干问题的思考

科学研究的目的就是研究事物的特征&#xff0c;并根据共同的特征加以分类 商品分类是商业&#xff0c;制造业中最普遍的活动&#xff0c;几乎所有的企业&#xff0c;电商平台都要对销售的商品&#xff0c;使用的原材料&#xff08;BOM&#xff09;进行分类和编号。 商品分类貌似…...

javascript中Number 类型 在实际开发中常用的一些操作方法

在 JavaScript 中&#xff0c;Number 类型是非常基础的数据类型之一&#xff0c;用于表示整数和浮点数。除了基本的算术运算外&#xff0c;还有许多内置的方法可以帮助你处理数字。下面列举了一些在实际开发中常用的 Number 类型的操作方法&#xff1a; 1. 转换方法 Number()…...

部分解决FDTD安装后,matlab指令fopen报错

今天在新的win11电脑上安装FDTD时&#xff0c;发现在C:\Program Files目录中并没有Lumerical文件夹&#xff0c;把激活文件粘贴过去后虽然能正常启动&#xff0c;但对于matlab link FDTD过程中无法响应以下代码&#xff1a; setenv(PATH, [getenv(PATH) ;C:\Program Files\Lum…...

[python3] 处理函数的重试

tenacity是一个 Python 库&#xff0c;用于简化重试逻辑的实现。它提供了装饰器和工具函数&#xff0c;使得在函数执行失败时可以自动重试。以下是对tenacity库的详细介绍&#xff1a; 一、安装 可以使用pip安装tenacity&#xff1a; pip install tenacity二、主要概念和功能…...

鸿蒙开发之ArkTS 界面篇 一

建好一个工程后&#xff0c;右侧可以预览&#xff0c;看到效果&#xff0c;效率十分可以&#xff0c;如图: State message: string 鸿蒙开发入门篇; 这个字符串改成什么&#xff0c;右侧就显示什么 Entry是类装饰器&#xff0c;可以简单的理解为程序入口的必须的装饰器&…...

嵌入式Linux学习笔记(6)-线程处理、线程同步、线程池(c语言实现)

一、概述 线程是一种轻量级的并发执行的机制。线程是进程中的一个实体&#xff0c;它执行在同一进程的上下文中&#xff0c;共享同一内存空间&#xff0c;但拥有独立的栈空间。 C语言的线程使用pthread库实现&#xff0c;通过包含头文件 pthread.h 来使用相关的函数和数据类型 …...

【HTTP】请求“报头”(Host、Content-Length/Content-Type、User-Agent(简称 UA))

Host 表示服务器主机的地址和端口号 URL 里面不是已经有 Host 了吗&#xff0c;为什么还要写一次&#xff1f; 这里的 Host 和 URL 中的 IP 地址、端口什么的&#xff0c;绝大部分情况下是一样的&#xff0c;少数情况下可能不同当前我们经过某个代理进行转发。过程中&#xf…...

【刷题日记】43. 字符串相乘

43. 字符串相乘 其实就是大数乘法题&#xff0c;这道题用草稿纸演练一下&#xff0c;其实很好找到方法&#xff0c;模拟大数乘法即可。需要注意的是进位和迭代值&#xff0c;还有注意向下取整和去除前导0&#xff08;容易遗漏&#xff09;。去除前导0的时候还要注意如果全是0&…...

Verilog学习之旅~

记录Verilog的学习日常~ 第一阶段&#xff1a;牛客网刷题 1.Verilog快速入门 基础语法 VL1:四选一多路器&#xff1a;case语句、条件表达符&#xff1b; VL2:异步复位的串联T触发器:T触发器的基本功能及代码实现、异步复位的概念&#xff1b; VL3:奇偶校验&#xff1a;缩…...

linux之mysql安装

1:mysql安装包下载 下载地址 可私信我直接获取安装包 2:linux下wget命令下载 下载地址 wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.24-linux-glibc2.12-x86_64.tar.gz3:手动安装 将自己的安装包上传到对应的位置 解压 压缩包 使用命令 tar -zxvf mysql-5.7…...

贵阳婚礼西服定制攻略:面料、工艺、版型避坑指南

婚礼西装是男士婚礼造型的核心&#xff0c;区别于日常商务正装&#xff0c;婚礼西服更看重版型精致度、面料质感、上身挺拔感以及镜头适配度。在贵阳备婚的新人&#xff0c;大多会放弃成品西装&#xff0c;选择专属定制服务。但本地婚礼西服定制市场参差不齐&#xff0c;很多新…...

2026年LLM推理加速全景:量化、投机解码与KV Cache工程实战

大语言模型推理速度慢、成本高&#xff0c;是阻碍AI大规模落地的核心障碍之一。一个7B参数的模型&#xff0c;在标准配置下每秒只能生成约30个token&#xff0c;对于需要实时响应的应用来说几乎无法接受。但2026年&#xff0c;一系列推理加速技术的成熟&#xff0c;让这一局面发…...

别再手动编译了!Matlab一键调用CEC2017测试函数的完整配置指南(附30个函数调用示例)

别再手动编译了&#xff01;Matlab一键调用CEC2017测试函数的完整配置指南&#xff08;附30个函数调用示例&#xff09; 算法研究者们常常需要借助标准测试函数来验证优化算法的性能&#xff0c;而CEC2017测试函数集因其复杂性和多维度的挑战性&#xff0c;成为评估算法鲁棒性的…...

在线文档协作工具选型必看:14款产品对比(2026版)

一、在线文档协作工具的概念解析及其核心功能 在线文档协作工具是基于云端的文档创建、编辑、共享与协同沟通平台&#xff0c;核心目标是让团队在同一份资料上“实时共同工作”&#xff0c;减少反复传文件、版本混乱与沟通成本。 企业常见的核心能力包括&#xff1a; 多人实…...

如何快速上手Redux Dynamic Modules:5分钟完成Redux模块化改造

如何快速上手Redux Dynamic Modules&#xff1a;5分钟完成Redux模块化改造 【免费下载链接】redux-dynamic-modules Modularize Redux by dynamically loading reducers and middlewares. 项目地址: https://gitcode.com/gh_mirrors/re/redux-dynamic-modules Redux Dyn…...

避坑指南:Unity动态加载模型时,TriLib插件材质丢失、缩放异常的5个常见问题解决

Unity动态加载模型避坑指南&#xff1a;TriLib插件材质丢失与缩放异常的深度解决方案当你在Unity项目中尝试使用TriLib插件动态加载外部模型时&#xff0c;是否遇到过这些令人抓狂的情况&#xff1a;模型加载后材质全部变成刺眼的粉红色&#xff0c;贴图神秘消失&#xff0c;或…...

【国家级攻防演练级建议】:DeepSeek私有化部署中4类隐蔽后门植入路径与实时检测方案

更多请点击&#xff1a; https://kaifayun.com 第一章&#xff1a;DeepSeek私有化部署中隐蔽后门植入的攻防对抗本质 在私有化场景下&#xff0c;DeepSeek模型的部署链路常跨越镜像构建、权重加载、推理服务启动及API网关接入等多个环节。攻击者可利用构建上下文污染、依赖包劫…...

PrediPrune:机器学习驱动的编译器超级优化候选剪枝策略

1. 项目概述与核心挑战在编译器优化的世界里&#xff0c;我们总在追求极致的性能。传统的编译器优化器&#xff0c;比如LLVM的Pass&#xff0c;依赖于一系列预定义的、经过验证的转换规则。它们很高效&#xff0c;但想象力也受限于这些规则。超级优化器&#xff08;Superoptimi…...

Safe Exam Browser虚拟机绕过实战:深度解析与安全研究指南

Safe Exam Browser虚拟机绕过实战&#xff1a;深度解析与安全研究指南 【免费下载链接】safe-exam-browser-bypass A VM and display detection bypass for SEB. 项目地址: https://gitcode.com/gh_mirrors/sa/safe-exam-browser-bypass 在数字化教育快速发展的今天&…...

LeetCode 80 · 删除有序数组中的重复项 II:通用模板的威力

LeetCode 26 要求每个元素最多出现一次&#xff0c;这道题放宽到最多出现两次。看起来只是把 1 改成了 2&#xff0c;但这个"小改动"背后藏着一个通用的快慢指针模板——把 2 换成任意整数 m&#xff0c;代码几乎不用动。这就是模板的威力&#xff1a;改一个数字&…...