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,如果程序员的代码没有显式给其赋值,那么也没有关系,类变量仍然有一个确定的系统初始值。但是局部变量则不同,如果一个局部变量只声明没有初始化,编译器是会报错的,即使编译器不提示错误直接手动生成字节码,字节码校验的时候也会被虚拟机发现而类加载失败。

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

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

相关文章

vue3 tsx 项目中使用 Antv/G2 实现多线折线图

Antv/G2 文档 Antv/G2 双折线图 安装 antV-G2 通过 npm 安装 项目中安装 antv/g2 依赖库&#xff1a; npm install antv/g2 --save安装成功&#xff1a; 浏览器引入 可以将脚本下载到本地&#xff0c;也可以直接引入在线资源。 引入在线资源 <!-- 引入在线资源&…

java springboot在测试类中启动一个web环境

我们在开发过程中 可以对数据层 业务层做测试 那我们的表现层能做测试吗&#xff1f; 答案自然是可以的 但是 前提 我们要有一个web环境 我们现在 测试类运行 明显是个很普通的java程序 还是这个 SpringBootTest 它有一个 webEnvironment 我们可以先这样写 package com.examp…

城市生命线丨市政综合管廊监测系统的效果

市政综合管廊&#xff0c;又被称为城市生命线&#xff0c;是我们在地下建造的一个智慧而高效的空间。它把市政、电力、通讯、燃气、给排水等各种管线集于一体&#xff0c;解决了城市中反复开挖路面、架空线网密集、管线事故频发等问题&#xff0c;为城市运行提供了重要的基础设…

Windows安装Vmware 虚拟机

目录 一、Vmware 虚拟机介绍 二、Vmware 虚拟机的三种网络模式 2.1桥接模式 2.2仅主机模式 2.3NAT 网络地址转换模式 三、Vmware 虚拟机的安装 一、Vmware 虚拟机介绍 VMware Workstation Pro 是一款可以在个人电脑的操作系统上创建一个完全与主机操作系统隔离的 虚拟机&…

【技巧】Word和Excel如何互相转换?

Word文档里有数据表格&#xff0c;如果编辑修改起来感觉没那么方便或容易出错&#xff0c;不妨将文档转换成Excel表格再来处理。 将Word文档转换成Excel&#xff0c;比较常用的是复制粘贴方法&#xff0c;也就是将Word文档的表格复制后&#xff0c;再粘贴到Excel表格里&#x…

FPC焊点剥离失效分析

一、案例背景 FPC在后续组装过程中&#xff0c;连接器发生脱落。在对同批次的样品进行推力测试后&#xff0c;发现连接器推力有偏小的现象。据此进行失效分析&#xff0c;明确FPC连接器脱落原因。 #1、#2样品连接器脱落连接器脱落&#xff1b;#3样品连接器未脱落&#xff1b;…

HTTP HTTPS 独特的魅力

目录 HTTP协议 HTTP协议的工作过程 首行 请求头&#xff08;header&#xff09; HOST Content-Length​编辑 User-Agent&#xff08;简称UA&#xff09; Referer Cookie 空行 正文&#xff08;body&#xff09; HTTP响应详解 状态码 报文格式 HTTP响应格式 如何…

鸿蒙:Harmony开发基础知识详解

一.概述 工欲善其事&#xff0c;必先利其器。 上一篇博文实现了一个"Hello Harmony"的Demo&#xff0c;今天这篇博文就以"Hello Harmony" 为例&#xff0c;以官网开发文档为依据&#xff0c;从鸿蒙开发主要的几个方面入手&#xff0c;详细了解一下鸿蒙开…

RAID技术复习笔记

Raid&#xff08;Redundant Array of independent Disks&#xff09;独立磁盘冗余阵列&#xff1a;磁盘阵列 Raid 分为:软raid、硬raid、软硬混合三种。 软Raid&#xff1a;所有的功能均有操作系统和CPU来完成&#xff0c;没有独立的raid控制、处理芯片和IO处理处理芯片。 硬R…

车载通信架构 —— 传统车内通信网络发展回顾

