Java方法中不使用的对象应该手动赋值为NULL吗?
在java方法中,不使用的对象是否应该手动赋值为null?我们先来通过一个示例看一下。
垃圾回收示例一
public class GuoGuoTest {public static void main(String[] args) {byte[] placeholder = new byte[64 * 1024 * 1024];System.gc();}
}
上面代码向内存填充了64MB的数据,然后通知虚拟机进行垃圾回收。我们在运行代码启动的时候,加上参数 “-verbose:gc” ,观察一下虚拟机垃圾回收的情况。

运行完代码之后,发现64MB内存并没有被回收。这个结果很正常,因为System.gc()执行的时候placeholder还处于作用域范围以内,虚拟机自然不会回收它。
![]()
垃圾回收示例二
现在我们将示例一的代码稍作修改,给placeholder用花括号加了一个作用域。在代码执行之前我们可以猜测一下,现在placeholder和System.gc()不处于一个作用域范围,placeholder不会再被访问,所以当执行System.gc()时,placeholder应当被虚拟机认作可以回收的变量。
public class GuoGuoTest {public static void main(String[] args) {{byte[] placeholder = new byte[64 * 1024 * 1024];}System.gc();}
}
执行结果如下图,可以看到结果出乎我们的预料,placeholder并没有被回收,这是什么原因呢?
![]()
垃圾回收示例三
在解释原因之前,我们可以将上面代码再次修改,加入一行代码 int guoguo = 0。这次运行代码之后,placeholder会被垃圾回收吗?
public class GuoGuoTest {public static void main(String[] args) {{byte[] placeholder = new byte[64 * 1024 * 1024];}int guoguo = 0;System.gc();}
}
增加的这一行代码看起来很无厘头,但程序运行的结果居然是内存这次被回收了!
![]()
栈帧
想要知道上述现象的原因,就要从栈帧结构说起。栈帧(Stack Frame)是虚拟机用于方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈的栈元素。栈帧的结构包括:
- 方法的局部变量表
- 操作数栈
- 动态连接
- 方法返回地址
在编译程序代码时,需要多大的局部变量表,多深的操作数栈,是由Class文件结构中方法表的Code属性决定的,也就是在程序运行之前就已经约定好了。程序运行期间变量数据的大小并不会影响栈帧的内存分配,而取决于虚拟机的具体实现。

