基于Java软引用机制最大使用JVM堆内存并杜绝OutOfMemory
题记:说好的坚持一周两篇文章在无数琐事和自己的懒惰下没有做好,在此表达一下对自己的不满并对有严格执行力的人深表敬意!!!!
---------------------------------------------------------------------------------------------------------------------------------
引文:Java程序员对OutOfMemory并不陌生,一般来说,出现此异常主要是由于应用里缓存了大量的数据没有被GC掉导致堆内存溢出,可是很多时候,为了减少重复计算或提升运行速度,必需要将一些数据缓存起来,比如启动的时候加载配置文件信息、从数据库里初始化进来的信息、运行过程中得到的一些中间结果等。程序员往JVM里加载这些数据的时候往往会很纠结,一方面想缓存的越多越好,尽量减少查库和重复计算,但另一方面过多的缓存对GC造成压力,甚至要提心吊胆的考虑溢出的问题。
需求:如果能有一种方法可以尽可能的缓存数据提高运行效率,又可以在GC前主动清空一部分过期数据从而防止内存溢出,该有多好。下面,我要讲的基于Java软引用实现堆内存监控,是笔者亲身在生产系统的实践,或许可以帮助程序员在这方面做一些尝试。
导读:文章会先解释什么是软引用,接着会说明GC对软引用的处理特点,围绕其特点利用JDK自带的相关类阐述代码实现细节。
正文:
1. 什么是软引用:
我们知道,Java中有四种引用关系,分别是强引用、软引用、弱引用、虚引用,如下图:

