Java 消除过期的对象引用
内存泄漏的第一个常见来源是存在过期引用。
import java.util.Arrays;
import java.util.EmptyStackException; public class Stack { private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
} private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
} public void push(Object e) {
ensureCapacity();
elements[size++] = e;
} public Object pop() {
if (size == 0) {
throw new EmptyStackException();
} return elements[--size];
} }
如果一个栈先是增长,然后再收缩,从栈中弹出来的对象不会被当作垃圾回收,即使使用栈的程序不再引用这些对象,它们也不会被回收。因为栈内部维护着对这些对象的过期引用(obsolete reference)。过期引用指永远也不会再被解除的引用。在本例中,在elements数组的“活动部分(active portion)”之外的任何引用都是过期的。活动部分指elements中下标小于size的那些元素。
如果一个对象引用被无意识地保留了,垃圾回收机制不仅不会回收这个对象,而且不会回收被这个对象所引用的所有其他对象。解决方法:一旦对象引用已经过期,只需清空这些引用即可。在本例中,只要一个元素被弹出栈,指向它的引用就过期了。修改如下:
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
} Object result = elements[--size];
elements[size] = null; // 清空引用 return result;
}
清空过期引用的另一个好处是,如果它们以后又被错误地解除引用,程序会立即抛出NullPointerException异常,而不是悄悄地错误运行下去。尽快地检测出程序中的错误总是有益的。消除过期引用最好的方法是让包含该引用的变量结束其生命周期。如果在最紧凑的作用域范围内定义每一个变量(见第45条),这种情形会自然而然地发生。
只要类是自己管理内存,程序员就应该警惕内存泄漏问题。一旦元素被释放掉,该元素中包含的任何对象引用都应该被清空。
内存泄漏的第二个常见来源是缓存。
把对象引用放到缓存中,它就很容易被遗忘掉,从而使得它不再有用之后很长一段时间内仍然留在缓存中。用WeakHashMap代表缓存时,当缓存中的项过期之后,它们会被自动地删除。只有当缓存项的生命周期是由该键的外部引用而不是由值决定时,WeakHashMap才起作用。
随着时间的推移,缓存项会变得越来越没有价值,缓存应该时不时地清除掉没用的项。可以由一个后台线程(可能是Timer或者ScheduledThreadPoolExecutor)来完成,或者在给缓存添加新条目时进行清理。LinkedHashMap类可以通过它的removeEldestEntry方法来实现后者。对于更加复杂的缓存,必须直接使用java.lang.ref。
内存泄漏的第三个常见来源是监听器和其他回调。
如果客户端在自己实现的API中注册回调,却没有显式地取消注册,那么除非自己采取某些动作,否则它们就会积聚。确保回调立即被当作垃圾回收的最佳方法是只保存它们的弱引用(weak reference),例如,只将它们保存为WeakHashMap中的键。
往往只有通过仔细检查代码,或者借助于Heap剖析工具(Heap Profiler)才能发现内存泄露问题。
参考资料
《Effective Java 中文版 第2版》 第6条:消除过期的对象引用 P21-23
Java 消除过期的对象引用的更多相关文章
- Effective Java —— 消除过期的对象引用
本文参考 本篇文章参考自<Effective Java>第三版第七条"Eliminate obsolete object references" Memory leak ...
- Effective Java 第三版——7. 消除过期的对象引用
Tips <Effective Java, Third Edition>一书英文版已经出版,这本书的第二版想必很多人都读过,号称Java四大名著之一,不过第二版2009年出版,到现在已经将 ...
- 《Effective Java》 读书笔记(七)消除过期的对象引用
大概看了一遍这个小节,其实这种感觉体验最多的应该是C/C++程序,有多杀少个new就得有多个delete. 一直以为Java就不会存在这个问题,看来是我太年轻. 感觉<Effective Jav ...
- Effective Java (6) - 消除过期的对象引用
一.引言 很多人可能在想这么一个问题:Java有垃圾回收机制,那么还存在内存泄露吗?答案是肯定的,所谓的垃圾回收GC会自动管理内存的回收,而不需要程序员每次都手动释放内存,但是如果存在大量的临时对象在 ...
- Effective Java 之-----消除过期的对象引用
public class Stack { private Object[] elements; private int size = 0; private static final int DEFAU ...
- Item 6 消除过期的对象引用
过期对象引用没有清理掉,会导致内存泄漏.对于没有用到的对象引用,可以置空,这是一种做法.而最好的做法是,把保存对象引用的变量清理掉,多用局部变量. 什么是内存泄漏? 在Java中,对象的内存空间回 ...
- EffectiveJava(6)消除过期对象的引用
消除过期对象的引用 过期引用 – 在操作集合类的时候,未清理过期的对象引用(即:过期引用),常会导致内存泄露.从而报outOfmemory错误. 1.过期对象导致内存泄漏. 2.一旦对象引用过期,清除 ...
- Java中对象、对象引用、堆、栈、值传递以及引用传递的详解
Java中对象.对象引用.堆.栈.值传递以及引用传递的详解 1.对象和对象引用的差别: (1).对象: 万物皆对象.对象是类的实例. 在Java中new是用来在堆上创建对象用的. 一个对象能够被多个引 ...
- java cache过期策略两种实现,一个基于list轮询一个基于timer定时
最近项目要引入缓存机制,但是不想引入分布式的缓存框架,所以自己就写了一个轻量级的缓存实现,有两个版本,一个是通过timer实现其超时过期处理,另外一个是通过list轮询. 首先要了解下ja ...
随机推荐
- Update openssh7.9 on centos6
一.制作RPM安装包1)依赖安装yum install rpm-build gcc make wget openssl-devel krb5-devel pam-devel libX11-devel ...
- asp.net core ioc 依赖注入
1.生命周期 内置的IOC有三种生命周期: Transient: Transient服务在每次被请求时都会被创建.这种生命周期比较适用于轻量级的无状态服务. Scoped: Scoped生命周期的服务 ...
- JCenter下载太慢, jcenter修改 https为http也许能帮助你
今天导入一个工程到studio,一直卡在下载那块. 看到下载地址是:https://jcenter.bintray.com/........https!!!! 到浏览器下载,果然也下载不下来.. 于是 ...
- 反射PropertyInfo的简单使用
namespace EF6._0Test { class Program { /// <summary> /// PropertyInfo的简单使用 /// </summary> ...
- ThreadPool线程池
1.GetMaxThreads,GetMinThreads class Program { static void Main(string[] args) { int workerThreads; i ...
- echarts 模拟迁徙
echarts 3 的功能貌似加了不少额 官方demo:http://echarts.baidu.com/demo.html#geo-lines 地图是基于地理地图的 百度提供了一些地图:http:/ ...
- NEST - 编写查询
Writing queries Version:5.x 英文原文地址:Writing queries 将数据索引到了 Elasticsearch 之后,就可以准备搜索它们了.Elasticsearch ...
- MUI组件四:选择器、滚动条、单选框、区域滚动和轮播组件
目录(?)[+] 1.picker(选择器) mui框架扩展了pipcker组件,可用于弹出选择器,在各平台上都有统一表现.poppicker和dtpicker是对picker的具体实现.*pop ...
- 从源码开始运行Bitcoin Core
安装Ubuntu 环境:虚拟机 网络连接:桥接 系统版本:16.04 源:ali 安装编译环境(依赖库) sudo apt-get update sudo apt-get install build- ...
- 再理解tcp backlog
在Linux 2.2以前,backlog大小包括了半连接状态和全连接状态两种队列大小.linux 2.2以后,分离为两个backlog来分别限制半连接SYN_RCVD状态的未完成连接队列大小跟全连接E ...