本文转载自不使用的大对象为什么要手动设置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. MySQL 数据库性能调优

    MySQL 数据库性能调优 MySQL性能 最大数据量 最大并发数 优化的范围有哪些 存储.主机和操作系统方面: 应用程序方面: 数据库优化方面: 优化维度 数据库优化维度有四个: 优化选择: 数据库 ...

  2. Dos命令思维导图

    通过思维导图的方式,总结常用Dos命令. 各种思维导图下载地址

  3. multiselect多选下拉框

    具体实现 <input type="hidden" id="q_dueDay" name="q_dueDay" value=" ...

  4. sqoop使用以及常见问题

    1.hdfs文件的权限问题 问题分析与解决: 根据报错信息是hdfs文件的权限问题,命令进入集群执行的用户为null,而hdfs文件所有者为hdfs. 要么以用户hdfs执行命令,要么调整hdfs文件 ...

  5. Codeforces Round #626 (Div. 2) E. Instant Noodles(二分图,最大公因数)

    题意: 给你一个二分图,求左侧端点的所有可能子集中的点相连的右侧端点的权值的和的最大公因数. 题解: 若所有右侧端点均不在同一左侧子集中,则求所有权值的最大公因数即可 . 否则,将在相同左侧子集中的右 ...

  6. Equal Numbers Gym - 101612E 思维

    题意: 给你n个数vi,你有k次操作.每一次操作你可以从n个数里面挑一个数,然后使得这个数乘于一个正整数.操作完之后,这n个数里面不同数的数量就是权值.你要使得这个值尽可能小. 题解: 如果a%b== ...

  7. KMP(算法描述)

    #include<iostream> using namespace std; const int N=10010,M=100010; int n,m; char p[N],s[M]; i ...

  8. 【uva 12219】Common Subexpression Elimination(图论--树+自定义比较器+映射+递归)

    题意:如题,用表达式树来表示一个表达式,且消除公共的部分,即用编号表示.编号 K 定义为表达式第 K 个出现的字符串. 解法:先构造表达式树,给每棵子树用(string,left_son,right_ ...

  9. tju3243 Blocked Road

    There are N seaside villages on X island, numbered from 1 to N. N roads are built to connect all of ...

  10. poj3260 The Fewest Coins

    Description Farmer John has gone to town to buy some farm supplies. Being a very efficient man, he a ...