从逻辑概念上看,栈帧结构如图所示。在活动线程中,只有顶端的栈帧才是有效的,叫做当前栈帧(Current Stack Frame),与当前栈帧相关联的方法叫做当前方法(Current Method),每一个方法在从调用开始到结束的过程,就对应着栈帧在虚拟机栈里从入栈到出栈的过程。虚拟机的执行引擎执行的所有字节码指令都只针对当前栈帧进行操作。
局部变量表
局部变量表(Local Variable Table)是一组变量值存储空间,用于存放方法参数和方法内部定义的局部变量。局部变量表容量的最小单元是变量槽(Variable Slot,简称Slot)。虚拟机规范并没有明确指明Slot占用的内存空间大小,只是向导性的表示应能存放一个boolean、byte、char、short、int、float、reference或returnAddress类型的数据,即32位及其以下的数据都可以存放。returnAddress类型现在已经很少见了。reference类型表示对一个对象实例的引用,虚拟机规范没有说明它的长度和它的结构,但虚拟机的实现至少应满足两点:
- 从此引用中直接或间接地查找到对象在Java堆中的数据存放的起始地址索引
- 从此引用中直接或间接地查找到对象所属数据类型在方法区中的存储的类型信息
对于64位的数据类型,虚拟机将用高位对齐的方式为其分配两个连续的Slot空间。64位的数据类型只有long和double两种,reference可能是32位也可能是64位。虽然long和double数据类型的一次读写会被分割称两次32位的读写,这样就有可能造成非原子性的数据安全问题。但是由于局部变量表是线程的堆栈元素,是线程私有的数据,所以读写两个连续的Slot无论是否原子操作,都不会造成数据安全问题。
虚拟机使用局部变量表通过索引定位,索引范围从0开始到Slot最大数量结束。对于32位数据类型的变量,索引n代表使用第n个Slot;对于64位数据类型的变量,则会使用第n和n+1个Slot。虚拟机规范不允许单独访问其中某一个。
如果虚拟机执行的是实例方法,而非static方法,局部变量表中索引为0的Slot默认用来传递方法所属实例对象的引用,在方法中用关键字“this”来访问这个隐含参数。其余参数按照参数表的顺序,从1开始占用其他Slot。
原理
以上对于虚拟机运行时数据区的栈帧和局部变量表做了简单介绍之后,我们回头再来看看本文一开始讲到的垃圾回收问题。示例三种加了一行 int guoguo = 0 的代码之后,就能正确回收placeholder变量了,这是什么原因呢?
在公布真相之前,我们首先要了解到一个事实,那就是局部变量表里的Slot是可以重用的。这么做的目的是为了节省更多的栈帧空间。在同一个方法体中,某个变量不可能覆盖整个方法。例如示例三种GuoGuoTest的main方法,placeholder变量被花括号包裹之后的作用域只限于花括号里面。此时,当字节码PC计数器的值已经超出placeholder的作用域时,那么placeholder对应的Slot就应该释放出来交由其他变量使用。
public class GuoGuoTest {public static void main(String[] args) {{byte[] placeholder = new byte[64 * 1024 * 1024];}int guoguo = 0;System.gc();}
}
placeholder能否被回收的根本原因是:局部变量表中的Slot是否还存有关于placeholder数组对象的引用。示例二中,当还没有 int guoguo = 0 这行代码的时候,代码虽然已经离开了placeholder变量的作用域,但之后没有对局部变量表的任何其他读写操作,placeholder占用的Slot也就不会被其他变量所复用,所以作为GC Roots一部分的局部变量表仍然保持着对它的关联。在绝大多数时候,这种情况造成的影响非常小。但是如果后面的代码非常耗时,而前面又定义了大量占用内存又实际不再使用的变量,那么手动将其设为null就变得非常具有意义。当然,这种情况非常罕见,一般我们也没有必要所有的变量都手动设为null,并且代码在经过JIT编译之后会将赋null值的操作给消除掉,所以从编码的角度来说,最优雅的解决方式还是通过变量的作用域来控制变量回收的时间。
局部变量初始化
文章最后,再写一点关于局部变量的小知识。类变量有两次赋初值的过程,准备阶段赋予系统初始值;初始化阶段赋予程序员定义的初始值。例如,int类型的类变量会首先被赋予系统初始值0,如果程序员的代码没有显式给其赋值,那么也没有关系,类变量仍然有一个确定的系统初始值。但是局部变量则不同,如果一个局部变量只声明没有初始化,编译器是会报错的,即使编译器不提示错误直接手动生成字节码,字节码校验的时候也会被虚拟机发现而类加载失败。

