本文转载自不使用的大对象为什么要手动设置null,真的有效吗?

导语

在我们开发过程中,对于大的对象使用过后,为了help gc ,我们会手动将大对象置为null,背后的原理是什么,是不是最佳的实践。

案例一

首先我们先看一段代码

package com.bk.exercise.stack;
/**
* @author BK
* @description: 栈变量槽
* 启动参数 : -Xms20M -Xmx20M -verbose:gc
* @date 2019-08-18 23:22
*/
public class StackSlot {
public static void main(String[] args) {
byte[] byteArrays = new byte[5 * 1024 * 1024];
byteArrays = null ;
System.gc();
}
}

对于上面这段代码,我们在使用完byteArrays数组之后,然后将其置为null,通过System.gc()去建议JVM做一次GC,在GC后我们看到GC日志如下,真的做了将byteArrays所占空间回收掉了

[GC (System.gc())  6537K->5656K(19968K), 0.0006770 secs]
[Full GC (System.gc()) 5656K->368K(19968K), 0.0032760 secs]

这种做法是否推荐使用

虽然案例一说明手动将使用过的大对象赋值为null在某些情况下确实是有用的,但是这种做法不是最佳实践,这样的做法在一些极特殊情形下可以使用,比如一些对象占用内存较大,方法的栈帧长时间不能被回收方法调用次数太少达不过JIT的编译条件下可以使用,其它情况下不推荐使用

为什么不推荐

不推荐的原因如下

  • 从编码的角度来讲,合理的使用变量作用作用域来控制变量回收才是最优雅的解决方法,后面原理解析会说原因
  • 从执行角度来讲,使用null值的操作来优化内存回收是建立在对字节码执行引擎概念模型的理解之上的,JVM真正运行的代码是JIT编译后的本地代码,如果上述代码作为热点代码,经过JIT编译优化后做了无用代码消除,这时对变量设置为null的操作是会被消除掉的,这时候将变量设置为null就是没有意义的了

读者可以在这里停下来细想一下

原理解析

在线程运行中,线程栈中存在的基本单位是栈帧(Slot),栈帧中存储了局部变量表操作数栈动态连接方法返回地址附加信息,而且为了节省空间,栈帧是可以被重用

在方法体中定义的变量,如果PC计数器的值已超过变量的作用域,那么这个变量对应的Slot就可以交给其它变量使用

局部变量表中的引和的对象做为GC Roots的一部分,所以被局部变量表引用的数据,是不会被GC回收的,所以只要保证变量的作用域合理,再加上Slot可重用的特性,保证不需要的变量从局部变量表中清除,在GC发生的时候就可以保证对象被回收

案例二

基于上面的结论我们看一下案例二

package com.bk.exercise.stack;
/**
* @author BK
* @description: 栈变量槽
* 启动参数 : -Xms20M -Xmx20M -verbose:gc
* @date 2019-08-18 23:22
*/
public class StackSlot1 {
public static void main(String[] args) {
{
byte[] byteArrays = new byte[5 * 1024 * 1024];
}
System.gc();
}
}

GC日志显示如下

[GC (System.gc())  6537K->5648K(19968K), 0.0009879 secs]
[Full GC (System.gc()) 5648K->5488K(19968K), 0.0033692 secs]

看GC日志我们发现,代码已经离开了byteArrays的作用域了,但是占用的内存却没有释放,主要原因是因为后面没有对局部变量表读写,所以被byteArrays占用的Slot还未被清除,给其它变量复用,所以bytesArrays仍然作为GC Roots的一个成员,无法被回收的。

我们对代码做一个细微的改动,在gc之前增加一个对局部变量表的读/写操作,再看一下效果

package com.bk.exercise.stack;
/**
* @author BK
* @description: 栈变量槽
* 启动参数 : -Xms20M -Xmx20M -verbose:gc
* @date 2019-08-18 23:22
*/
public class StackSlot1 {
public static void main(String[] args) {
{
byte[] byteArrays = new byte[5 * 1024 * 1024];
}
int x = 10 ;
System.gc();
}
}

看GC日志显示如下

[GC (System.gc())  6537K->5668K(19968K), 0.0006792 secs]
[Full GC (System.gc()) 5668K->374K(19968K), 0.0030587 secs]

byteArrays占用的空间已经被回收

啰嗦两句

到这里可能有人会问,如果不加int x = 10 这个操作岂不是就不会回收了,确实是这样的,细想一下如果作用域后面没有其它操作了,那说明方法也该结束了,方法结束后局部变量表中引用的对象,如果不被其它线程引用的话,在GC时直接就被回收了

总结

在日常开发中,将变量分配合理的作用域是一个比较推荐的编码规则。

回到开篇,*不使用的大对象为什么要手动设置null,真的有效吗?*,希望可以从本文中总结出自己的答案。欢迎大家在评论区留言,一起探讨。

