zoukankan      html  css  js  c++  java
  • 直接使用Unsafe类操作堆外内存

    在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 GC
    private byte[] bytes = null;

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

    https://www.cnblogs.com/duanxz/p/6090442.html

  • 相关阅读:
    Oracle中有大量的sniped会话
    Error 1130: Host '127.0.0.1' is not allowed to connect to this MySQL server
    汉字转换为拼音以及缩写(javascript)
    高效率随机删除数据(不重复)
    vs2010 舒服背景 优雅字体 配置
    mvc中的ViewData用到webfrom中去
    jquery ajax return值 没有返回 的解决方法
    zShowBox (图片放大展示jquery版 兼容性好)
    动感效果的TAB选项卡 jquery 插件
    loading 加载提示······
  • 原文地址:https://www.cnblogs.com/xd502djj/p/13096272.html
Copyright © 2011-2022 走看看