相关文章:
Java方法中不使用的对象应该手动赋值为NULL吗?
在java方法中,不使用的对象是否应该手动赋值为null?我们先来通过一个示例看一下。 垃圾回收示例一 public class GuoGuoTest {public static void main(String[] args) {byte[] placeholder new byte[64 * 1024 * 1024];System.gc();} } 上面代码向内…...
Mysql主从搭建
Mysql主从搭建 1.Mysql下载1.1 查看操作系统2.2 下载mysql安装包 2.Mysql安装2.1 解压2.2 目录重命名2.3 创建data,存储文件2.4 创建用户组2.5 授权用户2.6 配置环境变量2.7 编辑my.cnf2.8 创建相关目录和文件2.9 初始化数据库2.10 复制mysql.server到/etc/init.d/下…...
WPF程序给按钮增加不同状态的图片
首先我们在资源里添加几个图片,Up,Over和Down状态。 然后我们创建一个Style。默认我们的背景设置成Up 然后在Triggers里添加代码,当Property:IsMouseOver为True的时候更换成Over;当Property:IsPressed为Tr…...
Java编程陷阱(三)
陷阱11:不要使用StringBuffer类来拼接字符串 StringBuffer是Java中的一个类,它可以表示一个可变的字符串,也就是可以对字符串进行修改和追加的操作,比如使用append或insert方法来拼接字符串。有时候,我们需要使用StringBuffer类来拼接字符串,比如在循环中动态地构建一个字…...
数据仓库相关
在阿里巴巴的数据体系中,我们建议将数据仓库分为三层,自下而上为:数据引入层(ODS,Operation Data Store)、数据公共层(CDM,Common Data Model)和数据应用层(…...
SpringBoot学习笔记-创建个人中心页面(下)
笔记内容转载自 AcWing 的 SpringBoot 框架课讲义,课程链接:AcWing SpringBoot 框架课。 CONTENTS 1. 实现个人中心页面2. POJO时区修改3. 集成代码编辑器 本节实现个人中心的前端页面,用户能够查看自己的 Bot 信息,并能创建、修改…...
电子秤方案:做一个宠物勺方案设计
养宠物是一件费心劳力的事情,但同时也是能够给你带来快乐和幸福感的事情。就是有时候会怕宠物毫无征兆地生病令人措手不及,所以电子秤方案设计鼎盛合科技分享一个小方案,能够及时了解到宠物的身体状况问题。 蓝牙宠物勺是一种具有记录和称重…...
Debezium-Embedded 实时监控MySQL数据变更
1.Debezium-Embedded 简介 Debezium连接器的操作通常是将它们部署到Kafka Connect服务,并配置一个或多个连接器来监控上游数据库,并为它们在上游数据库中看到的所有更改生成数据更改事件。这些数据更改事件被写入Kafka,在那里它们可以被许多不…...
计算机是如何工作的(简单介绍)
目录 一、冯诺依曼体系 二、CPU基本流程工作 逻辑⻔ 电⼦开关——机械继电器(Mechanical Relay) ⻔电路(Gate Circuit) 算术逻辑单元 ALU(Arithmetic & Logic Unit) 算术单元(ArithmeticUnit) 逻辑单元(Logic Unit) ALU 符号 寄存器(Regis…...
JSP基本表单和Request对象使用例子
表单的jsp; <%page contentType"text/html;charsetgbk" pageEncoding"UTF-8"%> <!DOCTYPE html> <html><head><meta http-equiv"Content-Type" content"text/html; charsetUTF-8"><titl…...
【Redux】Redux 基本使用
1. Redux 快速上手 Redux 是 React 最常用的集中状态管理工具,类似于Vue中的Pinia(Vuex),可以独立于框架运行。 <button id"decrement">-</button> <span id"count">0</span> <…...
多线程Thread(初阶一:认识线程)
目录 一、引用线程的原因 二、线程的概念 三、进程和线程的区别 四、多线程编程 一、引用线程的原因 多任务操作系统,希望系统能同时运行多个任务。所以会涉及到进程,需要对进程进行管理、调度等。 而单任务操作系统,就完全不涉及到进程…...
系列五、GC垃圾回收【四大垃圾算法-复制算法】
一、堆的内存组成 二、复制算法 2.1、发生位置 复制算法主要发生在新生代,发生在新生代的垃圾回收也被叫做Minor GC。 2.2、 Minor GC的过程 复制>清空》交换 1、eden、from区中的对象复制到to区,年龄1 首先,当eden区满的时候会触发第一…...
LeetCode(24)文本左右对齐【数组/字符串】【困难】
目录 1.题目2.答案3.提交结果截图 链接: 文本左右对齐 1.题目 给定一个单词数组 words 和一个长度 maxWidth ,重新排版单词,使其成为每行恰好有 maxWidth 个字符,且左右两端对齐的文本。 你应该使用 “贪心算法” 来放置给定的单…...
Spring-Spring之事务底层源码解析
EnableTransactionManagement工作原理 开启Spring事务本质上就是增加了一个Advisor,但我们使用EnableTransactionManagement注解来开启Spring事务是,该注解代理的功能就是向Spring容器中添加了两个Bean: AutoProxyRegistrarProxyTransactio…...
后端面经学习自测(三)
文章目录 1、ArrayList和Linkedlist区别?2、ArrayList扩容机制?3、ArrayList和Linkedlist分别能做什么场景?4、事务特性?MySQL事务Redis事务Spring事务5、在Spring中事务失效的场景?6、Java泛型?7、泛型擦除…...
力扣labuladong——一刷day40
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、力扣341. 扁平化嵌套列表迭代器 前言 N叉树的结构,构造迭代器 一、力扣341. 扁平化嵌套列表迭代器 /*** // This is the interface that allo…...
在VS Code中使用VIM
文章目录 安装和基本使用设置 安装和基本使用 VIM是VS Code的强大对手,其简化版本VI是Linux内置的文本编辑器,堪称VS Code问世之前最流行的编辑器,也是VS Code问世之后,我仍在使用的编辑器。 对VIM无法割舍的原因有二࿰…...
注解【元数据,自定义注解等概念详解】(超简单的好吧)
注解的理解与使用 注解的释义元数据的含义基础阶段常见的注解注解的作用(包括但不限于)教你读懂注解内部代码内容五种元注解尝试解读简单注解我当时的疑惑点 自定义注解自定义注解举例 注解的原理总结 注解的释义 我们都知道注释是拿来给程序员看的&…...
vue-pdf在vue框架中的使用
在components目录下新建PdfViewer/index.vue vue-pdf版本为4.3.0 <template><div :id"containerId" v-if"hasProps" class"container"><div class"right-btn"><div class"pageNum"><input v-m…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
Java如何权衡是使用无序的数组还是有序的数组
在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
(转)什么是DockerCompose?它有什么作用?
一、什么是DockerCompose? DockerCompose可以基于Compose文件帮我们快速的部署分布式应用,而无需手动一个个创建和运行容器。 Compose文件是一个文本文件,通过指令定义集群中的每个容器如何运行。 DockerCompose就是把DockerFile转换成指令去运行。 …...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
C++ Visual Studio 2017厂商给的源码没有.sln文件 易兆微芯片下载工具加开机动画下载。
1.先用Visual Studio 2017打开Yichip YC31xx loader.vcxproj,再用Visual Studio 2022打开。再保侟就有.sln文件了。 易兆微芯片下载工具加开机动画下载 ExtraDownloadFile1Info.\logo.bin|0|0|10D2000|0 MFC应用兼容CMD 在BOOL CYichipYC31xxloaderDlg::OnIni…...
智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制
在数字化浪潮席卷全球的今天,数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具,在大规模数据获取中发挥着关键作用。然而,传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时,常出现数据质…...
企业如何增强终端安全?
在数字化转型加速的今天,企业的业务运行越来越依赖于终端设备。从员工的笔记本电脑、智能手机,到工厂里的物联网设备、智能传感器,这些终端构成了企业与外部世界连接的 “神经末梢”。然而,随着远程办公的常态化和设备接入的爆炸式…...
如何更改默认 Crontab 编辑器 ?
在 Linux 领域中,crontab 是您可能经常遇到的一个术语。这个实用程序在类 unix 操作系统上可用,用于调度在预定义时间和间隔自动执行的任务。这对管理员和高级用户非常有益,允许他们自动执行各种系统任务。 编辑 Crontab 文件通常使用文本编…...
Copilot for Xcode (iOS的 AI辅助编程)
Copilot for Xcode 简介Copilot下载与安装 体验环境要求下载最新的安装包安装登录系统权限设置 AI辅助编程生成注释代码补全简单需求代码生成辅助编程行间代码生成注释联想 代码生成 总结 简介 尝试使用了Copilot,它能根据上下文补全代码,快速生成常用…...