强引用:指JVM内存管理器从根引用集合(ROOt Set)出发遍寻堆中所有可到达的对象引用关系,也是常用的引用类型,如Object obj = new Object();只要强引用存在则GC时则必定不被回收。
软引用:用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。
弱引用:用来描述非必需对象的,在java中,用java.lang.ref.WeakReference类来表示。当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象。
虚引用:在任何时候都可能被垃圾回收器回收的对象应用,用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,此引用关系更多的是与虚引用队列相关联以方便做一些GC监控。
2. 软引用特点
根据java帮助文档:"软引用对象在响应内存需要时,由垃圾回收器决定是否清除此对象。软引用对象最常用于实现内存敏感的缓存。假定垃圾回收器确定在某一时间点某个对象是软可到达对象。这时,它可以选择自动清除针对该对象的所有软引用,以及通过强引用链,从其可以到达该对象的针对任何其他软可到达对象的所有软引用。在同一时间或晚些时候,它会将那些已经向引用队列注册的新清除的软引用加入队列。软可到达对象的所有软引用都要保证在虚拟机抛出 OutOfMemoryError 之前已经被清除。否则,清除软引用的时间或者清除不同对象的一组此类引用的顺序将不受任何约束。然而,虚拟机实现不鼓励清除最近访问或使用过的软引用。此类的直接实例可用于实现简单缓存;该类或其派生的子类还可用于更大型的数据结构,以实现更复杂的缓存。只要软引用的指示对象是强可到达对象,即正在实际使用的对象,就不会清除软引用。例如,通过保持最近使用的项的强指示对象,并由垃圾回收器决定是否放弃剩余的项,复杂的缓存可以防止放弃最近使用的项。"
根据上述内容我们知道,软引用对象会在OutOfMemoryError 之前由JVM保证将其回收,并把它加入到其注册的清除队列中。因此,通过监控该队列是否有即将被清除的软引用对象,我们就可以间接得知java应用是否已经到溢出崩溃边缘了,并在其溢出前迅速执行部分缓存数据的清空工作从而让虚拟机可以清理出一些内存出来避免堆内存的溢出,更进一步想,我们可以将该软引用对象设置成占一定内存大小的对象,如10M,这样当虚拟机内存不足时会第一时间将此对象回收进而腾出10M空余内存,进而缓解内存不足,同时为应用争取了宝贵清空部分缓存数据的时间,有效避免直接抛出内存溢出的异常。
3. 实现细节
根据上面的分析和实际的开发实践,利用软引用对象监控虚拟机内存使用情况的代码实现如下:
- 初始化软引用对象与引用队列,并设置软引用对象占用10M的内存
//内存监控
public static ReferenceQueue<byte[]> memoryDetectorQueue ;
public static SoftReference<byte[]> memoryDetector; // initial
public static void initial(){
memoryDetectorQueue = new ReferenceQueue<byte[]>();
memoryDetector = new SoftReference<byte[]>(new byte[(int)(10*1024*1024)],memoryDetectorQueue);
} - 设置一个单独的线程,并在软引用对象初始化后启动该线程,开始监视memoryDetectorQueue是否非空,非空则说明软引用对象由于内存空间不够被清理,内存告急:
public class MemoryMonitorService implements Runnable { public void run() {
while (true) {
try {
if (memoryDetectorQueue.remove() != null) {
doPartClean(); //执行部分缓存的清空以释放内存,可以根据一些LRU算法或按比例来执行清理
}
} catch (Exception e) {
logger.error("", e);
}finally{
memoryDetector = new SoftReference<byte[]>(new byte[(int) (10 * 1024 * 1024)],
memoryDetectorQueue); // 执行完部分缓存清理后重新创建软引用对象
}
}
}
}说明:memoryDetectorQueue.remove()方法会一直等待,阻塞到某个对象变得可用为止,它返回的值不为空时说明memoryDetector 软引用对象被GC掉了。
- 调用new MemoryMonitorService().start()启动监控线程。一般来说,上面代码里的doPartClean()工作是由专门的清理类来辅助的。
基于Java软引用机制最大使用JVM堆内存并杜绝OutOfMemory的更多相关文章
- 巩固java(二)----JVM堆内存结构及垃圾回收机制
前言: 我们在运行程序时,有时会碰到内存溢出(OutOfMemoryError)的问题,为了解决这种问题,我们有必要了解JVM的内存结构和垃圾回收机制. 正文: 1.JVM堆内存结构 ...
- 【高频Java面试题】简单说说JVM堆的内存结构和GC回收流程
目录 前言 JVM堆内存结构简述 JVM堆内存结构图 堆初体验 结构详情 新生代 老年代 永久代/元空间 GC回收流程 GC回收流程图 GC回收详细流程 查看JDK自带可视化堆空间图 总结 前言 我们 ...
- JVM堆内存监测的一种方式,性能调优依旧任重道远
上月,由极客邦.InfoQ和听云联合主办2016 APMCon中国应用性能管理大会圆满落下帷幕.会上,Java冠军Martijn Verburg进行了一场Java and the Machine的分享 ...
- 【转】JVM 堆内存设置原理
堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...
- [转]JVM 堆内存设置原理
堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...
- JVM 堆内存设置原理
堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...
- JVM 堆内存设置原理(转)
堆内存设置 原理 JVM堆内存分为2块:Permanent Space 和 Heap Space. Permanent 即 持久代(Permanent Generation),主要存放的是Java类定 ...
- JVM堆内存设置
今天碰到了一个题目,讲的是关于堆内存的问题,题目如下 下面哪种情况会导致持久区jvm堆内存溢出? A.循环上万次的字符串处理 B.在一段代码内申请上百M甚至上G的内存 C.使用CGLib技术直接操 ...
- JDK8中JVM堆内存划分
一:JVM中内存 JVM中内存通常划分为两个部分,分别为堆内存与栈内存,栈内存主要用运行线程方法 存放本地暂时变量与线程中方法运行时候须要的引用对象地址. JVM全部的对象信息都 存放在堆内存中.相比 ...
随机推荐
- 前端 webpack
前端 webpack http://www.cnblogs.com/lvdabao/
- ReentrantLock(重入锁)功能详解和应用演示
目录 1. ReentrantLock简介 2. ReentrantLock和synchronized的相同点 2.1 ReentrantLock是独占锁且可重入的 3. ReentrantLock相 ...
- Python操作SQLServer示例
本文主要是Python操作SQLServer示例,包括执行查询及更新操作(写入中文). 需要注意的是:读取数据的时候需要decode('utf-8'),写数据的时候需要encode('utf-8'), ...
- c++ 中介者模式(mediator)
中介者模式:用一个中介对象来封装一系列的对象交互.中介者使各个对象不需要显示地相互引用,从而使其耦合松散,而且可以独立地改变他们之间的交互.中介者模式的例子很多,大到联合国安理会,小到房屋中介.下面以 ...
- ubuntu server 12.04 jdk,ssh及hadoop配置
我是在32位的系统下配置的,所以在下载安装文件时候要注意. 第一步:下载并配置JDK 1.下载jdk,这里下载的是jdk1.7.0_65版本的,命令如下 $ wget http://download. ...
- 20. Valid Parentheses检验括号字符串的有效性
[抄题]: Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if th ...
- linux系统chkconfig使用方法及服务开机启动
一.基础知识 有关linux系统开机过程.运行等级,执行权限请看另一篇:linux系统启动过程及运行等级详解. 本篇文章实践的系统:centos6.5 二.创建服务 通过之前的说明,我们知道了如果需要 ...
- Oracle和Mysql的区别 转载
一.并发性 并发性是oltp数据库最重要的特性,但并发涉及到资源的获取.共享与锁定. mysql:mysql以表级锁为主,对资源锁定的粒度很大,如果一个session对一个表加锁时间过长,会让其他se ...
- 设计模式--单例模式(Singleton)详解
单例对象(Singleton)是一种常用的设计模式.在Java应用中,单例对象能保证在一个JVM中,该对象只有一个实例存在.这样的模式有几个好处: 1.某些类创建比较频繁,对于一些大型的对象,这是一笔 ...
- Python的内建比较函数cmp比较原理剖析-乾颐堂
cmp( x, y):比较2个对象,前者小于后者返回-1,相等则返回0,大于后者返回1. Python的cmp比较函数比较原理 Python的cmp函数可以比较同类型之间,或者不同数据类型之间.然后根 ...