不使用的大对象为什么要手动设置null,真的有效吗?的更多相关文章

  1. [翻译] 编写高性能 .NET 代码--第二章 GC -- 将长生命周期对象和大对象池化

    将长生命周期对象和大对象池化 请记住最开始说的原则:对象要么立即回收要么一直存在.它们要么在0代被回收,要么在2代里一直存在.有些对象本质是静态的,生命周期从它们被创建开始,到程序停止才会结束.其它对 ...

  2. oracle对大对象类型操作:blob,clob,nclob

     1.基本介绍 Oracle和plsql都支持lob(large object) 类型,用来存储大数量数据,如图像文件,声音文件等.Oracle 9i realse2支持存储最大为4g的数据,or ...

  3. [转帖]Oracle数据库lob大对象数据类型字段总结,值得收藏

    Oracle数据库lob大对象数据类型字段总结,值得收藏 原创 波波说运维 2019-07-11 00:02:00 https://www.toutiao.com/i67108943269703357 ...

  4. Python 数据库的Connection、Cursor两大对象

    Python 数据库的Connection.Cursor两大对象 pymysql是Python中操作MySQL的模块,其使用方法和py2的MySQLdb几乎相同. Python 数据库图解流程 Con ...

  5. JDBC_part4_大对象_DAO_Bean_DButi

    本文为博主辛苦总结,希望自己以后返回来看的时候理解更深刻,也希望可以起到帮助初学者的作用. 转载请注明 出自 : luogg的博客园 谢谢配合! JDBCday04_大对象_Dao_DBUtil_Ja ...

  6. [SQLServer大对象]——FileTable从文件系统迁移文件

    阅读导航 从文件系统中迁移文件到FileTable 批量加载文件到FileTable 如何批量加载文件到FileTable 通过博文[SQLServer大对象]——FileTable初体验,已经可以将 ...

  7. C#中考虑为大对象使用弱引用

    1.无论怎样尽力,我们总是会使用到某些需要大量内存的数据,而这些内存并不需要经常访问.或许你需要从一个大文件中查找某个特定的值,或者算法需要一个较大的查询表.这时,你也许会采用2中不太好做法:第一种是 ...

  8. [原创]java WEB学习笔记81:Hibernate学习之路--- 对象关系映射文件(.hbm.xml):hibernate-mapping 节点,class节点,id节点(主键生成策略),property节点,在hibernate 中 java类型 与sql类型之间的对应关系,Java 时间和日期类型的映射,Java 大对象类型 的 映射 (了解),映射组成关系

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  9. BLOB:大数据,大对象,在数据库中用来存储超长文本的数据,例如图片等

    将一张图片存储在mysql中,并读取出来(BLOB数据:插入BLOB类型的数据必须使用PreparedStatement,因为插入BLOB类型的数据无法使用字符串拼写): -------------- ...

随机推荐

  1. (一)在Spring Boot应用启动之后立刻执行一段逻辑

    在Spring Boot应用启动之后立刻执行一段逻辑 1.CommandLineRunner 2.ApplicationRunner 3.传递参数 码农小胖哥:如何在Spring Boot应用启动之后 ...

  2. 安装kettle

    保证安装了jdk 在window 下载解压 解压到任意目录下 运行startZK.bat 在Linux安装 1.将kettle上传到Linux服务器的/opt/server目录下(如果该目录不存在则创 ...

  3. jackson学习之八:常用方法注解

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  4. Jenkins(7)参数化构建(构建git仓库分支)

    前言 当我们的自动化项目越来越多的时候,在代码仓库会提交不同的分支来管理,在用jenkins来构建的时候,我们希望能通过参数化构建git仓库的分支. 下载安装Git Parameter插件 系统管理- ...

  5. Eclipse中配置Junit

    在要使用Junit的project名上,点击properties-->Java Build Path-->Libraries,点击Add Library 选择Junit 选择Junit 4 ...

  6. 2020Nowcode多校 Round5 C. Easy

    C. Easy 构造两个序列分别要满足 \(\sum_{i=1}^{k} a_{i} = N\) \(\sum_{i=1}^{k} b_{i} = M\) 一种方案能贡献\(\prod_{i=1}^{ ...

  7. 2014-2015 ACM-ICPC, NEERC, Southern Subregional Contest 题解(PART)(9/13)

    $$2014-2015\ ACM-ICPC,\ NEERC,\ Southern\ Subregional\ Contest$$ A Nasta Rabbara B Colored Blankets ...

  8. Codeforces Global Round 8 B. Codeforces Subsequences(构造)

    题目链接:https://codeforces.com/contest/1368/problem/B 题意 构造最短的至少含有 $k$ 个 $codeforces$ 子序列的字符串. 题解 如下表: ...

  9. dsu on tree ——附带buff的暴力解法

    这篇博客只是简单叙述思想(因为ML太弱了),具体例题请转其他博客. dsu on tree,许多OI将其归于启发式合并,当然如果你能理解更好,这只是一个理解方式罢了. 思想简述 顾名思义,这个算法是处 ...

  10. Slim Span POJ 3522 (最小差值生成树)

    题意: 最小生成树找出来最小的边权值总和使得n个顶点都连在一起.那么这找出来的边权值中的最大权值和最小权值之差就是本题的结果 但是题目要求让这个输出的结果最小,也就是差值最小.那么这就不是最小生成树了 ...