本文转载自不使用的大对象为什么要手动设置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构建 RESTful 风格应用

    Spring Boot构建 RESTful 风格应用 1.Spring Boot构建 RESTful 风格应用 1.1 实战 1.1.1 创建工程 1.1.2 构建实体类 1.1.4 查询定制 1.1 ...

  2. 关于Spring Boot的博客集合

    掘金: 关于Spring Boot的博客集合 CSDN: Spring Boot教程 掘金: SpringBoot2 简书: Spring Boot 核心技术 天码营 Spring Data JPA: ...

  3. linux驴杂记

    1. 命令行 vim下 的Ctrl + S ,ctrl +Q: 这个功能可以用于长时间编译的时候随时把屏幕输出停住,但是编译仍然会继续,这样就不会影响到编译时间了. S:是锁屏  q:是开屏 2. S ...

  4. DEDECMS:安装百度UEDITOR编辑器

    第一步:下载相对应编辑器的版本 首先,去百度搜索"百度ueditor编辑器",然后点击进入官网,找到下载页面.找到我们想要的编辑器的版本,看自己网站的编码是UTF-8还是GBK,下 ...

  5. Codeforces Round #673 (Div. 2) A. Copy-paste(贪心)

    题目链接:https://codeforces.com/contest/1417/problem/A 题意 给出一个大小为 $n$ 的数组 $a$,每次操作可以选择两个数,然后将一个数加到另一个数上, ...

  6. 使用eclipse写第一个Java_web的hello_world项目

    1.先创建一个Java_web项目 如果你没有下载过Tomcat服务器,不会配置,建议看一下我得这一篇博客:https://www.cnblogs.com/kongbursi-2292702937/p ...

  7. pbds初探

    今年暑假外校集训的时候一道题标算是最短路扩展,然而std用的是pbds,于是就产生了研究的兴趣.结果那个标程我现在死都找不到了233 定义: 在知乎上看到有oier去年向CCF发了邮件,得到的回复是p ...

  8. Mysql主从架构

    Mysql主从架构 1. 克隆虚拟机 克隆的虚拟机的网络适配,使得虚拟机可以进入局域网 vi /etc/sysconfig/network-scripts/ifcfg-eth0 删除 HWADDR所在 ...

  9. LINUX - openssl学习

    参考链接: https://www.cnblogs.com/AloneSword/p/4656492.html

  10. 二进制安装kubernetes(一) 环境准备及etcd组件安装及etcd管理软件etcdkeeper安装

    实验环境: 架构图: 主机环境: 操作系统:因docker对内核需要,本次部署操作系统全部采用centos7.6(需要内核3.8以上) VM :2C 2G 50G * 5  PS:因后面实验需要向k8 ...