博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java堆外内存之四:直接使用Unsafe类操作堆外内存
阅读量:6413 次
发布时间:2019-06-23

本文共 2567 字,大约阅读时间需要 8 分钟。

在nio以前,是没有光明正大的做法的,有一个work around的办法是直接访问Unsafe类。如果你使用Eclipse,默认是不允许访问sun.misc下面的类的,你需要稍微修改一下,给Type Access Rules里面添加一条所有类都可以访问的规则:

在使用Unsafe类的时候:

Unsafe f = Unsafe.getUnsafe();

发现还是被拒绝了,抛出异常:

java.lang.SecurityException: Unsafe

正如Unsafe的类注释中写道:

Although the class and all methods are public, use of this class is limited because only trusted code can obtain instances of it.

于是,只能使用反射来做这件事; 

Field f = Unsafe.class.getDeclaredField("theUnsafe");        f.setAccessible(true);        Unsafe us = (Unsafe) f.get(null);        long id = us.allocateMemory(1024 * 1024 * 1024);

其中,allocateMemory返回一个指针,并且其中的数据是未初始化的。如果要释放这部分内存的话,需要调用freeMemory或者reallocateMemory方法。Unsafe对象提供了一系列put/get方法,例如putByte,但是只能一个一个byte地put,我不知道这样会不会影响效率,为什么不提供一个putByteArray的方法呢?

示例:

import sun.misc.Unsafe;public class ObjectInHeap{	private long address = 0;	private Unsafe unsafe = GetUsafeInstance.getUnsafeInstance();	public ObjectInHeap()	{		address = unsafe.allocateMemory(2 * 1024 * 1024);	}	// Exception in thread "main" java.lang.OutOfMemoryError	public static void main(String[] args)	{		while (true)		{			ObjectInHeap heap = new ObjectInHeap();			System.out.println("memory address=" + heap.address);		}	}}

这段代码会抛出OutOfMemoryError。这是因为ObjectInHeap对象是在堆内存中分配的,当该对象被垃圾回收的时候,并不会释放堆外内存,因为使用Unsafe获取的堆外内存,必须由程序显示的释放,JVM不会帮助我们做这件事情。由此可见,使用Unsafe是有风险的,很容易导致内存泄露。

 

4、正确释放Unsafe分配的堆外内存

        虽然第3种情况的ObjectInHeap存在内存泄露,但是这个类的设计是合理的,它很好的封装了直接内存,这个类的调用者感受不到直接内存的存在。那怎么解决ObjectInHeap中的内存泄露问题呢?可以覆写Object.finalize(),当堆中的对象即将被垃圾回收器释放的时候,会调用该对象的finalize。由于JVM只会帮助我们管理内存资源,不会帮助我们管理数据库连接,文件句柄等资源,所以我们需要在finalize自己释放资源。

import sun.misc.Unsafe;public class RevisedObjectInHeap{	private long address = 0;	private Unsafe unsafe = GetUsafeInstance.getUnsafeInstance();	// 让对象占用堆内存,触发[Full GC	private byte[] bytes = null;	public RevisedObjectInHeap()	{		address = unsafe.allocateMemory(2 * 1024 * 1024);		bytes = new byte[1024 * 1024];	}	@Override	protected void finalize() throws Throwable	{		super.finalize();		System.out.println("finalize." + bytes.length);		unsafe.freeMemory(address);	}	public static void main(String[] args)	{		while (true)		{			RevisedObjectInHeap heap = new RevisedObjectInHeap();			System.out.println("memory address=" + heap.address);		}	}}

我们覆盖了finalize方法,手动释放分配的堆外内存。如果堆中的对象被回收,那么相应的也会释放占用的堆外内存。这里有一点需要注意下

// 让对象占用堆内存,触发[Full GCprivate byte[] bytes = null;

这行代码主要目的是为了触发堆内存的垃圾回收行为,顺带执行对象的finalize释放堆外内存。如果没有这行代码或者是分配的字节数组比较小,程序运行一段时间后还是会报OutOfMemoryError。这是因为每当创建1个RevisedObjectInHeap对象的时候,占用的堆内存很小(就几十个字节左右),但是却需要占用2M的堆外内存。这样堆内存还很充足(这种情况下不会执行堆内存的垃圾回收),但是堆外内存已经不足,所以就不会报OutOfMemoryError。

转载地址:http://zbdra.baihongyu.com/

你可能感兴趣的文章
zabbix监控memcached模板
查看>>
JavaScript中的对象
查看>>
ELK中的redis的问题记录
查看>>
zabbix low level discovery example use python(web_site_url)
查看>>
分布式服务Dubbo+Zookeeper安全认证
查看>>
我的友情链接
查看>>
request.getRequestURI() 、request.getRequestURL()
查看>>
二叉查找树--查找、删除、插入(Java实现)
查看>>
简单的UDP多线程模型
查看>>
Unity曲线编辑器和bezier曲线插值
查看>>
android中数据存储的ContentProvider的使用方法
查看>>
网络系统管理与维护2488
查看>>
裸机telnet处理过程记录
查看>>
Nginx源码安装及调优
查看>>
EasyUi subgrid 三级列表实现
查看>>
python 使用curve25519
查看>>
我的友情链接
查看>>
我的友情链接
查看>>
一个简单的AJAX示例(转)
查看>>
VMware esxi 5.1详细安装及设置
查看>>