车载通信架构 —— 传统车内通信网络发展回顾 我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 屏蔽力是信息过载时代一个人的特殊竞争力&#xff0c;任何…

c语言-数据结构-堆

目录 一、二叉树 1、二叉树的概念 2、完全二叉树和满二叉树 3、完全二叉树的顺序存储 二、堆 2、堆的概念与结构 3、堆的创建及初始化 4、堆的插入&#xff08;小堆&#xff09; 5、堆的删除 6、显示堆顶元素 7、显示堆里的元素个数 8、测试堆的各个功能 9、 实现堆…

学生邮箱白嫖/免费安装JetBrains全家桶(IDEA/pycharm等) —— 保姆级教程

&#x1f9f8;欢迎来到dream_ready的博客&#xff0c;&#x1f4dc;相信您对博主首页也很感兴趣o (ˉ▽ˉ&#xff1b;) 博主首页&#xff0c;更多redis、java等优质好文以及各种保姆级教程等您挖掘&#xff01; 目录 前言 JetBrains全家桶介绍 申请过程&#xff1a; 获取学…

这个技术也能管理教学质量,厉害了!

在当今数字化时代&#xff0c;教育领域也在积极应用先进的技术来提升教学质量和管理效率。在线巡课系统作为其中的一种创新工具&#xff0c;为学校管理者和教育工作者提供了便捷而高效的管理手段。 在线巡课系统通过数字化、自动化的方式&#xff0c;实现了对课堂教学的全面监控…

Python基础:正则表达式(regular expression)详解

在Python中&#xff0c;正则表达式是一种强大的工具&#xff0c;可用于匹配和操作字符串。什么是正则表达式&#xff1f; 正则表达式是一种模式匹配语言&#xff0c;用于匹配字符串中的特定模式。这些模式可以是字母、数字、字符组合或其他符号。正则表达式通常用于文本处理、网…

excel怎么能锁住行 和/或 列的自增长,保证粘贴公式的时候不自增长或者只有部分自增长

例如在C4单元格中输入了公式&#xff1a; 现在如果把C4拷贝到C5&#xff0c;D3会自增长为D4&#xff1a; 现在如果想拷贝的时候不自增长&#xff0c;可以先把光标放到C4单元格&#xff0c;然后按F4键&#xff0c;行和列的前面加上了$符号&#xff0c;锁定了&#xff1a; …

Linux中的进程终止(详解)

Linux中的进程终止 1. 进程退出场景2. 进程常见退出方法2.1 _exit函数2.2 exit函数2.3 return退出 1. 进程退出场景 代码运行完毕&#xff0c;结果正确代码运行完毕&#xff0c;结果不正确代码异常终止 2. 进程常见退出方法 正常终止&#xff08;可以通过 echo $? 查看进程…

【小黑嵌入式系统第九课】PSoC 5LP第一个实验——LED、字符型LCD显示实验

上一课&#xff1a; 【小黑嵌入式系统第八课】初识PSoC Creator™开发——关于PSoC Creator&下载、创建项目、单片机中的hello world&#xff08;点亮一个led) 文章目录 1 实验目的2 实验要求3 实验设备4 实验原理1. 基于 PWM 原理的 LED 亮度控制2. 时间的计量3. 按键抖动…

超全整理,Pytest自动化测试框架-多进程(pytest-xdist)运行总结...

目录&#xff1a;导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结&#xff08;尾部小惊喜&#xff09; 前言 平常我们功能测试…

SpringBoot-AOP学习案例

4. AOP案例 SpringAOP的相关知识我们就已经全部学习完毕了。最后我们要通过一个案例来对AOP进行一个综合的应用。 4.1 需求 需求&#xff1a;将案例中增、删、改相关接口的操作日志记录到数据库表中 就是当访问部门管理和员工管理当中的增、删、改相关功能接口时&#xff0…

整形数据和浮点型数据在内存中的存储差别

愿所有美好如期而遇 我们先来看代码&#xff0c;猜猜结果是什么呢&#xff1f; int main() {//以整型数据的方式存储int n 10;float* m (float*)&n;//以整型数据的方式读取printf("%d\n", n);//以浮点型数据的方式2读取printf("%f\n", *m);printf(&…